Как для переносимости использовать "$ {@: 2}"? - PullRequest
2 голосов
/ 30 июня 2019

Вкл. Допускается синтаксис $ {@: 2} в присваивании переменных они говорят, что я не должен использовать "${@:2}", потому что он разбивает вещи на разные оболочки, и вместо этого я должен использовать "${*:2}".

Но использование "${*:2}" вместо "${@:2}" - нонсенс, поскольку выполнение "${@:2}" не эквивалентно "${*: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}" (, как говорится ), какой эквивалент я могу использовать вместо этого?

Это оригинальный вопрос Обработка всех аргументов, кроме первого (в bash-скрипте) , и единственный ответ, чтобы сохранить аргументы вместе с пробелами, заключается в использовании "${@:2}"

Ответы [ 3 ]

4 голосов
/ 30 июня 2019

Есть контекст, который не ясен в вопросе, если вы не перейдете по ссылкам. Это касается следующей рекомендации от 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[@]}". Кроме того, массивы поддерживаются не всеми оболочками (в частности, dash не поддерживает их), поэтому, если вы используете их, вы должны обязательно использовать bash shebang (#!/bin/bash или #!/usr/bin/env bash). Если вам действительно нужна переносимость к другим оболочкам, все становится намного сложнее

3 голосов
/ 30 июня 2019

Ни ${@:2}, ни ${*:2} не являются переносимыми, и многие оболочки будут отклонять оба в качестве недопустимого синтаксиса. Если вы хотите обработать все аргументы, кроме первого, вы должны избавиться от первого с помощью сдвига.

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

В этот момент первый аргумент находится в «$ first», а позиционные параметры смещены на единицу.

0 голосов
/ 30 июня 2019

Это демонстрирует, как объединить все ${@} аргументы в один переменный без хака ${@:1} или ${@:2} ( живой пример ):

#!/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 '

Как указывает @ Уильям Перселл , если вы хотите получить только {@: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 '
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...