使用Bash以特定扩展的两个变量递归循环

Loop recursively with two variables for a specific extension with Bash

我正在尝试使用AtomicParsley将唱片集艺术添加到M4A文件中。以下内容适用于给定文件夹(有多个M4A文件,但只有一个cover.jpg):

1
for f in *.m4a; do AtomicParsley"$f" --artwork cover.jpg --overWrite; done

由于我想递归地执行此操作(每个文件夹有一个唯一的cover.jpg和多个m4a文件),并且所有子文件夹都具有相同的图像名称(即cover.jpg,尽管它们不同),所以我尝试了下面的操作,但它从第一个子文件夹中选择cover.jpg并应用于所有其他子文件夹(这是错误的,因为每个子文件夹都有自己的C超过JPG

1
2
3
4
5
for f in **/*.m4a; do
  for j in **/cover.jpg; do
    AtomicParsley"$f" --artwork $j --overWrite
  done
done


更容易想到的是找到每个包含cover.jpg的目录,然后查找该目录中的所有视频文件。

1
2
3
4
5
6
7
8
shopt -s globstar
shopt -s nullglob
for c in **/cover.jpg; do
    d=${c%/cover.jpg}
    for f in"$d"/*.m4a; do
        AtomicParsley"$f" --artwork"$c" --overWrite
    done
done


试试这个:

1
2
3
for file in **/*.m4a; do
    AtomicParsley"$file" --artwork"$(dirname"$file")/cover.jpg" --overWrite
done


试试这个:

1
2
3
4
5
main_dir="/path/to/dir"
while IFS= read -r -d '' m4a_file
do
  AtomicParsley"$m4a_file" --artwork"${m4a_file%/*}/cover.jpg" --overWrite
done < <(find"$main_dir" -type f -name"*.m4a" -print0)

这也是可能的:

1
2
3
4
5
6
7
8
shopt -s globstar
shopt -s nullglob
main_dir="/path/to/dir"
cd"$main_dir"
for m4a_file in **/*.m4a
do
  AtomicParsley"$m4a_file" --artwork"${m4a_file%/*}/cover.jpg" --overWrite
done

globstarshell选项允许**递归全局化(即输入子目录、子目录的子目录等)。

默认行为或全局化是,如果没有匹配项,则全局模式将扩展到自身。当在for循环中发生这种情况时,必须处理不存在的文件情况,因为失败的glob将导致循环体以变量中无效的文件名执行一次。nullglob选项改变了默认行为,因此失败的glob将扩展为空。在for循环的情况下,该循环根本不会执行。


对于更复杂的解决方案(有点过分杀伤力),允许您添加更多功能并使用相同的例程稍后执行不同的处理,您可以这样做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
parsley_file()
{
    AtomicParsley"$1" --artwork cover.jpg --overWrite
}
doforeach_arg()
{
    local callback="$1"
    shift
    local arg
    for arg in"${@}"; do"${callback}""${arg}"; done
}
parsley_dir()
( # use '(',')' for subshell so shopt only affects this function
    local dir="${1}"
    shopt -s nullglob
    doforeach_arg parsley_file"${dir}"/*.m4a
)

find_dirs()
{
  find"${1}" -type d | sort
}
doforeach_line()
{
  local callback="${1}"
  local line
  while read line; do"${callback}""${line}"; done
}
parsley_dirs()
{
    find_dirs"${1}" | doforeach_line parsley_dir
}

parsley_dirs"."

否则,如果您想这样做,那么对于几乎一个行程序来说就更简单了:

1
2
shopt -s nullglob
find"$PWD" - type d | while read dir ; do cd"$dir" && [[ -f cover.jpg ]] && ( for f in *.m4a ; do AtomicParsley"$f" -cover cover.jpg ; done ) ; done