bash 循环文件神秘错误

bash loop over files mysterious bug

谁能告诉我这是怎么回事:

这个:

find . -name "*.mp3" | while read fname ; do 
    echo "$fname"; 
    ls -l "$fname"; 
    echo mplayer "$fname" ; 
    echo "$fname" ;
done

据我所知工作得很好,但如果我真的尝试 运行 mplayer 而不是回显命令:

find . -name "*.mp3" | while read fname ; do 
     echo "$fname"; 
     ls "$fname"; 
     mplayer "$fname" ; 
     echo "$fname" ; 
done

然后它播放了一个文件,然后就乱七八糟了。

我认为 mplayer 必须以某种方式与 read 交互,但我在 bash 的方式上并不明智。

你猜对了。 find 将其文件列表输出到 stdout。您将其通过管道传输到第二个进程:您的 while 循环。在这个循环中,readmplayer 都从 stdin 读取。 read 将读取第一个文件名,mplayer 将读取所有其余输入,就好像您通过键盘控制它一样,而 运行.

最简单的解决办法是在mplayer后面加上</dev/null,但是如果不能从stdin读取它可能会不高兴。其他解决方案是为 find | read 部分使用单独的文件描述符,或者使用 for 循环。

使用 mplayer-noconsolecontrols 选项。它使它忽略标准输入,所以它不会从中窃取字符并且 read 可以处理它们,并且 mplayer 不会表现得很奇怪,将文件名解释为来自键盘的命令。

改为这样做:

#!/bin/env bash
while IFS= read -r -d '' fname <&9; do 
     printf '%s\n' "$fname"
     ls "$fname"
     mplayer "$fname"
     printf '%s\n' "$fname"
done 9< <(find . -name '*.mp3' -print0)

http://mywiki.wooledge.org/BashFAQ/001