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"
?
命令替换的输出受分词(您通过设置处理IFS
)和文件名通配的影响。其[abc]
构造是像往常一样“匹配任何字符a
, b
, c
”,并且 \n[2006_02_25].doc
匹配0.doc
。
在 Bash/ksh/zsh 中,可以使用双星来获取目录树中的所有文件(递归地,不仅仅是当前目录)。这应该找到与您的示例相同的文件:
\n\nshopt -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当然,它的功能find
很强大,如果你有很多条件的话,使用它可能会更容易。如果这样做,set -f
除了修复之外,您还应该禁用文件名通配IFS
:
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
带有进程替换的循环:
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 中执行的问题。)
这两个都假设文件名不包含换行符,因为它们被用作find
. (并且因为$(..)
吃掉了最后的换行符。)
至少在 Bash 中,实际上有一种方法可以使文件名中的换行符也起作用。将分隔符设置read
为空字符串可以有效地使用 NUL 字节作为分隔符。所以:
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
使其工作而不破坏输入。
至于IFS
... 设置IFS=$(echo -en "\\n");
设置IFS
为空字符串(因为命令替换会吃掉尾随换行符),从而导致不拆分。在这种情况下,输出似乎是正确的,因为您一次性获得了所有输出find
,而不是逐行获得。这也掩盖了文件名通配的问题,因为完整的多行字符串不匹配任何文件名并且按原样传递。
如果您执行其他操作而不只是打印循环值,您就会看到差异。尝试添加一些分隔符:
\n\nIFS=$(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\'
.
归档时间: |
|
查看次数: |
957 次 |
最近记录: |