Обработка всех аргументов, кроме первого (в скрипте bash)

444

У меня есть простой скрипт, где первый аргумент зарезервирован для имени файла, а все остальные необязательные аргументы должны быть переданы в другие части скрипта.

С помощью Google я нашел эту вики , но она предоставила буквальный пример:

echo "${@: -1}"

Я не могу заставить что-нибудь еще работать, например:

echo "${@:2}"

или

echo "${@:2,1}"

Я получаю "Плохая замена" из терминала.

В чем проблема, и как я могу обработать все, кроме первого аргумента, переданного скрипту bash?

тета
источник
21
Чтобы призвать кого-то еще запутать, был предоставлен неправильный шебанг, что привело "{@:2}"к неработоспособности, поэтому правильный ответ совпадает с приведенным выше.
Гуванте
3
Вы только что использовали оболочку по умолчанию, которая есть в Ubuntu и многих других Linux. В тире "$ {@: -1}" интерпретируется как: {параметр: -word} - использовать значения по умолчанию и использовать слово, если параметр не определен или имеет значение NULL. Таким образом, в тире "$ {@: -1}" результаты точно такие же, как и "$ @". Чтобы использовать bash, просто используйте следующую первую строку в файле сценария: #! /
Bin

Ответы:

661

Использовать этот:

echo "${@:2}"

Следующий синтаксис:

echo "${*:2}"

будет работать, но не рекомендуется, потому что, как уже объяснил @Gordon , при использовании *он запускает все аргументы вместе как один аргумент с пробелами, сохраняя при @этом разрывы между ними (даже если некоторые аргументы сами содержат пробелы ). Это не имеет значения echo, но это важно для многих других команд.

Оливер Чарльзуорт
источник
7
Я только что понял, что мой шебанг был плохим: #!/usr/bin/env shвот почему у меня были проблемы. Ваш пример работает нормально, так же, как указано выше, после того, как я удалил этот shebang
Theta
110
Используйте "${@:2}"вместо этого - использование *запускает все аргументы вместе как один аргумент с пробелами, сохраняя при @этом разрывы между ними (даже если некоторые из аргументов сами содержат пробелы). Разница не заметна с echo, но это имеет значение для многих других вещей.
Гордон Дэвиссон
2
@GordonDavisson Весь смысл в том, чтобы объединить аргументы. Если бы мы передавали имена файлов, вы были бы правы. echoпрощает достаточно, чтобы соединить их для вас; другие команды могут быть не такими хорошими. Не просто используйте один или другой: изучите разницу между *и @, и когда использовать каждый. Вы должны использовать их примерно одинаково. Хороший пример того, когда это будет проблемой: если $3содержит строку break ( \n), она будет заменена пробелом, если у вас есть $IFSпеременная по умолчанию .
Zenexer
1
@Zenexer: Насколько я понимаю, вопрос заключался в том, как передать все, кроме первого аргумента, «в другую часть сценария», echoиспользуя только в качестве примера - в этом случае их не следует запускать вместе. По моему опыту, ситуации, когда вы хотите, чтобы они работали вместе, встречаются редко (см. Этот вопрос для одного примера ), и "$@"это почти всегда то, что вы хотите. Кроме того, проблема, о которой вы упоминаете с разрывом строки, возникает, только если $@(или $*) не заключена в двойные кавычки.
Гордон Дэвиссон
@GordonDavisson Хм ... ты прав, теперь, когда я думаю об этом; разрыв строки не должен быть проблемой. Должно быть, я думал о чем-то еще. Тем не менее, я все еще не согласен с тем, чтобы управлять ими вместе; Мне нужно использовать $*довольно часто в моих сценариях.
Zenexer
181

Если вы хотите решение, которое также работает в /bin/shпопытке

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

shift [n]сдвигает позиционные параметры n раз. A shiftустанавливает значение $1в значение $2, значение $2в значение $3и т. Д., Уменьшая значение $#на единицу.

Бен Джексон
источник
25
+1: Вам, вероятно, следует сохранить $ 1 в переменную, прежде чем сдвинуть ее.
Гленн Джекман
3
Удивительно, foo=shiftно не делает то, что я ожидал.
Кит Смайли
Я знаю, что это старый, но попробуйтеfoo=$(shift)
Raumaan Kidwai
12
@raumaankidwai Это не работает по двум причинам: 1) shift(в оболочке) не имеет никакого вывода. Это просто отбрасывает $1и сдвигает все вниз. 2) $(...)запускает подоболочку, которая имеет свои локальные аргументы. Это сдвигает аргументы в подоболочке, что никак не влияет на родителя
Бен Джексон
Спасибо :) Есть ли способ сделать то, что somecommand "$1" "${@:2}"делает с этим методом (то есть сдвиг "inline"), хотя?
Аноним
0

Наткнулся на это в поисках чего то другого. Хотя пост выглядит довольно старым, простейшее решение в bash показано ниже (по крайней мере, bash 4) с использованием set -- "${@:#}"где # - начальный номер элемента массива, который мы хотим сохранить вперед:

    #!/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 и сохраняет все остальные элементы, включая третий. Я подозреваю, что есть способ выскочить и из последних элементов.

Pavman
источник