这个问题的灵感源于

为什么使用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返回的每个文件名/路径运行一个或多个命令的正确方法是什么?

评论

我认为这有点像“永不解析ls输出!” -当然,您可以一次性完成任何一项操作,但是与生产质量相比,它们更多的是快速的技巧。或者,更笼统地说,绝对不要教条。

更笼统地说:为什么我的shell脚本会在空白或其他特殊字符上出现问题?

这应该变成一个规范的答案

因为查找的重点是遍历所找到的内容。

一个辅助点-您可能希望将输出发送到文件,然后在脚本中稍后对其进行处理。如果您需要调试脚本,则可以通过这种方式查看文件列表。

#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循环的唯一方法将使用支持zshIFS=$'-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个文件参数。

使用bashfind -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/bazcmd),则当./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é.txtbash这样的地方,其中逐渐增加了多字节支持。这些将回到考虑不能像字符一样映射到字符的字节。他们仍然在这里和那里仍然存在一些错误,特别是对于不常见的多字节字符集,例如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 {} \;




findsh组合


如果您需要在命令中使用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 bash:删除不包含目录的目录给定字符串
文件中的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方法是使用管道,这是固有的并行运行,并且通常也不需要任意大的缓冲区。这意味着:您更希望findsmth并行运行,并且仅将当前文件名保留在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的建议时,如果不传达您所犯的陷阱和假设,这通常是一个问题,因为其他人的文件名可能有毒。