Bash查找筛选器和复制-空格有问题

Bash查找筛选器和复制-空格有问题,bash,find,Bash,Find,因此,在进行了大量搜索并试图解释他人对我需求的问题和答案后,我决定自问自答 我正在尝试获取一个充满图像的目录结构,并将所有图像(无论扩展名如何)放在一个文件夹中。除此之外,我希望能够在这个过程中删除与某些文件名匹配的图像。我有一个find命令,可以为我输出所有文件路径 find -type f -exec file -i -- {} + | grep -i image | sed 's/\:.*//' 但是,如果我试图用它来复制文件,我会遇到文件名中空格的问题 cp `find -type f

因此,在进行了大量搜索并试图解释他人对我需求的问题和答案后,我决定自问自答

我正在尝试获取一个充满图像的目录结构,并将所有图像(无论扩展名如何)放在一个文件夹中。除此之外,我希望能够在这个过程中删除与某些文件名匹配的图像。我有一个find命令,可以为我输出所有文件路径

find -type f -exec file -i -- {} + | grep -i image | sed 's/\:.*//'
但是,如果我试图用它来复制文件,我会遇到文件名中空格的问题

cp `find -type f -exec file -i -- {} + | grep -i image | sed 's/\:.*//'` out/

我做错了什么,还有更好的方法吗?

如果没有任何文件名包含任何换行符,那么
find
命令可以正常工作。在广泛的限制范围内,
grep
命令在相同的情况下工作正常。只要文件名中没有冒号,
sed
命令就可以正常工作。但是,由于名称中有空格,因此使用
$(…)
(命令替换,也用反勾号
`…`
)是一场灾难。不幸的是,
xargs
并不容易成为解决方案的一部分;默认情况下,它在空格上拆分。因为您必须在中间运行<代码>文件>代码> <代码> GRP>代码>,您不能轻易使用<代码> -PROT00/COD>选项(GNU)<代码>查找< /代码>和<代码> -0 < /Cord>选项到(GNU)<代码> XARGS

在某些方面,它是粗糙的,但在许多方面,如果您编写一个可由
find
调用的可执行shell脚本,它是最简单的:

#!/bin/bash

for file in "$@"
do
    if file -i -- "$file" | grep -i -q "$file:.*image"
    then cp "$file" out/
    fi
done
这有点痛苦,因为它分别为每个名称调用
file
grep
,但它是可靠的。如果文件名包含换行符,
file
命令甚至是安全的;
grep
可能不是

如果该脚本名为“copyimage.sh”,则
find
命令变为:

find . -type f -exec ./copyimage.sh {} +

而且,根据
grep
命令的编写方式,
copyimage.sh
文件不会被复制,即使其名称包含神奇的单词“image”。

将find命令的结果通过管道传输到

xargs -l --replace cp "{}" out/
我在Ubuntu 10.04上的工作原理示例:

atomic@atomic-desktop:~/temp$ ls
img.png  img space.png
atomic@atomic-desktop:~/temp$ mkdir out
atomic@atomic-desktop:~/temp$ find -type f -exec file -i \{\} \; | grep -i image | sed 's/\:.*//' | xargs -l --replace cp -v "{}" out/
`./img.png' -> `out/img.png'
`./img space.png' -> `out/img space.png'
atomic@atomic-desktop:~/temp$ ls out
img.png  img space.png
atomic@atomic-desktop:~/temp$ 

但要注意,如果文件名中有换行符,则它将不起作用:

find . -type f -exec file -i -- {} + |
awk -vFS=: -vOFS=: '$NF ~ /image/{NF--;printf "%s\0", $0}' |
xargs -0 cp -t out/


(根据Jonathan Leffler的回答以及随后与他和@devnull的评论讨论。)

大概您没有包含换行符的文件名。另外,鉴于您使用的
grep
命令,您没有任何文件,例如
windimage.o
,它是一个对象文件(不是图像文件),即使文件名包含“image”。我没有,但如果我遇到这样的文件,我愿意接受改进建议。你想删除什么样的文件名?取决于具体情况。我将此用作脚本的一部分,以将nook的存档文件处理为正确的格式,但稍后我将添加该部分。不幸的是,
xargs
命令将其输入行拆分为空格,因此它会将文件名拆分。我想我一定是遗漏了什么。我的xargs似乎没有用空格分隔文件名。我将用我看到的编辑我的答案。这一个经过一点调整后工作得很好。最后一个命令是
find-typef-print0 | xargs-0 file-i | grep-i“image/”| sed's/\:.*/'| xargs-l--replace cp\{\}out/
,尽管这可能不是最有效的处理方法。(并且可能有一些不必要的部分),它似乎完全按照我的预期工作是的,显然“find-print0 | xargs-0”是解决这个问题的安全方法。虽然看起来你的第二个xargs可能仍然会遇到@JonathanLeffler现在使用你的版本时遇到的相同问题,但是看起来更干净了,只是更改了grep以避免意外地抓取名为
find-type f-exec file-i\{}的带有“image”的文件grep-i image | sed's/\:.*/'| xargs-l--替换cp-v“{}”out/
edit:我应该使用哪个?如果文件名包含非自匹配的正则表达式元字符,grep将失败。(即,
可以,但
*
不行。)对于“$@”中的文件,更好的方法是
;do file-i--“$file”;完成| awk-F:'$2~/image/{printf“%s\0”,$1}'| xargs-0cp-t out/
+1。特别是为了指出
grep
问题(正如最初编写的那样)@rici我不确定管道到
awk
是否是个好主意。想象一下,一个名为
foo:bar.jpg的文件可以帮助我如何使用它吗?shell脚本对我也不起作用(可能是在一些文件名中找到的[]和uu?)@rici:很好-我没有在文件名中添加关于regex字符的警告。我同意
awk
可能有用,但我不确定你写的是否足够;如果文件名包含冒号,则单词“image”可能不是行中的第二个单词。这东西太疼了。
cp
(和
mv
)的(GNU)扩展允许您通过
-t target
指定目录,这是很有用的。Hmmm。。也许你想把后半段写进剧本或别的什么。这可能会混淆
-exec
@devnull:为什么
awk
只需调用一次,它一次只接受一行输入。您需要一个
\在第一个管道之前,它应该是好的(实际上,很棒)@事实上,我用的是+;甚至更好。在评论后立即看到+1.