如何在bash脚本中递归地执行foreach*.mp3文件?

Cha*_*lie 10 linux bash mp3 for-loop

以下在当前文件夹中工作正常,但我希望它也扫描子文件夹.

for file in *.mp3

do

echo $file

done
Run Code Online (Sandbox Code Playgroud)

Dav*_* W. 8

这些答案中有太多使用shell扩展来存储查找结果.这不是你应该轻易做的事情.

假设我有30,000首歌曲,这些歌曲的标题平均大约30个字符.让我们暂时不进入白空间问题.

我的发现将返回超过1,000,000个字符,而且我的命令行缓冲区很可能不是那么大.如果我做了这样的事情:

for file in $(find -name "*.mp3")
do
    echo "some sort of processing"
done
Run Code Online (Sandbox Code Playgroud)

问题(除了文件名中的空白区域)是你的命令行缓冲区将简单地从溢出中删除溢出find.它甚至可能完全无声地失败.

这就是xargs创建命令的原因.它确保命令行缓冲区永远不会溢出.它将xargs根据需要执行命令以保护命令行缓冲区:

$ find . -name "*.mp3" | xargs ...
Run Code Online (Sandbox Code Playgroud)

当然,使用xargs这种方式仍然会占用空白区域,但是现代实现xargs并且find有办法处理这个问题:

$ find . -name "*.mp3 -print0 | xargs --null ...
Run Code Online (Sandbox Code Playgroud)

如果您可以保证文件名中没有选项卡或\n(或双空格),那么将find放入while循环更好:

find . -name "*.mp3" | while read file
do
Run Code Online (Sandbox Code Playgroud)

管道将文件发送到while read命令行缓冲区已满之前.更好的是,read file读取整行并将该行中的所有项目放入$file.它并不完美,因为read仍会打破白色空间,因此文件名如下:

I will be in \n your heart in two lines.mp3
I   love song names with     multiple spaces.mp3
I \t have \t a \t thing \t for \t tabs.mp3.
Run Code Online (Sandbox Code Playgroud)

仍然会失败.该$file变量aill认为他们是:

I will be in 
your heart in two lines.mp3
I love song names with multiple spaces.mp3
I have a thing for tabs.mp3.
Run Code Online (Sandbox Code Playgroud)

为了解决这个问题,你必须find ... -print0使用null作为输入分隔符.然后更改IFS为使用null,或使用-d\0BASH shell中while read语句中的参数.

  • +1.应该有一个巨大的红色"请在你进行之前阅读"以防万一有人输入一个代码片段,试图解析指向正确回答问题的"ls".似乎这类问题的问题数量如此巨大,以至于它实际上会节省OP以及有用的灵魂.考虑到这里出现这种问题的错误答案,哦,男孩...... (2认同)

drq*_*ver 5

有很多方法可以给这只猫上皮.我会自己调用find命令:

for file in $(find . -name '*.mp3') do
  echo $file
  TITLE=$(id3info "$file" | grep '^=== TIT2' | sed -e 's/.*: //g')
  ARTIST=$(id3info "$file" | grep '^=== TPE1' | sed -e 's/.*: //g')
  echo "$ARTIST - $TITLE"
done
Run Code Online (Sandbox Code Playgroud)

如果文件名中有空格,则最好使用该-print0选项进行查找; 一种可能的方式是这样的:

find . -name '*.mp3' -print0 | while read -d $'\0' file
do
  echo $file
  TITLE=$(id3info "$file" | grep '^=== TIT2' | sed -e 's/.*: //g')
  ARTIST=$(id3info "$file" | grep '^=== TPE1' | sed -e 's/.*: //g')
  echo "$ARTIST - $TITLE"
done
Run Code Online (Sandbox Code Playgroud)

或者你可以保存和恢复IFS.感谢David W.的评论,特别是指出while循环版本还具有正确处理大量文件的好处,而第一个将a扩展$(find)为for循环的版本将无法在某些方面工作,因为shell扩展有限制.

  • @drquicksilver你的第二种选择是最好的方法. (3认同)

use*_*001 5

这适用于大多数文件名(包括空格),但不适用于换行符、制表符或双空格。

find . -type f -name '*.mp3' | while read i; do
   echo "$i"
done
Run Code Online (Sandbox Code Playgroud)

这适用于所有文件名。

find . -type f -name '*.mp3' -print0 | while IFS= read -r -d '' i; do
   echo "$i"
done
Run Code Online (Sandbox Code Playgroud)

但是如果您只想运行一个命令,您可以使用xargs示例:

find . -type f -name '*.mp3' -print0 | xargs -0 -l echo
Run Code Online (Sandbox Code Playgroud)


Jos*_*osh -1

听起来您正在寻找 find 命令。我还没有测试过这个,但有一些类似的事情:

files=(`find . -name *.mp3`)
for file in "${files[@]}"; do
    echo $file TITLE="id3info "$file" | grep '^=== TIT2' | sed -e 's/.*: //g'" ARTIST="id3info "$file" | grep '^=== TPE1' | sed -e 's/.*: //g'"
done
Run Code Online (Sandbox Code Playgroud)

编辑:使用数组使命令对于名称中带有空格的文件是安全的。