Linux shell bug?管道中的变量赋值不起作用

时间:2009-11-25 13:39:30

标签: linux shell

为什么FILE_FOUND在这个bugger的末尾为0:

FILE_FOUND=0

touch /tmp/$$.txt

ls -1 /tmp/$$.* 2>/dev/null | while read item; do
    FILE_FOUND=1
    echo "FILE_FOUND = $FILE_FOUND"
done

echo "FILE_FOUND = $FILE_FOUND"

rm -f /tmp/$$.txt 2>/dev/null

?? !!

在Unix上,FILE_FOUND保持为1(应该如此),但在Linux(RedHat,Cygwin,..)上它会跳回到0 !!

它是Linux shell功能,而不是bug吗? :)

请帮忙。

6 个答案:

答案 0 :(得分:5)

由于您正在流入while而导致的常见问题,因此在子shell中运行,而子shell无法将环境变量传递回其父级。我猜这个“unix”在这方面有所不同,因为你在那里运行一个不同的shell(ksh?)

可能不需要连接到while循环。你能用这个成语吗?

for item in /tmp/$$.*; do
    ....
done

如果你必须使用子shell,那么你必须做一些外部的事情,比如:

touch /tmp/file_found

答案 1 :(得分:3)

这是“bash”shell的“功能”。 “bash”手册条目清楚地说明了这一点:

  

管道中的每个命令都作为一个单独的进程执行(即在子shell中)。

使用Korn shell(“ksh”)执行的相同构造在同一进程中运行“while”(不在子shell中),因此给出了预期的答案。所以我检查了规格。

POSIX shell specification对此并不十分清楚,但它没有说明改变“shell执行环境”,所以我认为UNIX / Korn shell实现是兼容的并且Bourne Again shell实现是不。但是,“bash”并不声称符合POSIX!

答案 2 :(得分:2)

已经提到过了,但是由于你正在流行,所以整个while循环都在子shell中运行。我不确定你在'Unix'上使用哪个shell,我认为这意味着Solaris,但bash应该表现得一致,无论平台如何。

要解决这个问题,你可以做很多事情,最常见的是以某种方式检查while循环的结果,就像这样

result=`mycommand 2>/dev/null | while read item; do echo "FILE_FOUND"; done`

并在$result中查找数据。另一种常见的方法是让while循环产生有效的变量赋值,并直接评估它。

eval `mycommand | while read item; do echo "FILE_FOUND=1"; done`

将由您的'当前'shell评估以分配给定的变量。

我假设你不想只是迭代文件,在这种情况下你应该做

for item in /tmp/$$.*; do
  # whatever you want to do
done

答案 3 :(得分:2)

正如其他人所提到的,它是你使用管道符号创建的额外shell。试试这个:

while read item; do
    FILE_FOUND=1
    echo "FILE_FOUND = $FILE_FOUND"
done < <(ls -1 /tmp/$$.* 2>/dev/null)

在这个版本中,while循环在你的脚本的shell中,而ls是一个新的shell(与你的脚本正在做的相反)。

答案 4 :(得分:0)

另一种方式,将结果带回来,

FILE_FOUND=0
result=$(echo "something" | while read item; do
    FILE_FOUND=1
    echo "$FILE_FOUND"

done )
echo "FILE_FOUND outside while = $result"

答案 5 :(得分:-2)

嗯... ls -1而不是l在你的剧本上?