以下是一个简单的Bash命令行:
grep -li 'regex' "filename with spaces" "filename"
没问题。以下工作也很好:
grep -li 'regex' $(<listOfFiles.txt)
其中listOfFiles.txt
包含要grep的文件名列表,一个
每行文件名。
listOfFiles.txt
包含文件名时出现问题
嵌入空间。在我试过的所有情况下(见下文),Bash分裂了
空格处的文件名,例如listOfFiles.txt
中的一行
包含./this is a file.xml
之类的名称最终会尝试运行
grep on each piece(./this
,is
,a
和file.xml
)。
我以为我是一个相对先进的Bash用户,但我找不到了 简单的魔术咒语让这个工作。这是我的事情 试过。
grep -li 'regex' `cat listOfFiles.txt`
如上所述失败(我真的没想到这会起作用),所以我 以为我会在每个文件名周围加上引号:
grep -li 'regex' `sed -e 's/.*/"&"/' listOfFiles.txt`
Bash将引号解释为文件名的一部分,并给出“不这样的 文件或目录“为每个文件(和仍然分割文件名 空白)
for i in $(<listOfFiles.txt); do grep -li 'regex' "$i"; done
这对于原始尝试失败(也就是说,它表现得好像是 引号被忽略)并且非常慢,因为它必须启动一个'grep' 处理每个文件而不是在一次调用中处理所有文件。
以下工作,但如果需要一些小心的双重转义 正则表达式包含shell元字符:
eval grep -li 'regex' `sed -e 's/.*/"&"/' listOfFiles.txt`
这是构建命令行的唯一方法吗? 正确处理带空格的文件名?
答案 0 :(得分:42)
试试这个:
(IFS=$'\n'; grep -li 'regex' $(<listOfFiles.txt))
IFS
是内部字段分隔符。将其设置为$'\n'
会告诉Bash使用换行符来分隔文件名。其默认值为$' \t\n'
,可以使用cat -etv <<<"$IFS"
打印。
将括号括在括号中会启动子shell,以便只有括号内的命令会受到自定义IFS
值的影响。
答案 1 :(得分:8)
cat listOfFiles.txt |tr '\n' '\0' |xargs -0 grep -li 'regex'
xargs上的-0选项告诉xargs使用空字符而不是空格作为文件名终止符。 tr命令将传入的换行符转换为空字符。
这符合OP要求grep不能多次调用的要求。根据我的经验,对于大量避免grep多次调用的文件,可以大大提高性能。
此方案还避免了OP原始方法中的错误,因为他的方案将破坏listOfFiles.txt包含多个文件的位置,这些文件将超过命令的缓冲区大小。 xargs知道最大命令大小,并将多次调用grep以避免该问题。
使用xargs和grep的一个相关问题是,当使用多个文件调用时,grep将使用文件名为输出添加前缀。因为xargs使用多个文件调用grep,所以将接收带有前缀的文件名的输出,但不是listOfFiles.txt中的一个文件的情况,或者是最后一次调用包含一个文件名的多个调用的情况。要实现一致的输出,请将/ dev / null添加到grep命令:
cat listOfFiles.txt |tr '\n' '\0' |xargs -0 grep -i 'regex' /dev/null
请注意,这不是OP的问题,因为他在grep上使用-l选项;然而,这可能是其他人的问题。
答案 2 :(得分:6)
这有效:
while read file; do grep -li dtw "$file"; done < listOfFiles.txt
答案 3 :(得分:1)
虽然它可能超配,但这是我最喜欢的解决方案:
grep -i 'regex' $(cat listOfFiles.txt | sed -e "s/ /?/g")
答案 4 :(得分:0)
请注意,如果您以某种方式结束了包含Windows行结尾的文件中的列表\r\n
,则上述有关输入文件分隔符$IFS
的注释中没有任何内容(并引用参数)将工作;所以请确保行结尾正确\n
(我使用scite
来显示行结尾,并轻松地将它们从一个更改为另一个)。
cat
管道传输while file read ...
似乎也有效(显然无需设置分隔符):
cat <(echo -e "AA AA\nBB BB") | while read file; do echo $file; done
......虽然对我来说这对于&#34; grep&#34;更为重要。通过文件名中包含空格的目录:
grep -rlI 'search' "My Dir"/ | while read file; do echo $file; grep 'search\|else' "$ix"; done
答案 5 :(得分:0)
使用Bash 4,您还可以使用内置mapfile函数来设置包含每一行的数组并迭代此数组:
$ tree
.
├── a
│ ├── a 1
│ └── a 2
├── b
│ ├── b 1
│ └── b 2
└── c
├── c 1
└── c 2
3 directories, 6 files
$ mapfile -t files < <(find -type f)
$ for file in "${files[@]}"; do
> echo "file: $file"
> done
file: ./a/a 2
file: ./a/a 1
file: ./b/b 2
file: ./b/b 1
file: ./c/c 2
file: ./c/c 1