How*_*ing 10 shell bash find recursive filenames
我对 Bash 比较陌生,我正在尝试做一些表面上看起来很简单的事情 - 在目录层次结构上运行 find 以获取所有 *.wma 文件,将输出通过管道传输到一个命令,在那里我将它们转换为 mp3 和将转换后的文件另存为 .mp3。我的想法是该命令应该如下所示(我已经放弃了音频转换命令,而是使用 echo 进行说明):
$ find ./ -name '*.wma' -type f -print0 | xargs -0 -I f echo ${f%.*}.mp3
Run Code Online (Sandbox Code Playgroud)
据我了解, -print0 arg 将让我处理带有空格的文件名(其中许多都是音乐文件)。然后我期望(作为 xargs 的结果)在 f 中捕获来自 find 的每个文件路径,并且使用从字符串末尾匹配/删除的子字符串,我应该用 mp3 回显原始文件路径扩展名而不是 wma。但是,我看到的不是这个结果,而是以下内容:
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
...
Run Code Online (Sandbox Code Playgroud)
所以我的问题(除了特定的“我在这里做错了什么”),是这样 - 在字符串操作操作中需要对管道操作结果的值与变量赋值的结果进行不同的处理?
正如其他答案已经确定的那样,${f%.*}在运行xargs命令之前由 shell 展开。您需要对每个文件名进行一次扩展,并将 shell 变量f设置为文件名(传递-I f不会这样做:xargs没有 shell 变量的概念,它f在命令中查找字符串,因此如果您使用例如xargs -I e echo …它会执行类似的命令./somedir/somefile.wmacho .mp3)。
保持这种方法,告诉xargs调用可以执行扩展的 shell。更好,告诉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' _ {} +
Run Code Online (Sandbox Code Playgroud)
该参数_是$0在壳; 文件名作为for f; do …循环的位置参数传递。这个命令的一个更简单的版本为每个文件执行一个单独的 shell,它是等效的,但速度稍慢:
find . -name '*.wma' -type f -exec sh -c 'echo "${0%.*}.mp3"' {} \;
Run Code Online (Sandbox Code Playgroud)
您实际上不需要在find这里使用,假设您正在运行一个相当新的 shell(ksh93、bash ?4.0 或 zsh)。在 bash 中,shopt -s globstar输入您.bashrc以激活**/glob 模式以在子目录中递归(在 ksh 中,即set -o globstar)。然后你可以运行
for f in **/*.wma; do
echo "${f%.*}.mp3"
done
Run Code Online (Sandbox Code Playgroud)
(如果您有名为 的目录*.wma,请[ -f "$f" ] || continue在循环的开头添加。)
小智 6
如果您对 ${f%.*}.mp3 的解决方案评估是在您调用整个命令的 shell 中完成的,而不是在xargs分叉的 shell 中完成。在你的 shell 中没有f变量,它被一个空字符串替换。
使用xargs和-I f 的解决方案:
% 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
Run Code Online (Sandbox Code Playgroud)
但我会这样做:
% find ./ -name '*.wma' -type f | sed 's,\.wma$,.mp3,'
Run Code Online (Sandbox Code Playgroud)