【问题标题】:Using space-separated arguments from a field in a tab-separated file使用制表符分隔文件中字段的空格分隔参数
【发布时间】:2021-09-03 21:20:57
【问题描述】:

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

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)),甚至转义变量内的空格 (effects="delay\ 5")。似乎没有任何效果,一切都会产生错误。为什么一个命令有效而另一个无效,我缺少什么以及如何解决?

【问题讨论】:

  • 我试过这个,这就是为什么它很奇怪。 sox ./input.wav ./output.wav "$effects"sox ./input.wav ./output.wav '$effects' 都会产生同样的问题。
  • 对,使用数组effects=(delay 5) 然后使用"${effects[@]}"
  • 这些字符只是向您展示delay 5 只是一个参数的外壳。不是你不想要的东西被添加了;它是调试输出,可让您区分单参数和多参数。
  • 如果您希望您的IFS 更改为仅修改read,然后将其放在您的while read 行中,如while IFS=$'\t' read -r file effects text; do ...,不要在其他任何地方更改IFS -- 如果您这样做,则更改的范围为read
  • (不,IFS 不仅改变了 read;它还改变了 all 未加引号的扩展的工作方式,因此修改了 $effects 的含义;它没有t 更改 "$effects",但引号强制执行的行为是您不想要的行为)。

标签: bash shell sox


【解决方案1】:

IFS 不仅改变了read 的行为;它还改变了不带引号的扩展的行为。

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

因此,如果您希望delay5 之间的空格用于分词,您需要在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[@]}" 将数组中的每个项目扩展为一个单独的单词。


相比之下,如果您需要在effects 中允许引号/转义/等,请参阅Reading quoted/escaped arguments correctly from a string

【讨论】:

  • 谢谢,这就解释了。我在使用 read 命令之前阅读了它,文章说如果我想更改它使用的分隔符,我可以修改 IFS 变量,但他们没有提到它会改变任何其他内容。我正在尝试之前评论中建议的行:while IFS=$'\t' read -r file effects text; do。出于某种原因,这似乎破坏了循环内的 if 语句,对此进行了调查,但看起来它可能已经解决了它。
  • 我很想知道循环中到底有什么行为不端——如果你使用不带引号的扩展并假设你的扩展不会被分词,你可能需要更多的引号陷入不止一个论点。考虑通过shellcheck.net 运行您的代码并查看它发现了什么。
  • @MirceaKitsune, ...例如,如果您有if [ $key = $value ],则更安全地写为if [ "$key" = "$value" ],这样可以保证keyvalue 都将扩展为一个每个单词(因为如果多或少,结果可能不再是[ 命令的有效语法)。
  • 修复了最后一个问题:if [ ! -z $text ] 必须替换为 if [ -n "$text" ]。在 while 循环内设置 IFS 而不是之前的全局设置后,它现在可以正常工作了!感谢您的解决方案。
  • 太棒了! [ ! -z "$text" ] 也应该可以,但[ -n "$text" ] 肯定更清晰。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-02-24
  • 2019-11-24
  • 2018-01-04
  • 1970-01-01
  • 2017-08-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多