我仅在 9P 安装上的 WSL 中观察到脚本中的奇怪行为(Windows 磁盘,如
/mnt/c/...
)。
如果存在写入临时文件的管道,则 *
循环中的星号 (
for
) 的全局扩展会导致下次调用时出现重复。
#!/bin/bash
# save true file number
true_f_num=$(for fn in *; do echo; done | wc -l)
while :; do
# count of iterations to outer loop pipe
echo
# save current file list in file
echo -n '' >.list
for fn in *; do
echo "$fn" >>.list
# line below causes unexpected behaviour
# on 9P mounts (like /mnt/c/...)
# (WSL2, ubuntu 22.04)
done | cat > .tmp; rm .tmp
# check
[ $(cat .list | wc -l ) -ne $true_f_num ] && break
done | wc -l
这里有一个脚本来测试和说明这一点。预计它将永远运行。但在 WSL 的 9P 安装上,它会在随机次数的迭代后停止。我无法理解这个机制。我假设 glob 扩展发生一次,只有稍后它才开始传递到管道(子 shell)并更改目录的内容。这将如何影响下一次迭代?该文件曾经在那里,但现在已经不存在了。目录中是否出现了一些看不见的fifo?
请智者解释一下这里发生了什么。
[示例脚本]预计将永远运行。但在 WSL 的 9P 安装上,它会在随机次数的迭代后停止。大概这是因为
[ $(cat .list | wc -l ) -ne $true_f_num ]
在某些评估中评估为 true,事实上,您在注释中观察到,在脚本终止后,您会在留下的
.list
文件中发现重复的名称。 这样就可以了,除非你还漏掉了一个名字。文件
.list
的创建和填充方式如下:
echo -n '' >.list
for fn in *; do
echo "$fn" >>.list
# [...]
done | cat > .tmp
接下来是
rm .tmp
,但这不是管理
.list
的一部分,并且直到上述管道完成后才会执行。 但是,由于 .tmp
在每次执行管道后都会被删除,因此可以合理地假设它在控制到达管道之前不存在。因为for
命令出现在管道中,所以它是在子shell中执行的。 其中的模式 (
*
) 在子 shell 中展开,在执行该命令之前为每个文件生成一个单词。然后子shell的标准输出被重定向到管道中,最后,循环体对
*
扩展中的每个单词执行一次。
同时,
cat
的标准输入从管道重定向,其标准输出被重定向到.tmp
,如果需要,首先创建该文件,然后启动
cat
。
请注意,然后,相对于
.tmp
命令中 *
模式的扩展,何时创建
for
是未指定的,并且通常不可预测。 可能是之前、期间或之后。
观察:默认情况下,路径名扩展会忽略名称以
.
.
与模式显式匹配。 因此,在上述代码中,无论其创建的相对时间如何,
.tmp
预计不会出现在
*
的扩展中。在示例命令中,预计在执行循环体的任何迭代之前执行一次路径名扩展。
.list
这将是一个错误。
在我看来,这样的错误很可能与在.tmp
for
相关。 我可以想到至少一种可能导致此类问题的特定形式的实施缺陷。这当然是推测,但我认为这是合理的。
我认为外循环的每次迭代都有其自己的独立机会来触发错误。 而且我认为这可能是路径名扩展中的一个错误,因此也不是内部循环的一次迭代影响另一次迭代的问题。
目录中是否出现一些不可见的fifo?
不太可能。