使用带有嵌入空格的参数(例如文件名)调用 bash 脚本中的命令
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16358580/
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
Calling commands in bash script with parameters which have embedded spaces (eg filenames)
提问by John Dibling
I am trying to write a bash script which does some processing on music files. Here is the script so far:
我正在尝试编写一个对音乐文件进行一些处理的 bash 脚本。这是到目前为止的脚本:
#!/bin/bash
SAVEIFS=$IFS
IFS=printf"\n[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 $
"
find `pwd` -iname "*.mp3" -o -iname "*.flac" | while read f
do
echo "$f"
$arr=($(f))
exiftool "${arr[@]}"
done
IFS=$SAVEIFS
This fails with:
这失败了:
while read -r -d '' f; do
echo "$f" # For debugging?
arr+=("$f")
done < <(find . -iname "*.mp3" -o -iname "*.flac" -print0)
exiftool "${arr[@]}"
I have tried many different incantations, none of which have worked. The bottom line is I'm trying to call a command exiftool, and one of the parameters of that command is a filename which may contain spaces. Above I'm trying to assign the filename $fto an array and pass that array to exiftool, but I'm having trouble with the construction of the array.
我试过很多不同的咒语,都没有奏效。最重要的是我正在尝试调用 command exiftool,该命令的参数之一是可能包含空格的文件名。上面我试图将文件名分配$f给一个数组并将该数组传递给exiftool,但是我在构建数组时遇到了麻烦。
Immediate question is, how do I construct this array? But the deeper question is how, from within a bash script, do I call an external command with parameters which may contain spaces?
直接的问题是,我如何构造这个数组?但更深层次的问题是,如何从 bash 脚本中调用带有可能包含空格的参数的外部命令?
回答by michaelb958--GoFundMonica
You actually did have the call-with-possibly-space-containing-arguments syntax right (program "${args[@]}"). There were several problems, though.
您实际上确实拥有正确的 call-with-possably-space-包含-arguments 语法 ( program "${args[@]}")。但是,有几个问题。
Firstly, $(foo)executes a command. If you want a variable's value, use $fooor ${foo}.
首先,$(foo)执行一个命令。如果您想要一个变量的值,请使用$foo或${foo}。
Secondly, if you want to append something onto an array, the syntax is array+=(value)(or, if that doesn't work, array=("${array[@]}" value)).
其次,如果您想将某些内容附加到数组中,则语法为array+=(value)(或者,如果这不起作用,则为array=("${array[@]}" value))。
Thirdly, pleaseseparate filenames with \0whenever possible. Newlines are all well and good, but filenames can contain newlines.
第三,请\0尽可能将文件名与. 换行符都很好,但文件名可以包含换行符。
Fourthly, readtakes the switch -d, which can be used with an empty string ''to specify \0as the delimiter. This eliminates the need to mess around with IFS.
第四,read采用 switch -d,它可以与空字符串''一起使用\0作为分隔符。这消除了对IFS.
Fifthly, be careful when piping into whileloops - this causes the loop to be executed in a subshell, preventing variable assignments inside it from taking effect outside. There is a way to get around this, however - instead of piping (command | while ... done), use process substitution (while ... done < <(command)).
第五,当管道进入while循环时要小心——这会导致循环在子外壳中执行,防止其内部的变量赋值在外部生效。但是,有一种方法可以解决此问题 -command | while ... done使用进程替换 ( while ... done < <(command))代替管道( )。
Sixthly, watch your process substitutions - there's no need to use $(pwd)as an argument to a command when .will do. (Or if you really must have full paths, try quoting the pwdcall.)
第六,注意你的进程替换 - 不需要将$(pwd)何时用作命令的参数.。(或者,如果您确实必须拥有完整路径,请尝试引用pwd调用。)
tl;dr
tl;博士
The script, revised:
修改后的脚本:
find . -iname "*.mp3" -o -iname "*.flac" -exec exiftool {} +
# Much shorter!
Another way
其它的办法
Leveraging find's full capabilities:
利用find的全部功能:
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"
Edit 1
编辑 1
So you need to save the output of exiftool, manipulate it, then copy stuff? Try this:
所以你需要保存 的输出exiftool,操作它,然后复制东西?尝试这个:
shopt -s globstar nullglob
a=( **/*.{mp3,flac} )
exiftool "${a[@]}"
You probably will need to change that last bit - I've never used exiftool, so I don't know precisely what you're after (or how to do it), but that should be a start.
您可能需要更改最后一点 - 我从未使用过exiftool,所以我不确切知道您在追求什么(或如何去做),但这应该是一个开始。
回答by glenn Hymanman
You can do this just with bash:
你可以只用 bash 做到这一点:
##代码##This probably works too: exiftool **/*.{mp3,flac}
这可能也有效: exiftool **/*.{mp3,flac}

