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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.