从shell脚本(bash)的参数列表中删除最后一个参数

30
这个问题涉及一个在 automator osx 中运行的 bash 脚本。我使用 automator 动作从 Finder 获取和过滤一堆文件引用。然后我通过另一个 automator 动作将父文件夹的名称添加到该列表中。Automator 然后将这些参数传递给名为 "run shell script" 的动作。不确定 automator 如何调用脚本,但当使用 echo "$@" 命令回显时,参数列表看起来像这样:

/Volumes/G-Raid/Online/WAV_TEST/Testbok 50/01/01000 43-001.wav /Volumes/G-Raid/Online/WAV_TEST/Testbok 50/02/02000 43-002.wav /Volumes/G-Raid/Online/WAV_TEST/Testbok 50/03/03000 43-003.wav /Volumes/G-Raid/Online/WAV_TEST/Testbok 50

在这种情况下是三个文件和一个文件夹的路径。
在 shell 脚本中,我使用从 automator 传递的参数(列表中除了最后一个文件夹)来启动名为 ripcheckc* 的应用程序。
我使用以下命令删除最后一个参数:
_args=( "$@" )
unset _args[${#_args[@]}-1]

这是echo $_args的内容:

/Volumes/G-Raid/Online/WAV_TEST/Testbok 50/01/01000 43-001.wav /Volumes/G-Raid/Online/WAV_TEST/Testbok 50/02/02000 43-002.wav /Volumes/G-Raid/Online/WAV_TEST/Testbok 50/03/03000 43-003.wav

与之前相同,但不包含文件夹。

现在,如果我使用"$@"作为参数运行ripcheckc,它可以正常工作(但由于参数列表中的最后一个路径而在后面失败)。如果我使用${_args[@]},应用程序将悄无声息地中止。当我回显$@_args时,输出看起来相同,除了最后一个参数。

我的问题是 - $@和$_args之间有什么区别,使得第一个是有效输入,而第二个则无效?

*此应用程序是ripcheckc

希望我的问题有意义。

编辑:已解决。


我通过使用双引号解决了这个问题。像这样:ripcheckc "${_args[@]}"。 - Nordanfors
4个回答

59
我以前使用过这个 bash 一行命令
set -- "${@:1:$(($#-1))}"

它将参数列表设置为当前参数列表,去掉最后一个参数。


工作原理:

  • $# 是参数的数量
  • $((...)) 是算术表达式,所以 $(($#-1)) 表示比参数数量小 1。
  • ${variable:position:count} 是一个子字符串表达式:它从位置开始提取变量中的count个字符。 在特殊情况下,variable@,表示参数列表,它从列表的position处开始提取count参数。 这里,对于第一个参数,position1,而count是之前计算出的参数数量减一。
  • set -- arg1...argn 将参数列表设置为给定的参数

因此,最终结果是用新列表替换参数列表,其中新列表是原始列表除了最后一个参数之外的部分。


简单而优雅:我喜欢它。我使用了@chepner的建议的变体,以“删除”前两个参数,以便正确生成子进程:spawnProc "${@:3:$#}"。不可否认,这有点神秘,但这是由于BASH的根源。尽管如此,它很简洁而且简单。 - Andrew Falanga
我可以请求一个详细的解释吗? - Steven Lu
1
@StevenLu 我已经修改了答案并为您提供了解释。希望能对您有所帮助。 - starfry
太完美了! - Steven Lu
很好的解释,注意下面@uvsmtid提供了一种更紧凑的写法,即 "${@:1:${#}-1}" - mikemtnbikes
“特殊情况”适用于任何数组变量,而不仅仅是@ - undefined

14
假设你已经有一个数组,那么可以这样说:
unset "array[${#array[@]}-1]"
例如,如果您的脚本包含以下内容:
array=( "$@" )
unset "array[${#array[@]}-1]"    # Removes last element -- also see: help unset
for i in "${array[@]}"; do
  echo "$i"
done

使用以下命令调用:bash scriptname foo bar baz 会产生以下结果:

foo
bar

谢谢,这是一个更好的方法来摆脱最后一个参数。我的问题是,$@ 似乎与 $array 有所不同,使其成为我要传递给应用程序的有效输入。我只是想不出它是什么。 - Nordanfors
@user1047256 尝试将${array[@]}传递给您的应用程序。 - devnull
我尝试了你建议的方法,但仍然不行。顺便说一下,我把我的昵称改成了Nordanfors。 :-) - Nordanfors
2
@Nordanfors 始终 引用变量。 - devnull
1
下一个答案应该被接受,而不是这个。它有三倍的赞同票数。 - Noumenon
显示剩余4条评论

11

你也可以使用以下方法获取除最后一个参数外的所有参数

"${@:0:$#}"

说实在的,这有点不太靠谱,因为它似乎滥用了参数从1开始编号而不是0的事实。

更新:这仅适用于处理$@时存在漏洞(最晚在4.1.2中修复)。在版本3.2中有效。


11
代码 "${@:1:${#}-1}" 的作用是提取除了最后一个参数之外的所有参数,使得其与 "$@" 中的参数列表完全相同。 - uvsmtid
没错。不过有点不太清晰的是使用0作为第一个参数,以避免从第二个参数中减去1。 - chepner
实际上,索引0包括非参数 - 脚本路径(由shell自动提供的值)。您并没有避免减去1 - 最后一个参数仍然包括在内,您必须减去1。使用0只是向列表中添加了一个(可能不需要的)值。 - uvsmtid
1
似乎这在bash 3.2中起作用是由于后来修复的一个错误。 - chepner

1
set -- "${@:1:$#-1}"

将参数列表设置为第一个到倒数第二个,删除最后一个


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接