Bash和带空格的文件名(Bash and filenames with spaces)

2019-08-18 17:41发布

下面是一个简单的bash命令行:

grep -li 'regex' "filename with spaces" "filename"

没问题。 另外,以下的作品就好了:

grep -li 'regex' $(<listOfFiles.txt)

其中listOfFiles.txt包含文件名列表被grepped,每行一个文件名。

出现问题时listOfFiles.txt包含嵌入空格的文件名。 在我试过(见下文),所有的情况下,击分裂的空间,以便文件名,例如,在一行listOfFiles.txt包含类似名称./this is a file.xml最终试图在每次运行的grep件( ./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`

这是建立在命令行所以它会正确处理的文件名与空间的唯一途径?

Answer 1:

试试这个:

(IFS=$'\n'; grep -li 'regex' $(<listOfFiles.txt))

IFS是内部字段分隔符。 将其设置为$'\n'告诉Bash使用换行符来分隔文件名。 它的默认值是$' \t\n' ,并且可以使用打印cat -etv <<<"$IFS"

括在括号中的脚本启动子shell,这样只有通过括号定制受影响范围内的命令IFS值。



Answer 2:

cat listOfFiles.txt |tr '\n' '\0' |xargs -0 grep -li 'regex'

在xargs的-0选项告诉xargs的使用空字符,而不是空白的文件名终止。 tr命令输入的换行符转换成一个空字符。

这符合一些grep不会被调用多次OP的要求。 这一直是我的经验,对于大量文件的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选项的问题; 但是,很可能成为别人的一个问题。



Answer 3:

这工作:

while read file; do grep -li dtw "$file"; done < listOfFiles.txt


Answer 4:

虽然它可能均优于,这是我最喜欢的解决方案:

grep -i 'regex' $(cat listOfFiles.txt | sed -e "s/ /?/g")


Answer 5:

请注意,如果你以某种方式结束了其拥有的Windows行尾文件列表, \r\n ,票据概上述有关输入文件分隔符$IFS (和报价参数)将工作; 所以请确保行结束正确\n (我用scite显示行尾,并轻松地将它们从一种模式改变为其他)。

此外cat管道进入while file read ...似乎工作(显然没有必要设置分隔符):

cat <(echo -e "AA AA\nBB BB") | while read file; do echo $file; done

...虽然对我来说是一个“grep的”通过文件名中使用空格的目录更相关:

grep -rlI 'search' "My Dir"/ | while read file; do echo $file; grep 'search\|else' "$ix"; done


Answer 6:

与击4,也可以使用内置的映射文件函数来设置一个数组包含每一行,并重复这个阵列上:

$ 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


文章来源: Bash and filenames with spaces