ChatGPT解决这个技术问题 Extra ChatGPT

处理除第一个参数之外的所有参数(在 bash 脚本中)

我有一个简单的脚本,其中第一个参数是为文件名保留的,所有其他可选参数都应该传递给脚本的其他部分。

使用 Google 我找到了 this wiki,但它提供了一个文字示例:

echo "${@: -1}"

我无法进行其他任何工作,例如:

echo "${@:2}"

或者

echo "${@:2,1}"

我从终端收到“错误替换”。

问题是什么,除了传递给 bash 脚本的第一个参数之外,我该如何处理?

为了让其他人感到困惑,提供了错误的 shebang 导致 "{@:2}" 不起作用,这就是正确答案与上面匹配的原因。
您刚刚使用了默认 shell,它在 Ubuntu 和许多其他 Linux 上是 dash。在破折号中,“${@: -1}”被解释为:{parameter:-word} - 使用默认值,如果参数未定义或为空,则使用 word。所以在破折号中“${@: -1}”的结果与“$@”完全相同。要使用 bash,只需在脚本文件中使用以下第一行:#!/bin/bash

C
Community

用这个:

echo "${@:2}"

以下语法:

echo "${*:2}"

也可以,但不推荐使用,因为正如 @Gordon 已经解释的那样,使用 *,它将所有参数作为带有空格的单个参数一起运行,而 @ 保留它们之间的中断(即使一些参数本身包含空格)。它与 echo 没有区别,但对许多其他命令很重要。


我刚刚意识到我的 shebang 很糟糕:#!/usr/bin/env sh这就是我遇到问题的原因。您的示例工作正常,与上面提供的相同,在我删除了那个 shebang 之后
改用 "${@:2}" - 使用 * 将所有参数作为带有空格的单个参数一起运行,而 @ 保留它们之间的中断(即使某些参数本身包含空格)。 echo 的差异并不明显,但它对许多其他事情都很重要。
@GordonDavisson这里的重点是一起运行论点。如果我们传递文件名,您将是正确的。 echo 足以为您连接它们;其他命令可能不太好。不要只使用其中一个:了解 *@ 之间的区别,以及何时使用它们。您应该大致平等地使用它们。一个很好的例子说明何时会出现问题:如果 $3 包含换行符 (\n),它将被替换为空格,前提是您有一个默认的 $IFS 变量。
@Zenexer:据我所知,问题是如何将除第一个参数以外的所有参数传递给“脚本的其他部分”,echo 仅用作示例——在这种情况下,它们应该 一起运行。根据我的经验,您希望它们一起运行的情况很少见(请参阅 this question for one example),而 "$@" 几乎总是您想要的。此外,仅当 $@(或 $*)不在双引号中时才会出现您提到的换行问题。
@GordonDavisson 嗯......你是对的,现在我想起来了;换行应该不是问题。我一定是在想别的东西。但是,我仍然不同意将它们一起运行。我需要在我的脚本中经常使用 $*
E
Esteis

如果您想要一个同样适用于 /bin/sh 的解决方案,请尝试

first_arg="$1"
shift
echo First argument: "$first_arg"
echo Remaining arguments: "$@"

shift [n] 移动位置参数 n 次。 shift$1 的值设置为 $2 的值,将 $2 的值设置为 $3 的值,依此类推,将 $# 的值减一。


+1:您可能应该在将其移走之前将 $1 保存到变量中。
令人惊讶的是 foo=shift 并没有达到我的预期。
我知道这是旧的,但试试 foo=$(shift)
@raumaankidwai 这不起作用有两个原因:1)shift(在shell中)没有任何输出。它只是丢弃 $1 并将所有内容向下移动。 2) $(...) 启动一个子shell,它有自己的本地参数。它移动子shell中的参数,这不会影响父shell
谢谢 :) 有什么办法可以做到 somecommand "$1" "${@:2}" 使用这种方法(即移位“内联”)吗?
w
walknotes

在 bash 4 或更高版本中工作:

#!/bin/bash
echo "$0";         #"bash"
bash --version;    #"GNU bash, version 5.0.3(1)-release (x86_64-pc-linux-gnu)"

在功能上:

echo $@;              #"p1" "p2" "p3" "p4" "p5"
echo ${@: 0};  #"bash" "p1" "p2" "p3" "p4" "p5"
echo ${@: 1};         #"p1" "p2" "p3" "p4" "p5"
echo ${@: 2};              #"p2" "p3" "p4" "p5"
echo ${@: 2:1};            #"p2"
echo ${@: 2:2};            #"p2" "p3"
echo ${@: -2};                       #"p4" "p5"
echo ${@: -2:1};                     #"p4"

注意 ':' 和 '-' 之间的空格,否则表示不同:

${var:-word} 如果 var 为 null 或未设置,则 word 将替换 var。 var 的值不会改变。 ${var:+word} 如果设置了 var,则用 word 代替 var。 var 的值不会改变。

如下所述:Unix / Linux - Shell Substitution


您的解释很有帮助,使用 bash 时示例运行良好。尽管如此,我不能使用“减号”元素。这对我没有用。不像预期的那样。谢谢
r
raghav710

http://wiki.bash-hackers.org/scripting/posparams

它解释了 shift 的使用(如果你想丢弃前 N 个参数),然后是 implementing Mass Usage


P
Pavman

遇到这个寻找别的东西。虽然这篇文章看起来相当陈旧,但下面使用 set -- "${@:#}" 说明了 bash 中最简单的解决方案(至少是 bash 4),其中 # 是我们要向前保留的数组元素的起始编号:

    #!/bin/bash

    someVar="${1}"
    someOtherVar="${2}"
    set -- "${@:3}"
    input=${@}

    [[ "${input[*],,}" == *"someword"* ]] && someNewVar="trigger"

    echo -e "${someVar}\n${someOtherVar}\n${someNewVar}\n\n${@}"

基本上,set -- "${@:3}" 只是像 perl 的 shift 一样弹出数组中的前两个元素,并保留包括第三个在内的所有剩余元素。我怀疑还有一种方法可以弹出最后一个元素。