处理从find命令通过管道传输的文件名


10

我是Bash的新手,正在尝试做一些表面上看起来很简单的事情-在目录层次结构上运行find以获取所有* .wma文件,将输出管道传输到命令,然后将它们转换为mp3并将转换后的文件另存为.mp3。我的想法是该命令应如下所示(我不再使用音频转换命令,而是使用echo作为示例):

$ find ./ -name '*.wma' -type f -print0 | xargs -0 -I f echo ${f%.*}.mp3

据我了解,-print0 arg将允许我处理具有空格的文件名(其中许多文件是音乐文件)。然后,我期待(由于xargs的原因)find中的每个文件路径都在f中捕获,并且使用子字符串match / delete从字符串末尾开始,我应该用mp3回显原始文件路径扩展名而不是wma。但是,我看到的不是这个结果,而是:

*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
...

所以我的问题(除了特定的“我在这里做错了什么”之外)是吗?在字符串操作操作中,是否需要对管道操作结果所产生的值与变量赋值所导致的值进行不同的处理? ?


1
没有理由使用xargsfind。它带有一个-exec选项。您可以仅将要使用的命令添加到问题中,然后有人可以向您显示正确的find命令吗?
ixtmixilix

是的(如下文所述),只要我仍然可以对find命令的每个结果(例如,{}成员)进行字符串操作
Howard Dierking 2013年

实际上有些情况xargs比情况更合适exec。有关示例,请参见此stackpost stackoverflow.com/questions/896808/find-exec-cmd-vs-xargs
d34dh0r53 2013年

@ d34dh0r53什么情况?您链接到的线程没有指向任何线程。
吉尔(Gilles)'所以

Answers:


8

正如已经确定的其他答案一样,${f%.*}shell在运行xargs命令之前对其进行了扩展。您需要为每个文件名进行一次扩展,并将shell变量f设置为文件名(传递-I f不会这样做:xargs没有shell变量的概念,它会f在命令中查找字符串,因此如果需要使用,例如xargs -I e echo …它会执行类似的命令./somedir/somefile.wmacho .mp3

继续使用这种方法,告诉xargs调用一个可以执行扩展的外壳程序。更好地说find,这xargs是一个主要已过时的工具,并且很难正确使用,因为现代版本的find具有可以完成相同任务(甚至更多)且管道困难更少的构造。相反find … -print0 | xargs -0 command …,运行find … -exec command … {} +

find . -name '*.wma' -type f -exec sh -c 'for f; do echo "${f%.*}.mp3"; done' _ {} +

该参数_$0在壳; 文件名作为for f; do …循环遍历的位置参数传递。此命令的一个简单版本为每个文件执行一个单独的shell,这等效但稍慢一些:

find . -name '*.wma' -type f -exec sh -c 'echo "${0%.*}.mp3"' {} \;

find假设您正在运行相当新的Shell(ksh93,bash≥4.0或zsh),则实际上不需要在这里使用。在bash,把shopt -s globstar你的.bashrc激活**/在子目录水珠图案递归(在ksh中,该公司set -o globstar)。那你就可以跑

for f in **/*.wma; do
  echo "${f%.*}.mp3"
done

(如果您有名为的目录*.wma,请[ -f "$f" ] || continue在循环的开头添加。)


很棒的解释,也为我指明了我什至没有意识到的方向(升级bash shell以获取globstar功能)-最后,递归glob是使命令保持简单并完成我尝试做的所有事情的解决方案。谢谢!
Howard Dierking

6

如果对$ {f%。*}。mp3的解决方案评估是在要调用整个命令的shell中完成的,而不是在xargs派生的shell中完成的。而且在您的shell中没有f变量,它由一个空字符串代替。

使用带有-I f的xargs的解决方案:

% cat 1.sh 
#!/bin/sh
f=$1
echo ${f%.*}.mp3
% /bin/ls -1
1.sh
aaa.wma
bbbb.wma
ccccc.wma
% find ./ -name '*.wma' -type f -print0 | xargs -0 -I f ./1.sh f
./aaa.mp3
./ccccc.mp3
./bbbb.mp3

但是我会这样:

% find ./ -name '*.wma' -type f | sed 's,\.wma$,.mp3,'
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.