为什么使用shell循环来处理文本被认为是不好的做法?
我看到了这些结构
for file in `find . -type f -name ...`; do smth with ${file}; done
和
for dir in $(find . -type d -name ...); do smth with ${dir}; done
几乎每天都在这里使用,即使有些人花时间评论这些帖子,解释为什么这种东西应该避免...
看到此类帖子的数量(以及有时忽略这些评论的事实),我想我不妨问一个问题:
为什么循环
find
的输出错误做法,以及对find
返回的每个文件名/路径运行一个或多个命令的正确方法是什么?#1 楼
问题for f in $(find .)
合并了两个不兼容的内容。
find
打印由换行符分隔的文件路径列表。当您在列表上下文中未引用该$(find .)
时调用的split + glob运算符将其拆分为$IFS
的字符(默认情况下包括换行符,还包括空格和制表符(以及zsh
中的NUL)),并对每个结果生成glob单词(zsh
除外)(甚至ksh93或pdksh派生中的大括号扩展!)。即使您这样做,也是如此:IFS='
' # split on newline only
set -o noglob # disable glob (also disables brace expansion in pdksh
# but not ksh93)
for f in $(find .) # invoke split+glob
这仍然是错误的,因为换行符与文件路径中的任何字符一样有效。
find -print
的输出根本无法可靠地进行后处理(除非使用一些复杂的技巧,如下所示)。这也意味着shell需要将
find
的输出完全存储,然后进行分割+在开始循环遍历文件之前,先对其进行glob处理(这意味着将输出第二次存储在内存中)。请注意,
find . | xargs cmd
存在类似的问题(存在,空白,换行,单引号,双引号和反斜杠)。 (以及某些xarg
实现字节不构成有效字符的一部分)是一个问题)更正确的选择
在
for
输出上使用find
循环的唯一方法将使用支持zsh
的IFS=$'-print0
'
和:IFS=$'find . -exec something with {} \;
'
for f in $(find . -print0)
(将
-exec printf '%sfind
' {} +
替换为-print0
,以实现不支持非标准的-exec
实现(但如今非常普遍) something
)。这里,正确且可移植的方法是使用
sh
:find . -exec something with {} +
或者如果
something
可以接受多个参数:find . -exec sh -c '
for file do
something < "$file"
done' find-sh {} +
如果确实需要由shell处理的文件列表:
find . -print0 | xargs -r0 something with
(请注意,它可能会启动多个
stdin
)。在某些系统上,您可以使用:
xargs -r0n 20 -P 4 -a <(find . -print0) something
尽管这与标准语法相比没有什么优势,并且意味着
/dev/null
的-P
是管道还是xargs
。您可能要使用的一个原因可能是使用GNU
stdin
选项进行并行处理。 xargs
问题也可以与带有-a
选项的GNU something
一起解决,其中shell支持进程替换:例如,最多运行4个并发调用zsh
的并发调用。每个参数都包含20个文件参数。使用
bash
或find -print0
,另一种循环遍历read -d ''
输出的方法是:while IFS= read -rd '' file <&3; do
something "$file" 3<&-
done 3< <(find . -print0)
bash-4.4
读取NUL分隔记录而不是换行分隔记录。find -print0
及更高版本还可以将zsh
返回的文件存储在具有以下内容的数组中:readarray -td '' files < <(find . -print0)
find
等效项(具有保留zsh
退出状态的优点):files=(${(0)"$(find . -print0)"})
使用
find
,您可以将大多数find . -name '*.txt' -type f -mtime -1
表达式转换为递归glob与glob的组合限定词。例如,循环遍历--
将是:for file (./**/*.txt(ND.m-1)) cmd $file
或
for file (**/*.txt(ND.m-1)) cmd -- $file
(请注意
**/*
和./
一样,文件路径不是以-
开头,因此可以以ksh93
开头。bash
和**/
最终增加了对**
的支持(尽管没有更多的递归形式)仍然不是glob限定词,这使得bash
的使用非常有限。还要注意,降序目录树时,版本4.3之前的$(find .)
遵循符号链接。就像循环
find
一样,这也意味着将整个文件列表存储在memory1中。在某些情况下,当您不希望对文件执行的操作对文件的查找有影响时(例如,当您添加更多可能最终被自己发现的文件时),这可能是合乎需要的。其他可靠性/安全性考虑因素
竞赛条件
现在,如果我们谈论可靠性,我们必须提到
zsh
/ find
发现之间的竞争条件一个文件,并检查其是否符合标准以及使用时间(TOCTOU竞赛)。即使降级目录树,也必须确保不遵循符号链接,并且TOCTOU比赛。
find
(至少是GNU openat()
)通过使用带有正确的O_NOFOLLOW
标志(受支持)的zsh
打开目录并为每个目录保持打开的文件描述符来做到这一点,bash
/ ksh
/ find
不这样做。因此,面对攻击者能够在正确的时间用符号链接替换目录的情况,您可能最终会降级错误的目录。即使
-exec cmd {} \;
确实正确降级了目录,也可以使用-exec cmd {} +
和如果使用cmd
,则更是如此,一旦执行cmd ./foo/bar
(例如cmd ./foo/bar ./foo/bar/baz
或cmd
),则当./foo/bar
使用bar
时,find
的属性可能不再满足./foo
匹配的条件,但更糟糕的是,可能已替换了-exec {} +
通过符号链接到其他地方(并且find
使竞争窗口变大了,其中cmd
等待有足够的文件来调用find
)。某些
-execdir
实现具有(非标准的) find
谓词可缓解第二个问题。具有:
find . -execdir cmd -- {} \;
在运行
chdir()
之前,将cmd
放入文件的父目录中。而不是调用cmd -- ./foo/bar
,而是调用cmd -- ./bar
(在某些实现中是cmd -- bar
,因此是--
),因此避免了将./foo
更改为符号链接的问题。这使得使用rm
之类的命令更加安全(它仍然可以删除其他文件,但不能删除其他目录中的文件),但是除非这些文件被设计为不遵循符号链接,否则不能修改这些文件的命令。-execdir cmd -- {} +
有时也可以使用,但在包括某些版本的GNU find
在内的几种实现中,它等效于-execdir cmd -- {} \;
。-execdir
还具有解决与太深目录树相关的一些问题的好处。 。在:
find . -exec cmd {} \;
给
cmd
的路径的大小将随着文件所在目录的深度而增加。该大小变得大于PATH_MAX
(类似于Linux上的4k),则cmd
在该路径上执行的任何系统调用都会失败,并出现ENAMETOOLONG
错误。使用
-execdir
时,仅文件名(可能以前缀./
)传递给cmd
。大多数文件系统上的文件名本身的限制(NAME_MAX
)比PATH_MAX
的限制低得多,因此ENAMETOOLONG
出错的可能性较小。字节与字符
此外,考虑到
find
的安全性(通常更一般地处理文件名)时,通常被忽略的事实是,在大多数类似Unix的系统上,文件名是字节序列(文件路径中除0外的任何字节值,而在大多数系统上(ASCII基于的文件,我们现在将忽略基于EBCDIC的罕见文件)0x2f是路径分隔符)。由应用程序决定是否将这些字节视为文本。它们通常这样做,但是通常从字节到字符的转换是根据用户的语言环境和环境完成的。
这意味着给定的文件名可能具有不同的文本表示形式,具体取决于地区。例如,对于在字符集为ISO-8859-1的语言环境中解释该文件名的应用程序,而在字符集为IS0-8859-5的语言环境中解释该文件名,字节序列
63 f4 74 e9 2e 74 78 74
将为côté.txt
。 > 更糟。在字符集为UTF-8(当今的规范)的语言环境中,根本无法将63 f4 74 e9 2e 74 78 74映射到字符!
cєtщ.txt
是一个这样的应用程序,它考虑文件名作为其find
/ -name
谓词的文本(以及更多内容,例如-path
或-iname
的某些实现)。这意味着例如,具有多个
-regex
实现(包括GNU find
)。 > find . -name '*.txt'
在UTF-8语言环境中以
find
(匹配0个或多个字符,而不是字节)调用时,找不到上面的63 f4 74 e9 2e 74 78 74
文件与那些非字符不匹配。 *
将解决该问题,因为C语言环境意味着每个字符一个字节,并且(通常)保证所有字节值都映射到一个字符(尽管某些字节值可能是未定义的)。 > 现在,当涉及从shell循环这些文件名时,字节和字符之间的关系也可能成为问题。在这方面,我们通常会看到4种主要的shell类型:
仍然不是多字节感知的壳,例如
LC_ALL=C find...
。对于他们来说,一个字节映射到一个字符。例如,在UTF-8中,dash
为4个字符,但为6个字节。在以UTF-8为字符集的语言环境中,请在find . -name '????' -exec dash -c '
name=${1##*/}; echo "${#name}"' sh {} \;
côté
将成功找到名称由4个以UTF-8编码的字符组成的文件,但是find
将报告长度在4到24之间的文件。dash
:相反。它仅处理字符。它所需要的所有输入都在内部翻译为字符。它使外壳最一致,但也意味着它不能应付任意字节序列(那些不能转换为有效字符的序列)。即使在C语言环境中,它也不能处理大于0x7f的字节值。find . -exec yash -c 'echo ""' sh {} \;
在我们的ISO-8859-1
yash
中,在UTF-8语言环境中将失败例如较早的版本。像
côté.txt
或bash
这样的地方,其中逐渐增加了多字节支持。这些将回到考虑不能像字符一样映射到字符的字节。他们仍然在这里和那里仍然存在一些错误,特别是对于不常见的多字节字符集,例如GBK或BIG5-HKSCS(它们非常讨厌,因为它们的许多多字节字符包含0-127范围内的字节(例如ASCII字符) )。类似于FreeBSD的
zsh
(至少11个)或sh
,它们支持多字节,但仅适用于UTF-8。注意事项
1 For完整性,我们可以在
mksh -o utf8-mode
中提到一种利用递归glob循环遍历文件而不将整个列表存储在内存中的骇人方式:process() {
something with $REPLY
false
}
: **/*(ND.m-1+process)
是zsh
是调用+cmd
的glob限定符(通常是一个函数),其当前文件路径位于cmd
中。该函数返回true或false以决定是否应选择文件(并且还可以修改$REPLY
或返回$REPLY
数组中的多个文件)。在这里,我们在该函数中进行处理并返回false,因此不会选择该文件。评论
正确的方法是使用find的底线。 -exec用{} \;或找到。 -print0 | xargs -r0有什么用?在这两种情况下,我是否都需要编写一个函数/脚本(在其中用来调用),其中包含本该包含在for循环中的任何代码行?谢谢。
–乔什
20-4-11在20:05
#2 楼
为什么循环遍历
find
的输出是不正确的做法?简单的答案是:
因为文件名可以包含任何字符。
因此,没有可以可靠地用来分隔文件名的可打印字符。
换行符经常(不正确地)用于分隔文件名,因为包含换行符并不常见文件名中的字符。
但是,如果您围绕任意假设构建软件,则充其量只是无法处理异常情况,最坏的情况是容易受到恶意利用,从而无法控制系统。因此,这是鲁棒性和安全性的问题。
如果您可以用两种不同的方式编写软件,并且其中一种可以正确处理边缘情况(异常输入),但是另一种则更易于阅读,您可能会争辩说有一个权衡。 (我不会。我更喜欢正确的代码。)
但是,如果正确且健壮的代码版本也易于阅读,则没有理由在极端情况下编写失败的代码。
find
就是这种情况,需要在找到的每个文件上运行命令。更具体一点:在UNIX或Linux系统上,文件名可以包含任何字符,除了
/
(用作路径组件分隔符),并且它们可能不包含空字节。因此,空字节是分隔文件名的唯一正确方法。
由于GNU
find
包含一个-print0
主对象,它将使用空字节来分隔其打印的文件名,因此GNU find
可以安全地与GNU xargs
及其-0
标志(和-r
标志)一起使用,以处理find
的输出: br /> find ... -print0 | xargs -r0 ...
但是,没有充分的理由使用这种形式,因为:
它添加了对GNU findutils的依赖性不需要在那里,并且
find
被设计为能够在找到的文件上运行命令。另外,GNU
xargs
需要-0
和-r
,而FreeBSD xargs
只需要-0
(并且没有-r
选项),而某些xargs
根本不支持-0
。因此,最好坚持使用find
的POSIX功能(请参阅下一节),并跳过xargs
。关于第2点,
find
能够在找到的文件上运行命令,我认为Mike Loukides说得最好:find
的业务正在评估表达式-而不是查找文件。是的,find
当然可以找到文件;但这实际上只是副作用。--Unix电动工具
POSIX指定对
find
的使用为
find
的每个结果运行一个或多个命令的正确方法是什么?要为找到的每个文件运行一个命令,请使用:
find dirname ... -exec somecommand {} \;
要对找到的每个文件依次运行多个命令,仅在第一个命令成功的情况下才应运行第二个命令,请使用: br />
要一次对多个文件运行单个命令:
find dirname ... -exec somecommand {} \; -exec someothercommand {} \;
find
与sh
组合如果您需要在命令中使用shell功能,例如重定向输出或从文件名中删除扩展名或类似内容,则可以使用
sh -c
构造。您应该了解以下几点:切勿将{}
直接嵌入到sh
代码中。这允许从恶意制作的文件名中任意执行代码。而且,POSIX实际上甚至没有指定它可以工作。 (请参阅下一点。)请勿多次使用
{}
,或将其用作较长参数的一部分。这不是便携式的。例如,不要执行以下操作:find ... -exec cp {} somedir/{}.bak \;
引用
find
的POSIX规范:如果Utility_name或参数字符串包含两个字符“ {}”,而不仅仅是两个字符“ {}”,则由find替换这两个字符还是使用不更改的字符串由实现定义。
...如果存在多个包含两个字符“ {}”的参数,则行为未指定。
传递了shell命令字符串后的参数从
-c
开始,将
选项中的to设置为外壳的位置参数。并非以find-sh
开头。出于这个原因,最好包含一个“虚拟” "$@"
值,例如"$@"
,该值将用于在生成的shell中进行错误报告。而且,这允许在将多个文件传递到外壳时使用诸如for f do
之类的构造,而省略for f in "$@"; do
的值将意味着传递的第一个文件将设置为find
,因此不包含在find
中。要对每个文件运行单个shell命令,请使用:
find dirname ... -exec somecommand {} +
但是,通常在处理shell中的文件时会提供更好的性能。循环,这样就不会为找到的每个文件生成外壳程序:
find dirname ... -exec sh -c 'somecommandwith ""' find-sh {} \;
(请注意,q4312079q等效于q4312079q,并依次处理每个位置参数—换句话说,它使用q4312079q找到的每个文件,而不管文件名中是否有特殊字符。)
正确的q4312079q用法的其他示例:
(注意:请随意扩展此列表。)
通过`file`命令的解析输出,过滤`find`生成的文件
find -exec中的子字符串删除
如何做L ist与Find进行比较?
在find -exec
文件中的Grep单词,然后复制文件
删除除文件夹中的某些类型的文件
评论
在一种情况下,我不知道解析find输出的替代方法-在这种情况下,您需要为每个文件在当前shell中运行命令(例如,因为要设置变量)。在这种情况下,当IFS =读取-r -u3 -d''文件时;做...完成3 <<(查找... -print0)是我所知道的最好的成语。注意:<()不可移植-使用bash或zsh。另外,如果循环中的任何内容尝试读取stdin,则-u3和3 <存在。
–戈登·戴维森(Gordon Davisson)
16年11月7日在23:42
@GordonDavisson,也许-但是您需要为这些变量设置什么呢?我会争辩说,无论它是什么都应该在find ... -exec调用中处理。或者,如果它可以处理您的用例,则仅使用shell glob。
–通配符
16年7月7日在23:45
我经常想在处理文件后打印摘要(“转换了2个,跳过了3个,以下文件有错误:...”),这些计数/列表必须累积在shell变量中。另外,在某些情况下,我想创建一个文件名数组,这样我可以做的事情比按顺序进行迭代要复杂得多(在这种情况下,它是filelist =();而...则是filelist + =(“ $ file”);完成...)。
–戈登·戴维森(Gordon Davisson)
16年11月7日在23:53
您的回答是正确的。但是我不喜欢教条。尽管我了解得更多,但在许多(特别是交互式)用例中,安全性很高,并且在查找输出时键入循环更容易,甚至使用ls更糟。我每天都这样做,没有问题。我知道各种工具的-print0,-null,-z或-0选项。但是除非真正需要,否则我不会浪费时间在交互式shell提示符下使用它们。这也可以在您的答案中注明。
– rudimeier
16年11月8日在19:48
@rudimeier,关于教条与最佳实践的争论已经to折。没兴趣。如果您以交互方式使用它并且可以正常工作,那么对您有好处,但是我不会提倡这样做。脚本作者不愿意学习健壮的代码,然后仅在编写生产脚本时才去做,而不仅仅是习惯于交互地进行操作,所以他们所占的比例非常小。处理方法是始终在推广最佳实践。人们需要了解存在正确的做事方法。
–通配符
16年11月8日在20:07
#3 楼
该答案适用于非常大的结果集,并且主要涉及性能,例如,通过慢速网络获取文件列表时。对于少量文件(例如本地磁盘上的几百个甚至什至1000个文件),大多数都没有意义。并行性和内存使用情况
除了其他答案给定的,与分离问题等有关,还有一个问题,在行中断之前,必须首先对反引号内的部分进行充分评估。这意味着,如果您获得大量文件,它可能会阻塞各种组件中存在的大小限制;如果没有限制,则可能会耗尽内存;在任何情况下,您都必须等到
find
输出整个列表,然后再由for
解析,然后再运行第一个smth
。首选的unix方法是使用管道,这是固有的并行运行,并且通常也不需要任意大的缓冲区。这意味着:您更希望
find
与smth
并行运行,并且仅将当前文件名保留在RAM中,同时将其交给smth
。 前述的
find -exec smth
是至少部分可行的解决方案。它消除了将所有文件名保留在内存中的需求,并且可以很好地并行运行。不幸的是,每个文件也启动一个smth
进程。如果smth
只能处理一个文件,则必须这样。如果可能的话,最佳解决方案是
find -print0 | smth
,其中smth
能够在其STDIN上处理文件名。这样,无论有多少文件,您都只有一个smth
进程,并且两个进程之间仅需要缓冲少量字节(无论正在进行什么内部管道缓冲)。当然,如果smth
是标准的Unix / POSIX命令,这是不现实的,但是如果您自己编写,则可能是一种方法。如果这不可能,那么
find -print0 | xargs -0 smth
很可能是一个更好的解决方案。就像注释中提到的@ dave_thompson_085一样,当达到系统限制(默认情况下,在128 KB范围内或xargs
对系统施加的任何限制)下,smth
确实将参数分散在exec
的多次运行中。影响一次调用smth
的文件数量,从而在smth
进程数和初始延迟之间找到平衡。编辑:删除了“最佳”的概念-很难说是否有东西会更好。 ;)
评论
查找... -exec smth {} +是解决方案。
–通配符
16年11月8日,0:46
查找-print0 | xargs smth根本不起作用,但是找到-print0 | xargs -0 smth(注意-0)或查找| xargs smth,如果文件名没有空格引号或反斜杠运行1 smth,并且文件名与可用的文件数一样多,并且适合一个参数列表;如果超过maxargs,它将运行smth多次以处理所有给定的args(无限制)。您可以使用-L /-max-lines -n /-max-args -s /-max-chars设置较小的“块”(因此,并行度要早一些)。
–dave_thompson_085
16-11-08在1:19
相关:递归grep vs查找/ -type f -exec grep {} \;哪个更有效/更快?
–StéphaneChazelas
16年8月8日在9:16
#4 楼
原因之一是空格在作品中抛出了一个扳手,使得文件'foo bar'被评估为'foo'和'bar'。$ ls -l
-rw-rw-r-- 1 ec2-user ec2-user 0 Nov 7 18:24 foo bar
$ for file in `find . -type f` ; do echo filename $file ; done
filename ./foo
filename bar
$
如果- exec代替了
$ find . -type f -exec echo filename {} \;
filename ./foo bar
$ find . -type f -exec stat {} \;
File: ‘./foo bar’
Size: 0 Blocks: 0 IO Block: 4096 regular empty file
Device: ca01h/51713d Inode: 9109 Links: 1
Access: (0664/-rw-rw-r--) Uid: ( 500/ec2-user) Gid: ( 500/ec2-user)
Access: 2016-11-07 18:24:42.027554752 +0000
Modify: 2016-11-07 18:24:42.027554752 +0000
Change: 2016-11-07 18:24:42.027554752 +0000
Birth: -
$
评论
特别是在find的情况下,因为可以在每个文件上执行命令,所以它很容易成为最佳选择。
–世纪
16年11月7日在18:35
还考虑-exec ... {} \;与-exec ... {} +
– thrig
16年11月7日在18:51
如果您在“ $(find。-type f)”中使用文件并回显“ $ {file}”,则即使在空格下也可以使用,但我猜其他特殊字符会带来更多麻烦
–令人惊讶
16年11月7日在18:51
@mazs-不,引用不符合您的想法。在包含多个文件的目录中,尝试在“ $(find。-type f)”中查找文件;执行printf'%s%s \ n'名称:“ $ {file}”;执行(应根据您的要求)打印每个文件文件名放在单独的行上,并以name:开头。没有。
–don_crissti
16-11-7在19:34
#5 楼
因为任何命令的输出都是单个字符串,但是循环需要一个字符串数组来循环。它“起作用”的原因是外壳出卖了您的空白字符串。其次,除非您需要
find
的特定功能,否则请注意您的外壳很可能已经可以扩展递归glob模式本身,并且至关重要的是,它将扩展为适当的数组。bash示例:
shopt -s nullglob globstar
for i in **
do
echo «"$i"»
done
与Fish相同:
for i in **
echo «$i»
end
如果确实需要
find
的功能,请确保仅在NUL上分割(例如find -print0 | xargs -r0
习惯用语)。鱼可以迭代NUL分隔的输出。所以这个实际上还不错:
find -print0 | while read -z i
echo «$i»
end
最后一点,在很多shell中(当然不是Fish),循环命令输出将使循环体成为循环体一个子shell(这意味着您不能以循环终止后可见的任何方式设置变量),这永远不是您想要的。
评论
准确地@don_crissti。通常它不起作用。我试图讽刺地说它“有效”(带引号)。
–user2394284
16-11-12在14:53
请注意,递归glob起源于90年代初的zsh(尽管在那里需要** / *)。但是,像bash的等效功能的早期实现一样,fish会在下降目录树时遵循符号链接。有关实现之间的差异,请参见ls *,ls **和ls ***的结果。
–StéphaneChazelas
16-11-15在12:31
#6 楼
遍历find的输出不是一个坏习惯-在这种情况下(所有情况下)的坏习惯是假设您的输入是一种特定格式,而不是知道(测试并确认)它是一种特定格式。tldr / cbf:
find | parallel stuff
#7 楼
为find返回的每个文件名/路径运行一个或多个命令的正确方法是什么?我在Mac上使用zsh。我在搜索如何将fd命令的结果(管道连接到fzf)放入数组时发现了这个问题。具体来说,我不必担心文件名中的空格存储为单独的元素。然后,通过这个数组,我将这些文件名一次发送到另一个脚本。
我试图对Stephane的答案投赞成票,因为它为我提供了寻找答案所需的详细信息-对我来说就是使用此答案:
array_name=(${(0)"$(fd "\.tar\.gz$|\.tgz$|\.zip$|\.tar.bz2|\.tar$" | fzf --print0)"})
我可能应该更好地了解($ {(0)...})块在做什么。也许它与使用
IFS=$'(q%q%q)
'
的提示有关。我尝试进行快速搜索,发现与\ 0有关。我在这里找到了:在Expansion的zsh文档中,它提到:14.3.1参数Expansion标志
如果右括号后面紧跟着开头括号,
直到匹配的结尾括号之间的字符串将作为标志的
列表。如果重复标记是有意义的,则
重复不必连续。例如,“
(%%qqq)
”与易读的“ 0
”具有相同的含义。支持以下标志:[...]
ps:q4312079q:
将扩展结果拆分为空字节。这是'q4312079q'的简写形式。
为什么循环查找结果的不良做法?
我是最后一个应该这样做的人即使经过几年的bash脚本编写,也请尝试为您提供建议。 :)但是我一直在尝试做的更少的是将命令中的rc放入变量中,然后测试该变量。相反,我尝试使用以下类型的if语句:
if echo “${something}” | grep '^s’ -q; then {
:
} elif [[ $(dirname \""${file}\"") == "." ]]; then {
我希望这对您/某人有帮助。
#8 楼
遍历find的输出通常是一种不好的做法,尤其是在通用脚本中,因为您对情况不太了解,所以使用它。在我的日常工作中,我经常对子目录下的文件以排除可能会击中有毒文件名的可能性。而且我自己从来不会在文件名中使用空格,换行符,制表符等。首先,乞求问题是个坏主意。
例如,我有一个脚本可以缩小相机中的图像,这些图像是通用的,永远不会改变。它们的路径不包含空格,也不包含文件名。 (IMG00001.JPG)如果我有新相机,则需要更改脚本。
对于已发布和遗忘的临时命令,这不是问题。对于单用户计算机,如果您不共享代码,也应该不是问题。
在提供有关SO的建议时,如果不传达您所犯的陷阱和假设,这通常是一个问题,因为其他人的文件名可能有毒。
评论
我认为这有点像“永不解析ls输出!” -当然,您可以一次性完成任何一项操作,但是与生产质量相比,它们更多的是快速的技巧。或者,更笼统地说,绝对不要教条。更笼统地说:为什么我的shell脚本会在空白或其他特殊字符上出现问题?
这应该变成一个规范的答案
因为查找的重点是遍历所找到的内容。
一个辅助点-您可能希望将输出发送到文件,然后在脚本中稍后对其进行处理。如果您需要调试脚本,则可以通过这种方式查看文件列表。