使用制表符分隔文件中字段的 space 分隔参数

Using space-separated arguments from a field in a tab-separated file

我正在编写一个 shell 脚本,旨在使用 sox 命令编辑音频文件。我已经 运行 遇到一个奇怪的问题,我以前在 bash 脚本中从未遇到过:在 sox 中定义 space 分离效果时,该命令将在直接写入该效果时起作用,但不会当它存储在变量中时。这意味着以下工作正常并且没有任何问题:

sox ./test.in.wav ./test.out.wav delay 5

但由于某些原因,以下方法不起作用:

IFS='   ' # set IFS to only have a tab character because file is tab-separated
while read -r file effects text; do
  sox $file.in.wav $file.out.wav $effects
done <in.txt

...当其 in.txt 创建时:

printf '%s\t%s\t%s\n' "test" "delay 5" "other text here" >in.txt

错误表明这是导致它将输出文件视为另一个输入。

sox FAIL formats: can't open input file `./output.wav': No such file or directory

我尝试了所有我能想到的方法:使用引号 (sox "$file.in.wav" "$file.out.wav" "$effects"),内联变量回显 (sox $file.in.wav $file.out.wav $(echo $effects)),甚至转义变量内的 space ( effects="delay\ 5")。似乎什么都不起作用,一切都会产生错误。为什么一个命令有效而另一个无效,我缺少什么以及如何解决它?

IFS 不仅改变了 read 的行为;它还更改了未引用扩展的行为。

特别是,未加引号的扩展的内容在 IFS 中找到的字符上被拆分,然后拆分产生的每个元素被扩展为一个 glob。

因此,如果要将delay5之间的space用于分词,则需要有一个正则的space,而不仅仅是一个选项卡,在 IFS 中。如果您将 IFS 赋值移动到与 read 相同的 简单命令 的一部分,就像 IFS=$'\t' read -r file effects text; do 中一样,这将阻止它改变脚本其余部分的行为。


但是,使用不带引号的扩展来进行分词根本不是一个好习惯。请改用数组。您可以将 effects 字符串拆分为一个数组:

IFS=' ' read -r -a effects_arr <<<"$effects"

...然后 运行 sox "$file.in.wav" "$file.out.wav" "${effects_arr[@]}" 将数组中的每个项目展开为一个单独的词。


相比之下,如果您需要 quotes/escapes/etc 在 effects 中被允许,请参阅 Reading quoted/escaped arguments correctly from a string