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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15930407/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 05:09:32  来源:igfitidea点击:

How can I do foreach *.mp3 file recursively in a bash script?

linuxbashfor-loopmp3

提问by Charlie

The following works fine within the current folder, but I would like it to scan sub folders as well.

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

for file in *.mp3

do

echo $file

done

回答by David W.

Too many of these answers use shell expansion to store the results of a find. This is not something you should do lightly.

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

Let's say I have 30,000 songs, and the titles of these songs average around 30 characters. Let's not even get into the white spaceproblem for now.

假设我有 30,000 首歌曲,这些歌曲的标题平均约为 30 个字符。现在我们甚至不讨论空白问题。

My find will return over 1,000,000 characters, and it's very likely that my command line buffer isn't that big. If I did something like this:

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

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

The problem (besides the white space in file names) is that your command line buffer will simply drop off the overflow from the find. It may even fail absolutely silently.

问题(除了文件名中的空格)是您的命令行缓冲区只会从find. 它甚至可能完全无声地失败。

This is why the xargscommand was created. It makes sure that the command line buffer never overflows. It will execute the command following the xargsas many times as necessary to protect the command line buffer:

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

$ find . -name "*.mp3" | xargs ...

Of course, using xargsthis way will still choke on white space, but modern implementations of xargsand findhave a way of handling this issue:

当然,使用xargs这种方式仍然会在空白处窒息,但是现代实现xargsfind有一种处理这个问题的方法:

$ find . -name "*.mp3 -print0 | xargs --null ...

If you can guarantee that file names won't have tabs or \n(or double spaces) in them, piping a find into a while loop is better:

如果您可以保证文件名中没有制表符或\n(或双空格),则将 find 管道化到 while 循环中会更好:

find . -name "*.mp3" | while read file
do

The pipeline will send the files to the while readbefore the command line buffer is full. Even better, the read filereads in an entire line and will put all items found in that line into $file. It isn't perfect because the readstill breaks on white space so file names such as:

管道将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.

Will still fail. The $filevariable aill see them as:

还是会失败。该$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.

In order to get around thisproblem, you have to use find ... -print0to use nulls as input dividers. Then either change IFSto use nulls, or use the -d\0parameter in the while read statement in BASH shell.

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

回答by drquicksilver

There are lots of ways to skin this cat. I would use a call to the find command myself:

有很多方法可以给这只猫剥皮。我会自己调用 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

If you have spaces in your filenames then it's best to use the -print0option to find; one possible way is this:

如果文件名中有空格,那么最好使用-print0查找选项;一种可能的方法是:

find . -name '*.mp3' -print0 | while read -d $'
find . -type f -name '*.mp3' | while read i; do
   echo "$i"
done
' 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

alternatively you can save and restore IFS. Thanks to David W.'s comments and, in particular, for pointing out that the whileloop version also has the benefit that it will handle very large numbers of files correctly, whereas the first version which expands a $(find)into a for-loop will fail to work at some point as shell expansion has limits.

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

回答by user000001

This works with most filenames (including spaces) but not newlines, tabs or double spaces.

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

find . -type f -name '*.mp3' -print0 | while IFS= read -r -d '' i; do
   echo "$i"
done

This works with all filenames.

这适用于所有文件名。

find . -type f -name '*.mp3' -print0 | xargs -0 -l echo

But if you only want to run one command you can use xargsexample:

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

find . -name *.mp3 -exec echo {} \;

回答by hzrari

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

The string {}is replaced by the current file name being processed everywhere it occurs in the arguments to the command, not just in arguments where it is alone, as in some versions of find.

该字符串{}被当前正在处理的文件名替换,它出现在命令的参数中的任何地方,而不仅仅是在它单独出现的参数中,就像在某些版本的 find 中一样。

Please check the find man for further info http://unixhelp.ed.ac.uk/CGI/man-cgi?find

请检查查找人以获取更多信息http://unixhelp.ed.ac.uk/CGI/man-cgi?find

回答by Josh

Sounds like you are looking for the find command. I haven't tested this, but something along these lines:

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

##代码##

EDIT: using the array makes the command safe for files with spaces in their names.

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