在连续命令中将文件名维护为单独的参数

在连续命令中将文件名维护为单独的参数

免责声明:这个不是与 ls 或 grep 等特定命令相关,尽管我在示例中使用它们,但这更多是 bash 的事情。

假设我想查看一组文件 (ls -l) 的文件大小、时间戳和权限/所有者/组,这些文件都包含“某个单词”。通常我会这样做:

ls -l $( grep -l 'a certain word' * )

这就像魔术一样,直到任何文件名包含空格,例如名为“文件名”的文件。那么 ls 找不到它认为的两个文件名,“file”和“name”。

Bash 脚本有一种熟悉的方法来解决此问题的变体,如果您在命令行上将文件名传递给脚本,它们将被引用为 ${1}、${2} 等。如果您将它们包装在引号,即“${1}”、“${2}”,那么如果它们有空格,你就没有问题,甚至可以处理全部使用“${@}”立即显示文件名,它将正确列出每个参数,并保持它们完整,即使它们有空格 - 我只是还没有找到这样的机制来解决我列出的 grep/ls 样式问题多于。如果您用引号包装扩展命令,$( )它会将所有文件视为 ls 的一个大参数。

我通常会用类似的方法来解决这个问题grep -l .... | while read file; do ls -l "${file}"; done,但我真的不应该这样做。这样做仍然不允许按日期/大小/等对文件进行排序(即ls -ltr $( grep -l ....)

我一直认为 bash 有一个解决方案,我只是还没有遇到过,这个问题只是偶尔出现一次,我通常会解决它,但现在它已经足够困扰我了,我认为一定有办法。

是的,我看到了以下内容,它们要么是由正在使用的特定工具(如 tar)解决的问题,要么答案是“抱歉,您无法从这里到达那里”,因为他们的问题是特定于命令的。我要求处理作为结果给出的任何嵌入空格的文件名,并将其作为参数传递给另一个命令,而不是更多的 while 或找到魔术来解决这个问题。我宁愿这个问题没有答案(证明没有解决方案),而不是提供任何不相同的问题:

答案1

唔。 @don_crissti 已经给出了答案对于 grep在评论中。但既然你说这并不是真正关于grepor ls,我将重写有问题的命令以不使用这些命令。

我认为你想要的是:

do-something-with $(produce-list-of-files)

其中一个命令的输出应作为另一命令的命令行参数放入。恰好有一个实用程序专门用于此目的,它称为xargs (手册页)

如果文件名“好”,我们可以这样做

produce-list-of-files | xargs do-something-with

如果文件名可以包含空格但由换行符分隔,我们必须告诉xargs不要拆分任何空格,但只有换行符:

produce-list-of-files | xargs -d '\n' do-something-with

如果文件名也可以包含换行符,则列表必须用 NUL('\0',值为零的字节)分隔,并且我们需要一个xargs支持它的文件名。至少各种实用程序的某些版本支持列出由 NUL 而不是换行符分隔的文件,在这些工具的 GNU 版本中至少有find -print0,sort -z和。标志grep -Z中是或。所以:xargs--null-0

produce-list-of-files -0 | xargs -0 do-something-with

cat一个使用and运行的示例ls -l

$ touch "abba acdc" "foo bar" $'new\nline'
$ echo -en "abba acdc\0foo bar\0new\nline\0" > list
$ cat list | xargs -0 ls -l
-rw-r--r-- 1 itvirta itvirta 0 Aug  2 01:23 abba acdc
-rw-r--r-- 1 itvirta itvirta 0 Aug  2 01:23 foo bar
-rw-r--r-- 1 itvirta itvirta 0 Aug  2 01:23 new?line

答案2

如果您有一个命令生成以换行符分隔的项目列表(例如文件名),并且这些项目本身可以包含换行符,那么您无法从这里到达那里。

某些系统上的某些命令可以生成或管理由空字节而不是换行符分隔的列表,例如 GNU grep ( grep -Z)、GNU sed ( sed -z)、GNU awk ( gawk RS='\0' ORS='\0')、GNU 和 BSD find ( find -print0) 等,然后您可以使用xargs -0它来执行命令在空分隔列表的项目上。

find … -print0 | sed -z … | xargs -0 frob …

如果您愿意假设文件名不包含换行符,则可以使用 split+glob 运算符(即不带引号的扩展)来拆分换行符分隔列表。您需要关闭 gobbingset -f并设置分隔符以将(IFS变量)拆分为换行符。

IFS='
'; set -f
ls -l -- $(grep -l 'a certain word' *)
set +f; unset IFS

最后一行恢复默认设置。准确地保存和恢复set -f和的设置IFS是可以做到的,但是很痛苦。

如果需要保存输出以供以后处理,可以在定义时将其拆分并将数据存储在数组中,或者在使用时将其拆分并将数据存储在字符串中。使用数组(需要 ksh、bash 或 zsh):

IFS='
'; set -f
files=($(grep -l 'a certain word' *))
set +f; unset IFS
ls -l -- "${files[@]}"

用字符串:

files=$(grep -l 'a certain word' *)
IFS='
'; set -f
ls -l -- $files
set +f; unset IFS

也可以看看为什么我的 shell 脚本会因为空格或其他特殊字符而卡住?

相关内容