Распространение всех аргументов в сценарии оболочки bash

854

Я пишу очень простой скрипт, который вызывает другой скрипт, и мне нужно передать параметры из моего текущего скрипта в скрипт, который я выполняю.

Например, мой сценарий зовут foo.shи называетbar.sh

foo.sh:

bar $1 $2 $3 $4

Как я могу сделать это без явного указания каждого параметра?

Fragsworth
источник

Ответы:

1410

Используйте "$@"вместо обычного, $@если вы действительно хотите, чтобы ваши параметры передавались одинаково.

Заметим:

$ cat foo.sh
#!/bin/bash
baz.sh $@

$ cat bar.sh
#!/bin/bash
baz.sh "$@"

$ cat baz.sh
#!/bin/bash
echo Received: $1
echo Received: $2
echo Received: $3
echo Received: $4

$ ./foo.sh first second
Received: first
Received: second
Received:
Received:

$ ./foo.sh "one quoted arg"
Received: one
Received: quoted
Received: arg
Received:

$ ./bar.sh first second
Received: first
Received: second
Received:
Received:

$ ./bar.sh "one quoted arg"
Received: one quoted arg
Received:
Received:
Received:
Сдаз МакСкиббонс
источник
7
Это работает для чистого прохождения через кавычки / экранированные строки: Наблюдать: cat rsync_foo.sh #! / Bin / bash echo "$ @" rsync "$ @" ./rsync_foo.sh -n "bar me" bar2 bar me bar2skipping directory bar me Возможно ли иметь скрипт оболочки, который может видеть настоящую оригинальную командную строку с кавычками или экранированными строками?
Марк Эдингтон
3
Как насчет прохождения флагов? Например, './bar.sh --with-stuff'
Натан Лонг
4
Я предлагаю всем, кто хочет лучше понять тему разделения слов, узнать больше об этом здесь.
Рани Альбег Вайн
4
Я предлагаю вам показать, что происходит при включении и 'arg with spaces'для трех примеров. Я был удивлен результатами; надеюсь, вы можете объяснить их.
Майкл Шепер
2
@MichaelScheper, во всяком случае, ./foo.sh "arg with spaces"и ./foo.sh 'arg with spaces'на 100% идентичны, поэтому я не понимаю, как предложение добавить его в приведенные примеры могло бы помочь.
Чарльз Даффи
475

Для баш и других оболочек типа Борна:

java com.myserver.Program "$@"
Крис Джонсен
источник
13
@ Амир: Нет, не для csh . Для всеобщего здравомыслия: не пишите сценарий csh . Но я думаю, что возможно $argv:qбудет работать в некоторых вариантах csh.
Крис Джонсен
2
Спасибо! По желанию, это передает тот же набор аргументов, полученных сценарием - не один большой аргумент. Двойные кавычки необходимы. Работает, даже если с кавычками аргументы, которые включают пробелы.
Энди Томас,
24
Связано: если ваш скрипт оболочки просто выступает в роли оболочки для запуска java, подумайте над созданием последней строки. exec java com.myserver.Program "$@"Это заставит bash выполнить java, а не ждать, пока он завершится. Итак, вы используете на один процесс меньше. Кроме того, если родительский процесс (который запускал ваш скрипт) просматривает его через pid и ожидает, что это будет процесс 'java', некоторые необычные вещи могут прерваться, если вы не выполните exec; exec заставляет Java наследовать тот же pid.
Грегго
7
@dragonxlwang: Вы можете использовать переменную массива, если ваша оболочка поддерживает их (например, bash , zsh , другие, но не обычные Bourne- или POSIX-оболочки): сохранить в аргументы с args=("$@")и расширить каждый элемент как отдельное «слово» оболочки ( сродни "$@") с "${args[@]}".
Крис Джонсен
1
Есть ли какие-то "ошибки", которые следует учитывать при использовании "$@", например, не получится ли, если в аргументе были пробелы, или нулевые символы, или другие специальные символы?
IQAndreas
97

Использование "$@"(работает для всех POSIX- совместимых).

[...] bash имеет переменную $ @, которая распространяется на все параметры командной строки, разделенные пробелами.

Из Баш по примеру .

Мик
источник
6
Значит, «$ @» - это не просто кавычки вокруг $ @, а фактически другая встроенная переменная?
Бен
5
@ben Это единственная переменная, но она требует, чтобы двойные кавычки вокруг нее имели полезное значение, отличное от (ломаного) $*. Я верю, что здесь есть историческая прогрессия; $*не работал, как задумано, поэтому $@был изобретен, чтобы заменить его; но поскольку правила цитирования являются тем, чем они являются, двойные кавычки вокруг них все еще требуются (или это вернется к нарушенной $*семантике).
трипл
7
«Значит,« $ @ »- это не просто кавычки вокруг $ @, а фактически другая встроенная переменная?» - Для всех намерений и целей, да: stackoverflow.com/a/28099707/162094
Стюарт Берг
1
Если вы вызываете скрипт, содержащий echo "$@"as, ./script.sh a "b c" dто вы просто получаете a b c dвместо a "b c" d, что очень сильно отличается.
Изаранди
7
@isarandi Хотя это правда, что вывод больше не содержит кавычек, это то, что вы ожидаете ... но будьте уверены, что в сценарии, echoполучает три аргумента: "a" "b c" "d"(тогда оболочка объединяет их вместе как часть своего расширения строки). Но если бы вы использовали, for i in "$@"; do echo $i; doneвы бы получили a⏎b c⏎d.
Февраль
64

Я понимаю, что на это хорошо ответили, но вот сравнение между "$ @" $ @ "$ *" и $ *

Содержание тестового скрипта:

# cat ./test.sh
#!/usr/bin/env bash
echo "================================="

echo "Quoted DOLLAR-AT"
for ARG in "$@"; do
    echo $ARG
done

echo "================================="

echo "NOT Quoted DOLLAR-AT"
for ARG in $@; do
    echo $ARG
done

echo "================================="

echo "Quoted DOLLAR-STAR"
for ARG in "$*"; do
    echo $ARG
done

echo "================================="

echo "NOT Quoted DOLLAR-STAR"
for ARG in $*; do
    echo $ARG
done

echo "================================="

Теперь запустите тестовый скрипт с различными аргументами:

# ./test.sh  "arg with space one" "arg2" arg3
=================================
Quoted DOLLAR-AT
arg with space one
arg2
arg3
=================================
NOT Quoted DOLLAR-AT
arg
with
space
one
arg2
arg3
=================================
Quoted DOLLAR-STAR
arg with space one arg2 arg3
=================================
NOT Quoted DOLLAR-STAR
arg
with
space
one
arg2
arg3
=================================
JDS
источник
1
хорошая краткая демонстрация и вклад в этот ответ.
Мерлин
33
#!/usr/bin/env bash
while [ "$1" != "" ]; do
  echo "Received: ${1}" && shift;
done;

Просто подумал, что это может быть немного более полезно при попытке проверить, как аргументы входят в ваш скрипт

Грегори Патмор
источник
6
это определенно не помогло ответить на его вопрос, но это действительно полезно. upvoted!
Дарио Руссо
2
Он ломается, когда передается пустой параметр. Вы должны проверить$#
Себи
Хороший тестер аргументов, согласитесь "", ''в качестве аргумента, также, если аргументов не было, он молчит. Я пытался это исправить, но нужен цикл для и счетчик с $#. Я только добавил это в конце:echo "End of args or received quoted null"
Мерлин
8

У моего SUN Unix есть много ограничений, даже «$ @» не интерпретируется как нужно. Мой обходной путь - $ {@}. Например,

#!/bin/ksh
find ./ -type f | xargs grep "${@}"

Кстати, мне нужно было этот конкретный скрипт, потому что мой Unix также не поддерживает grep -r

Hampden123
источник
Это вопрос Bash; вы используетеksh
tripleee
6

Если вы включите $@в строку в кавычках другие символы, то при наличии нескольких аргументов поведение будет очень странным, только первый аргумент будет включен в кавычки.

Пример:

#!/bin/bash
set -x
bash -c "true foo $@"

Урожайность:

$ bash test.sh bar baz
+ bash -c 'true foo bar' baz

Но сначала присваиваем другую переменную:

#!/bin/bash
set -x
args="$@"
bash -c "true foo $args"

Урожайность:

$ bash test.sh bar baz
+ args='bar baz'
+ bash -c 'true foo bar baz'
ColinM
источник
3
Я не буду отрицать, что это сбивает с толку, но это на самом деле имеет смысл в семантике "$@"в bash. Это также помогает проиллюстрировать ключевое различие между $@и $*, и почему они оба полезны. Из bash(1)справочной страницы раздела «Специальные параметры»: « *- Когда раскрытие происходит в двойных кавычках, оно раскрывается в одно слово со значением каждого параметра […], то "$*"есть эквивалентно "$1c$2c...", где cнаходится [ $IFS].» И действительно, использование $*вместо $@вашего первого примера приведет к получению результатов, идентичных второй версии.
февраля
2
Теперь сравните это с "$@". Снова со страницы руководства: « @- Когда раскрытие происходит в двойных кавычках, каждый параметр раскрывается в отдельное слово. То "$@"есть эквивалентно "$1" "$2"… Если раскрытие в двойных кавычках происходит внутри слова, расширение первого параметра присоединяется с начальной частью исходного слова, а расширение последнего параметра соединяется с последней частью исходного слова. " ... И действительно, если бы ваш код был bash -c "true foo $@ bar baz", то запускайте его как test.sh one twoбы чистый bash -c 'true foo one' 'two bar baz'.
февраля
1
Спасибо за цитирование и информацию о документах $*, я, кажется, забыл, что он существует ..
ColinM
Хех. Я наоборот, $@только набирал обороты, когда я впервые начал писать сценарии оболочки, мне все еще нужно напоминать себе, что он есть. Это было обычным делом увидеть, как они "$*"используются в сценариях ... тогда автор понимал, что он разбивает все их аргументы вместе, поэтому они пробуют всевозможную сложную чепуху с разделением слов "$*"или [пере] собирают список аргументов с помощью цикла над shiftтянуть их вниз по одному ... только с помощью $@решает эту проблему. (Помогает тому, что bash использует ту же мнемонику для доступа к элементам массива: ${var[*]}для них всех как для слова, ${var[@]}для списка слов.)
FeRD
Настоящая проблема здесь в том, что вы используете bash -cспособ, который не имеет абсолютно никакого смысла.
трипл
4

Иногда вы хотите передать все свои аргументы, но перед ними стоит флаг (например --flag)

$ bar --flag "$1" --flag "$2" --flag "$3"

Вы можете сделать это следующим образом:

$ bar $(printf -- ' --flag "%s"' "$@")

примечание: чтобы избежать дополнительного разделения полей, вы должны заключить в кавычки %sи $@, и, чтобы избежать единственной строки, вы не можете заключить в кавычки подоболочку printf.

kvantour
источник
3

Работает нормально, за исключением случаев, когда у вас есть пробелы или экранированные символы. Я не нахожу способ захватить аргументы в этом случае и отправить в ssh внутри скрипта.

Это может быть полезно, но так ужасно

_command_opts=$( echo "$@" | awk -F\- 'BEGIN { OFS=" -" } { for (i=2;i<=NF;i++) { gsub(/^[a-z] /,"&@",$i) ; gsub(/ $/,"",$i );gsub (/$/,"@",$i) }; print $0 }' | tr '@' \' )
user6650302
источник
3

Многие ответы здесь рекомендуют $@или $*с и без кавычек, однако ни один, кажется, не объясняет, что они действительно делают и почему вы должны так поступать. Итак, позвольте мне украсть это превосходное резюме из этого ответа :

введите описание изображения здесь

Обратите внимание, что кавычки имеют все значение и без них оба имеют идентичное поведение.

Для моей цели мне нужно было передать параметры из одного скрипта в другой как есть, и для этого лучшим вариантом является:

# file: parent.sh
# we have some params passed to parent.sh 
# which we will like to pass on to child.sh as-is

./child.sh $*

Обратите внимание, что нет кавычек и $@должно работать в вышеуказанной ситуации.

Шиталь шах
источник
2

bar "$@" будет эквивалентно bar "$1" "$2" "$3" "$4"

Обратите внимание, что кавычки важны!

"$@", $@, "$*"Или $*будет каждый вести себя немного отличается относительно побега и конкатенации , как описано в этом StackOverflow ответ .

Один тесно связанный вариант использования передает все заданные аргументы внутри аргумента, подобного этому:

bash -c "bar \"$1\" \"$2\" \"$3\" \"$4\"",

Я использую вариант ответа @ kvantour для достижения этой цели:

bash -c "bar $(printf -- '"%s" ' "$@")"

Davidiusdadi
источник