我想对文件列表重复执行某些操作。问题中的文件名称中有空格:
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 和管道来编写多行命令。)
当我这样做时,它可以正确地处理那些名称中没有空格的文件。但其他的...
stat: cannot stat ‘(another’: No such file or directory
stat: cannot stat ‘copy)’: No such file or directory
更改$(ls)
为"$(ls)"
或更改$file
为"$file"
不起作用。我该怎么办?
编辑:
echo '
for files in *
do
stat "$files"
done' | bash
成功了!由于我是 Bash 新手,所以我想让事情尽可能简单 - 因此不要试图转义空格,也不要使用xargs
或 解决方案read -r
,尽管它们可以解决问题。
正如一些人所问:是的,用这个代替stat *
很奇怪。但我只是想找到一种通用方法,使用 for 循环在 bash 中对一堆文件名应用相同的命令。所以stat
可以代表gzip
,gpg
或rm
。
答案1
来自的多次引用echo '
使事情变得复杂。
您可以使用:
for f in *; do stat -- "$f"; done
但是也
stat -- *
...如果您想收集文件然后应用命令(为什么?),您可以继续(但要小心包含新行的文件...(1))
for f in *; do echo "$f"; done | xargs stat --
...如果你还想要隐藏文件,只是用作* .*
模式,但要记住,.
并将..
在集合中。
作为旁白,你不应该解析ls
输出。
(1)但如果您的文件名带有换行符,那么您就值得拥有它…… ;-)
答案2
附注:您可以通过Enter在新行中添加空格和反斜杠并按 来将长/复杂的命令拆分为多行,而不是使用 分叉多个进程echo [...] | bash
;此外,您还应该将其括$file
在双引号中,以防止stat
在文件名包含空格时中断:
for file in $(ls); \
do \
stat "$file"; \
done
问题是$(ls)
扩展为包含空格的文件名列表,并且也会发生同样的情况"$(ls)"
。
即使解决了这个问题,这种方法仍然会在包含反斜杠的文件名和包含换行符的文件名上中断(正如 terdon 指出的那样)。
这两个问题的解决方案是将 的输出通过管道传输find
到while
运行的循环中read -r
,以便在每次迭代时,将的输出read -r
的一行存储到 中:find
$file
find . -maxdepth 1 -type f | while read -r file; do \
stat "$file"; \
done
答案3
使用旧的好方法find
,可以处理隐藏文件、换行符和空格。
find . -print0 | xargs -I {} -0 stat {}
或任何其他代替stat
find . -print0 | xargs -I {} -0 file {}
find . -print0 | xargs -I {} -0 cat {}
答案4
我在 for 循环中遇到过其他空格问题,因此我通常使用以下命令(我认为更可靠)。它也非常适合管道。
$ ls | while read line; do stat "$line"; done;
您可以将其与以下内容结合grep
使用或find
改用:
$ find ./ -maxdepth 2 | grep '^\./[/a-z]+$' | while read line; do stat "$line"; done;