1

我正在尝试编写一个对音乐文件进行一些处理的 bash 脚本。这是到目前为止的脚本:

#!/bin/bash

SAVEIFS=$IFS
IFS=printf"\n\0"

find `pwd` -iname "*.mp3" -o -iname "*.flac" | while read f
do
    echo "$f"
    $arr=($(f))
    exiftool "${arr[@]}"
done

IFS=$SAVEIFS

这失败了:

[johnd:/tmp/tunes] 2 $ ./test.sh 
./test.sh: line 9: syntax error near unexpected token `$(f)'
./test.sh: line 9: `    $arr=($(f))'
[johnd:/tmp/tunes] 2 $ 

我尝试了许多不同的咒语,但都没有奏效。底线是我正在尝试调用命令exiftool,并且该命令的参数之一是可能包含空格的文件名。上面我试图将文件名分配$f给一个数组并将该数组传递给exiftool,但是我在构建数组时遇到了麻烦。

直接的问题是,我如何构造这个数组?但更深层次的问题是,如何从 bash 脚本中调用带有可能包含空格的参数的外部命令?

4

2 回答 2

5

您实际上确实拥有正确的调用可能包含空格的参数语法(program "${args[@]}")。但是,有几个问题。

首先,$(foo) 执行一个命令。如果您想要变量的值,请使用$foo${foo}

其次,如果你想在数组上附加一些东西,语法是array+=(value)(或者,如果这不起作用,array=("${array[@]}" value))。

第三,\0 尽可能用 分隔文件名。换行符都很好,但文件名可以包含换行符。

第四,read采用 switch -d,可以与空字符串一起使用''来指定\0分隔符。这消除了对IFS.

第五,当管道进入while循环时要小心——这会导致循环在子shell中执行,防止其中的变量分配在外部生效。但是,有一种方法可以解决这个问题 -command | while ... done使用流程替换 ( ) 代替管道 ( while ... done < <(command))。

第六,注意你的进程替换 - 没有必要在$(pwd)何时将其用作命令的参数.。(或者如果你真的必须有完整的路径,请尝试引用pwd调用。)

tl;博士

修改后的脚本:

while read -r -d '' f; do
    echo "$f" # For debugging?
    arr+=("$f")
done < <(find . -iname "*.mp3" -o -iname "*.flac" -print0)
exiftool "${arr[@]}"

其他方式

利用find的全部功能:

find . -iname "*.mp3" -o -iname "*.flac" -exec exiftool {} +
# Much shorter!

编辑 1

所以你需要保存输出exiftool,操作它,然后复制东西?试试这个:

while read -r -d '' f; do
    echo "$f" # For debugging?
    arr+=("$f")
done < <(find . -iname "*.mp3" -o -iname "*.flac" -print0)
# Warning: somewhat misleading syntax highlighting ahead
newfilename="$(exiftool "${arr[@]}")"
newfilename="$(manipulate "$newfilename")"
cp -- "$some_old_filename" "$newfilename"

您可能需要更改最后一点-我从未使用过exiftool,所以我不确切知道您在追求什么(或如何去做),但这应该是一个开始。

于 2013-05-03T12:24:46.190 回答
2

你可以用 bash 做到这一点:

shopt -s globstar nullglob
a=( **/*.{mp3,flac} )
exiftool "${a[@]}"

这可能也有效:exiftool **/*.{mp3,flac}

于 2013-05-03T13:37:07.790 回答