带空格的Bash和文件名

时间:2009-10-15 20:39:15

标签: bash command-line

以下是一个简单的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(./thisisafile.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`

这是构建命令行的唯一方法吗? 正确处理带空格的文件名?

6 个答案:

答案 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