如何使用“ $ {@:2}”实现可移植性?

时间:2019-06-30 03:27:01

标签: arrays bash shell posix shellcheck

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

但是用"${*:2}"代替"${@:2}"是无意义的,因为这样做"${@:2}"不等同于following example"${*:2}"

#!/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}"

3 个答案:

答案 0 :(得分:4)

除非您点击链接,否则问题中的上下文并不清楚。与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}"将获得除第一个参数以外的所有参数,每个参数都被视为一个单独的项目(“单词”)。另一方面,"${*:2}"会产生单个项目,其中除了第一个参数之外,所有其他参数都粘结在一起,并用空格(或$IFS的第一个字符不管)分隔。

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

  • 真的很糟糕:var="${@:2}"
  • 稍差一些:var="${*:2}"
  • 更好:arrayvar=("${@:2}")(括号使它成为数组)

注意:要取回数组的元素(将每个元素都正确地视为一个单独的项目),请使用"${arrayvar[@]}"。另外,并非所有shell都支持数组(特别是dash不支持数组),因此,如果使用它们,则应确保使用bash shebang(#!/bin/bash#!/usr/bin/env bash )。如果您确实需要可移植到其他Shell,则事情变得更加复杂。

答案 1 :(得分:3)

${@:2}${*:2}都不是可移植的,并且许多shell都将两者都视为无效语法。如果要处理除第一个参数以外的所有参数,则应移开第一个参数。

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

此时,第一个参数在“ $ first”中,并且位置参数向下移动一个。

答案 2 :(得分:0)

这演示了如何将所有${@}自变量组合为一个变量,而不会被${@: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="${1}";
    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 '