文件名中的空格问题
我想在文件列表上重复做一些事情。 问题中的文件名称中包含空格:
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
可以代表gzip
, gpg
或rm
。
来自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 -r
的while
循环,这样在每次迭代时, 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