在 unix 终端中,如果我执行:
egrep "Stuff" $(ls *.txt)
结果是显而易见的。但是
$(ls *.txt)
我能做到吗?我发现我可以通过以下方法复制效果
egrep "Stuff" *.txt
那么是什么$(ls *.txt)
?
答案1
“做什么$(ls *.txt)
”
简短回答
$(ls *.txt)
收集文件名列表并然后弄乱它们。 做不是用这个。
较长的答案
ls
旨在提供人类可读的输出。作为 ls 的维护者之一写道回答为什么ls
不提供--null
选项:如果我们这样做,那么这就是我们将使用的界面。然而
ls
实际上是供人类直接消费的工具,在这种情况下,进一步处理就没那么有用了。对于进一步处理,find
(1)更适合。[强调]换句话说,
ls
除了向人类展示之外,其他任何用途都不受支持。ls
例如,维护人员最近将默认输出格式更改为他们认为更加人性化的格式。没有警告。因此,请遵循他们的建议:如果您要进一步处理文件名,请使用find
而不是ls
。$(...)
是命令替换。使用命令替换的一个后果是删除尾随换行符。如果列表中的最后一个文件名恰好包含尾随换行符,则它们将被删除。由于
$(...)
没有双引号,它生成的文本将受到以下约束:分词
路径名扩展
这两种情况的结果是文件名进一步混乱。
更不用说由于
-
缺失而导致的文件名问题--
(对于两者ls
,egrep
因为 Ubuntuegrep
是 GNU 实现,即使在非选项参数之后也接受选项,除非POSIXLY_CORRECT
在环境中)。另外,如果这些
txt
文件中的任何一个属于以下类型目录,ls
将列出他们的内容而不是他们自己。如果没有
txt
文件,则的输出ls
将为空(尽管您会看到有关缺少文件的错误*.txt
),并且由于egrep
没有收到文件参数,它将查找东西在其标准输入中(并且似乎挂起)。
例子
让我们Stuff
在目录中创建包含以下 4 个文件:
$ echo Stuff | tee file1 file2 'a b c.txt' 'f* .txt'
Stuff
$ ls -Q
"a b c.txt" "file1" "file2" "f* .txt"
现在,让我们运行 egrep 命令:
$ egrep "Stuff" $(ls *.txt)
grep: a: No such file or directory
grep: b: No such file or directory
grep: c.txt: No such file or directory
file1:Stuff
file2:Stuff
f* .txt:Stuff
grep: .txt: No such file or directory
观察我们收到 4 条有关不存在文件的错误消息。这是由于单词拆分。结果还显示与两个文件匹配,file1
并且file2
不应该搜索它们,因为它们没有以结尾.txt
。这是因为 _pathname 扩展。
正确书写的命令产生两次成功匹配并且没有错误:
$ egrep -- "Stuff" *.txt
a b c.txt:Stuff
f* .txt:Stuff
推荐的解决方案
使用:
egrep -- "Stuff" *.txt
或者 POSIXly:
grep -E -- "Stuff" *.txt
或者:
grep -E -e Stuff -- *.txt
这适用于任何文件名,并且没有任何方法的限制ls
。
答案2
运行$( )
该命令ls *.txt
并返回STDOUT
该命令的。
这种特殊用法至少在三个层面上是新手编程的错误:
egrep 'Stuff' *.txt
就像你说的,除了名为A File.txt
- 使用
ls
输出作为程序的输入是不明智的。参见原因 $( ls *.txt)
错误处理带有空格和其他奇怪字符的文件名。find . -maxdepth 1 -type f -iname '*.txt' -print0 | xargs -0 egrep 'Stuff'
是一种更防弹的方式。
针对@8bittree 的回应,shell glob 扩展/混淆的一个例子是:
$ /bin/ls -l -b
total 36
-rw------- 1 w3 walt 2 Aug 11 13:41 A\ \\012\ File.txt
-rw------- 1 w3 walt 2 Aug 11 13:42 A\ \n\ File.txt
-rw------- 1 w3 walt 2 Aug 11 13:40 A\ File.txt
$ grep "STUFF" $(ls *.txt)
grep: A: No such file or directory
grep: \012: No such file or directory
grep: File.txt: No such file or directory
grep: A: No such file or directory
grep: File.txt: No such file or directory
grep: A: No such file or directory
grep: File.txt: No such file or directory
$ find . -maxdepth 1 -type f -iname '*.txt' -print0 | xargs -0 egrep 'Stuff'
$ find . -maxdepth 1 -type f -iname '*.txt' -print0 | xargs -0 egrep '1'
./A File.txt:1
答案3
哇,有一瞬间我以为你想知道`$(ls *.txt)`
(带$(..)
加号反引号)
由于这个问题已经得到解答,我只想警告你一件事。下面是一系列命令,向你展示为什么这样做很危险:
set -x
查看执行的内容touch 'rm -f *.txt'
创建一个名为“rm -f *.txt”的文本文件,我在这里使用简单的引号,因此 Bash 不会扩展通配符和空格。`$(ls *.txt)`
这是棘手的部分。该命令ls *.txt
将被运行,然后将结果重定向到 STDOUT。rm -f *.txt
由于反引号,现在将执行此结果。
因此,rm 将删除所有文本文件,在我们的例子中是文件rm -f *.txt
我希望你能理解——作为演示,只需在空目录中运行前面的命令,这样你就不会破坏任何东西。
答案4
这个问题在现实世界中似乎没什么用处,但是,
获得可用结果的更好方法是:
echo $(ls *.txt)
要不就
ls *.txt
这将列出所有扩展名为 .txt 的文件。要查找包含特定字符串的文本文件,
egrep -l "Stuff" *.txt
有关搜索工具的更多信息,
man egrep