如何移植使用“${@:2}”?

How to portability use "${@:2}"?

Allow for ${@:2} syntax in variable assignment 上,他们说我不应该使用 "${@:2}",因为它会破坏不同 shell 的东西,我应该改用 "${*:2}"

但是使用 "${*:2}" 而不是 "${@:2}" 是无稽之谈,因为 "${@:2}" 不等同于 "${*:2}",因为 following example:

#!/bin/bash
check_args() {
  echo "$#=$#"
  local counter=0
  for var in "$@"
  do
      counter=$((counter+1));
      printf "$counter. '$var', ";
  done
  printf "\n\n"
}

# setting arguments
set -- "space1 notspace" "space2 notspace" "lastargument"; counter=1
echo $counter': ---------------- "$*"'; counter=$((counter+1))
check_args "$*"

echo $counter': ---------------- "${*:2}"'; counter=$((counter+1))
check_args "${*:2}"

echo $counter': ---------------- "${@:2}"'; counter=$((counter+1))
check_args "${@:2}"

-->

GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)
1: ---------------- "$*"
$#=1
1. 'space1 notspace space2 notspace lastargument',

2: ---------------- "${*:2}"
$#=1
1. 'space2 notspace lastargument',

3: ---------------- "${@:2}"
$#=2
1. 'space2 notspace', 2. 'lastargument',

如果我不能使用 "${@:2}" (as they say),我可以用什么替代?

这是原始问题 Process all arguments except the first one (in a bash script),他们将参数与空格放在一起的唯一答案是使用 "${@:2}"

除非您点击链接,否则问题中的上下文不清楚。这是关于 the following recommendation from shellcheck.net:

local _help_text="${@:2}"
                  ^––SC2124 Assigning an array to a string! Assign as array, or use * instead of @ to concatenate.

简短回答:不要将事物列表(如参数)分配给普通变量,而是使用数组。

长答案:通常,"${@:2}" 会得到除第一个参数以外的所有参数,每个参数都被视为一个单独的项目 ("word")。另一方面,"${*:2}" 生成一个项目,其中除了第一个参数之外的所有参数都粘在一起,由 space 分隔(或者 $IFS 的第一个字符是什么)。

但是在您分配给普通变量的特定情况下,该变量只能存储单个项目,因此 var="${@:2}" also 折叠参数缩小到单个项目,但它以不如 "${*:2}" 一致的方式进行。为了避免这种情况,请使用能够存储多个项目的东西:数组。所以:

  • 非常糟糕:var="${@:2}"
  • 差一点:var="${*:2}"
  • 好多了:arrayvar=("${@:2}")(括号使它成为一个数组)

注意:要取回数组的元素,并将每个元素作为单独的项目正确处理,请使用 "${arrayvar[@]}"。此外,并非所有 shell 都支持数组(值得注意的是,dash 不支持它们),因此如果你使用它们,你应该确保使用 bash shebang(#!/bin/bash#!/usr/bin/env bash).如果您真的需要对其他 shell 的可移植性,事情会变得 复杂。

${@:2}${*:2} 都不是可移植的,许多 shell 会拒绝这两种语法。如果你想处理除第一个参数之外的所有参数,你应该用 shift 去掉第一个参数。

first=""
shift
echo The arguments after the first are:
for x; do echo "$x"; done

此时第一个参数在"$first"中,位置参数向下移动一位。

这演示了如何将所有 ${@} 个参数合并到一个变量中,而不需要 hack ${@:1}${@:2} (live example):

#!/bin/bash

function get_all_arguments_as_single_one_unquoted() {
    single_argument="$(printf "%s " "${@}")";
    printf "unquoted arguments %s: '%s'\n" "${#}" "${single_argument}";
}

function get_all_arguments_as_single_one_quoted() {
    single_argument="";
    printf "quoted arguments %s: '%s'\n" "${#}" "${single_argument}";
}

function escape_arguments() {
    escaped_arguments="$(printf '%q ' "${@}")";
    get_all_arguments_as_single_one_quoted "${escaped_arguments}";
    get_all_arguments_as_single_one_unquoted ${escaped_arguments};
}

set -- "first argument" "last argument";
escape_arguments "${@}";

-->

GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)
quoted arguments 1: 'first\ argument last\ argument '
unquoted arguments 4: 'first\ argument last\ argument '

正如 @William Pursell 答案指出的那样,如果您只想获得 {@:2} 个参数,您可以在 "${@}"

之前添加一个 shift 调用
function escape_arguments() {
    shift;
    escaped_arguments="$(printf '%q ' "${@}")";
    get_all_arguments_as_single_one_quoted "${escaped_arguments}";
    get_all_arguments_as_single_one_unquoted ${escaped_arguments};
}

-->

GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)
quoted arguments 1: 'last\ argument '
unquoted arguments 2: 'last\ argument '