文件名中的空格问题

我想在文件列表上重复做一些事情。 问题中的文件名称中包含空格:

david@david: ls -l total 32 -rw-rw-r-- 1 david david 13 Mai 8 11:55 haha -rw-rw-r-- 1 david david 0 Mai 8 11:55 haha~ -rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (3rd copy) -rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (4th copy) -rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (5th copy) -rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (6th copy) -rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (7th copy) -rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (another copy) -rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (copy) 

现在我想统计每个文件:

 david@david: echo ' for file in $(ls) do stat $file done' | bash 

(我使用echo和pipe来编写多行命令。)

当我这样做时,它在那些名称中没有任何空格的文件上正常工作。 但其他人……

 stat: cannot stat '(another': No such file or directory stat: cannot stat 'copy)': No such file or directory 

$(ls)更改$(ls) "$(ls)"或将$file更改为"$file"不起作用。 我能做什么?

编辑:

 echo ' for files in * do stat "$files" done' | bash 

诀窍! 由于我是bash的新手,我希望尽可能保持简单 – 所以没有尝试逃避空间,或使用xargs或带有read -r的解决方案,尽管它们解决了问题。

正如一些人所说的那样:是的,用这个代替stat *是很奇怪的。 但我只是想找到一种通用的方法,使用for循环在bash中对一堆文件名应用相同的命令。 因此stat可以代表gzipgpgrm

来自echo '的多引号使事情复杂化。

你可以使用:

 for f in *; do stat -- "$f"; done 

但是也

 stat -- * 

…如果你想收集文件然后应用命令(为什么?)你可以去(但要小心包含新行的文件……(1))

 for f in *; do echo "$f"; done | xargs stat -- 

…如果你也想要隐藏文件, 只需使用* .*作为模式,但请记住...将在集合中 。

顺便说一句, 你不应该解析ls输出 。


(1)但是如果你有带换行符的文件名,你有点值得…… 😉

在旁注:您可以通过添加空格后跟反斜杠并在每次要开始写入新行时按Enter键而不是通过使用echo [...] | bash分支多个进程来在多行上分割长/复杂命令echo [...] | bash echo [...] | bash ; 你也应该用双引号括起$file ,以防止在包含空格的文件名的情况下stat被破坏:

 for file in $(ls); \ do \ stat "$file"; \ done 

问题是$(ls)扩展为包含空格的文件名列表, "$(ls)"也会出现同样的情况。

即使解决了这个问题,这个方法仍然会破坏包含反斜杠的文件名和包含换行符的文件名(如terdon所指出的)。

这两个问题的解决方案是将find的输出传递给运行read -rwhile循环,这样在每次迭代时, read -r都会将一行find的输出存储到$file

 find . -maxdepth 1 -type f | while read -r file; do \ stat "$file"; \ done 

使用旧的find ,使用隐藏文件,换行符和空格。

 find . -print0 | xargs -I {} -0 stat {} 

或任何其他而不是stat

 find . -print0 | xargs -I {} -0 file {} find . -print0 | xargs -I {} -0 cat {} 

作为R家伙,我已经在R中找到了一个解决方法:

 filenames <- dir(); # reads file names into an array. # It works also recursively # with dir(recursive=TRUE) for (i in 1:length(filenames)) { system( # calls a system function paste( # join stat and the file name "stat", filenames[i] ) ) } 

我知道,这很疯狂。 我希望ls的输出更容易解析... R可以处理空格,因为dir()返回一个带引号的字符值。 引号之间的任何内容都是带空格的有效文件名。

我在for循环中遇到了其他空白问题的实例,因此我通常使用以下(imo more robust)命令。 它也非常适合管道。

 $ ls | while read line; do stat "$line"; done; 

您可以将其与grep结合使用或使用find代替:

 $ find ./ -maxdepth 2 | grep '^\./[/az]+$' | while read line; do stat "$line"; done; 

这个答案将解决解析ls并处理退格和新行的问题

试试这个,它将使用内部字段分隔符IFS解决您的问题。

 IFS="\n" for f in $(ls); do stat "$f"; done 

但是你也可以轻松地解决它,而无需使用解析ls输出

 for f in *; do stat "$f"; done 

相反,您可以使用其他字符(如下划线)重命名文件替换空格,这样您就可以解决此问题:

为此,请轻松运行命令:

 for file in * ; do mv "$f" "${f// /_}" ; done