变量 IFS 和不同的结果与循环列出文件

duq*_*uqu 5 bash find wildcards

我想获取当前目录及其子目录中的文件列表(我想使用单行脚本):

IFS=$(echo -en "\n\b");
for FILE in $(find -type f); do echo "$FILE"; done
Run Code Online (Sandbox Code Playgroud)

通常,它按预期工作,但最近,使用我的文件列表:

file_.doc
file_0.doc
_文件[2006_02_25] .DOC
_文件[2016_06_16]的.odt
_文件[2016_06_16] .PDF
_文件[16-6-2006] .DOC
file_.pdf
_文件4-4-2006.doc

输出是:

./file_.doc                                                                                                                                                 
./file_0.doc                                                                                                                                                
./file_0.doc
./file_[2016_06_16].odt
./file_[2016_06_16].pdf
./file_0.doc
./file_.pdf
./file_ 4-4-2006.doc
Run Code Online (Sandbox Code Playgroud)

如果我将变量 IFS 更改为:

IFS=$(echo -en "\n");
Run Code Online (Sandbox Code Playgroud)

那么输出将是(更正):

./file_.doc
./file_0.doc
./file_[2006_02_25].doc
./file_[2016_06_16].odt
./file_[2016_06_16].pdf
./file_[16-6-2006].doc
./file_.pdf
./file_ 4-4-2006.doc
Run Code Online (Sandbox Code Playgroud)

我已阅读,'\b'neccesary,发现一个解决方案使用printf的替代,echo

我的问题是:

1) 你能解释一下是什么让这些输出不同吗?

2)使用printf上面的解决方案可以替代echo -en "\n\b"

ilk*_*chu 4

命令替换的输出受分词(您通过设置处理IFS文件名通配的影响。其[abc]构造是像往常一样“匹配任何字符a, b, c”,并且 \n[2006_02_25].doc匹配0.doc

\n\n
\n\n

在 Bash/ksh/zsh 中,可以使用双星来获取目录树中的所有文件(递归地,不仅仅是当前目录)。这应该找到与您的示例相同的文件:

\n\n
shopt -s globstar      # in Bash\n# set -o globstar      # in ksh\nfor file in **/* ; do\n    [[ -f $file ]] ||\xc2\xa0continue     # check it\'s a regular file, like find -type f\n    ...\ndone\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n\n

当然,它的功能find 强大,如果你有很多条件的话,使用它可能会更容易。如果这样做,set -f除了修复之外,您还应该禁用文件名通配IFS

\n\n
set -f\nIFS=$\'\\n\'\nfor file in $(find -type f -some -other -conditions) ; do\n    ...\ndone\n
Run Code Online (Sandbox Code Playgroud)\n\n

或者使用while read带有进程替换的循环:

\n\n
while IFS= read -r file ; do \n    ...\ndone < <(find -type f -some -other -conditions)\n
Run Code Online (Sandbox Code Playgroud)\n\n

(上面与 类似find ... |\xc2\xa0while ...,但它绕过了管道最后一部分在子 shell 中执行的问题。)

\n\n

这两个都假设文件名不包含换行符,因为它们被用作find. (并且因为$(..)吃掉了最后的换行符。)

\n\n

至少在 Bash 中,实际上有一种方法可以使文件名中的换行符也起作用。将分隔符设置read为空字符串可以有效地使用 NUL 字节作为分隔符。所以:

\n\n
while IFS= read -d \'\' -r file ; do \n    ...\ndone < <(find -type f -some -other -conditions -print0)\n
Run Code Online (Sandbox Code Playgroud)\n\n

尽管这开始变得令人讨厌,因为您确实需要所有这些选项才能read使其工作而不破坏输入。

\n\n
\n\n

至于IFS... 设置IFS=$(echo -en "\\n");设置IFS为空字符串(因为命令替换会吃掉尾随换行符),从而导致拆分。在这种情况下,输出似乎是正确的,因为您一次性获得了所有输出find,而不是逐行获得。这也掩盖了文件名通配的问题,因为完整的多行字符串不匹配任何文件名并且按原样传递。

\n\n

如果您执行其他操作而不只是打印循环值,您就会看到差异。尝试添加一些分隔符:

\n\n
IFS=$(echo -en "\\n")       # same as IFS=\nfor FILE in $(find -type f); do echo "<$FILE>"; done\n
Run Code Online (Sandbox Code Playgroud)\n\n

到目前为止,IFS除了最简单的标准 shell 之外,在任何其他 shell 中设置换行符的最简单方法是IFS=$\'\\n\'.

\n