$(ls *.txt) 起什么作用?

$(ls *.txt) 起什么作用?

在 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

  • $(...)是命令替换。使用命令替换的一个后果是删除尾随换行符。如果列表中的最后一个文件名恰好包含尾随换行符,则它们将被删除。

  • 由于$(...)没有双引号,它生成的文本将受到以下约束:

    • 分词

    • 路径名扩展

    这两种情况的结果是文件名进一步混乱。

  • 更不用说由于-缺失而导致的文件名问题--(对于两者lsegrep因为 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该命令的。

这种特殊用法至少在三个层面上是新手编程的错误:

  1. egrep 'Stuff' *.txt就像你说的,除了名为A File.txt
  2. 使用ls输出作为程序的输入是不明智的。参见原因
  3. $( 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

相关内容