使用具有嵌入空格的参数(例如文件名)在bash脚本中调用命令

时间:2013-05-03 12:07:17

标签: linux bash

我正在尝试编写一个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脚本中调用带有可能包含空格的参数的外部命令?

2 个答案:

答案 0 :(得分:5)

你实际上确实拥有call-with-possible-space-containing-arguments语法(program "${args[@]}")。但是有几个问题。

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

其次,如果要将某些内容附加到数组上,则语法为array+=(value)(或者,如果它不起作用,array=("${array[@]}" value))。

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

第四,read接受开关-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,所以我不确切知道您之后(或如何操作),但这应该是一个开始。

答案 1 :(得分:2)

你可以用bash做到这一点:

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

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