Поширити всі аргументи в скрипті bash shell


851

Я пишу дуже простий сценарій, який викликає інший скрипт, і мені потрібно поширити параметри з мого поточного сценарію до сценарію, який я виконую.

Наприклад, моє ім'я сценарію - foo.shі дзвонитьbar.sh

foo.sh:

bar $1 $2 $3 $4

Як я можу це зробити, не чітко вказуючи кожен параметр?



Відповіді:


1404

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

Дотримуйтесь:

$ 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 каталог bar me Чи можливо мати скрипт оболонки, який може бачити справжній оригінальний командний рядок у комплекті з цитатами або втеченими рядками?
Марк Едінгтон

3
А як щодо передачі прапорів? Напр., "./Bar.sh --with-stuff"
Натан Лонг

4
Я пропоную всім, хто хоче краще зрозуміти тему Розбиття слів, прочитати більше про це тут.
Rany Albeg Wein

4
Я пропоную вам показати, що відбувається, якщо включити і 'arg with spaces'для трьох прикладів. Я був здивований результатами; сподіваємось, ви зможете пояснити їх.
Майкл Шепер

2
@MichaelScheper, як би там не було, ./foo.sh "arg with spaces"і на ./foo.sh 'arg with spaces'100% однакові, тому я не бачу, як пропозиція додати його до наведених прикладів може допомогти.
Чарльз Даффі

475

Для баш та інших оболонок Борна:

java com.myserver.Program "$@"

13
@Amir: Ні, не для csh . Для розуму кожного: не скрипт csh . Але я думаю, можливо, $argv:qце спрацює в деяких варіантах csh.
Кріс Джонсен

2
Дякую! За бажанням, це передає той самий набір аргументів, отриманий сценарієм, - не один великий аргумент. Подвійні лапки необхідні. Працює навіть якщо цитуються аргументи, що містять пробіли.
Енді Томас

24
Пов’язано: якщо ваш скрипт оболонки просто виконує функцію обгортки для запуску Java, подумайте про те, щоб зробити останній рядок. exec java com.myserver.Program "$@"Це спричиняє, що баш виконує java, а не чекає завершення. Отже, ви використовуєте один менш слотовий процес. Крім того, якщо батьківський процес (який керував вашим сценарієм) переглядає його через pid і очікує, що це буде процес "java", деякі незвичайні речі можуть порушитися, якщо ви не зробите exec; exec змушує java успадкувати той самий під.
greggo

7
@dragonxlwang: Ви можете використовувати змінну - масив, якщо ваша оболонка підтримує їх (наприклад , Баш , ЗШ , інші, але не ясно , Bourne- або POSIX-оболонки): зберегти в аргументах з args=("$@")і розкладемо кожен елемент у вигляді окремої оболонки «слово» ( схожий на "$@") з "${args[@]}".
Кріс Джонсен

1
Чи є які-небудь "gotchas", які слід пам’ятати при використанні "$@", як, наприклад, це не вдасться, якщо ви уникнули пробілів в аргументі, нульові символи чи інші спеціальні символи?
IQAndreas

97

Використання "$@"(працює для всіх сумісних POSIX ).

[...], bash містить змінну "$ @", яка розширюється на всі параметри командного рядка, розділені пробілами.

З Баша на прикладі .


6
Тож "$ @" не просто котирування навколо $ @, а фактично інша вбудована змінна?
бен

5
@ben Це єдина змінна, але для неї потрібні подвійні лапки, щоб мати корисне значення, відмінне від (ламане) $*. Я вважаю, що тут є історичний прогрес; $*не працювали так, як було розроблено, тому $@було придумано для його заміни; але правила цитування є такими, якими вони є, подвійні лапки навколо нього все ж потрібні (або вони повернуться до порушеної $*семантики).
трійчатка

7
"Так" $ @ "є не просто котируванням навколо $ @, а фактично іншою вбудованою змінною?" - Для всіх намірів і цілей, так: stackoverflow.com/a/28099707/162094
Стюарт Берг

1
Якщо ви називаєте сценарій, що містить echo "$@"так, ./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.
FeRD

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
=================================

1
хороша лаконічна демонстрація та внесок у цю відповідь.
Мерлін

33
#!/usr/bin/env bash
while [ "$1" != "" ]; do
  echo "Received: ${1}" && shift;
done;

Просто подумав, що це може бути трохи корисніше, намагаючись перевірити, як аргументи потрапляють у ваш сценарій


6
це однозначно не допомогло відповісти на його запитання, але це справді корисно. прихильне!
Даріо Руссо

2
Він порушується при передачі порожнього параметра. Слід перевірити$#
Себі

хороший тестер арґусів, погодьтеся "", ''як аргумент, також якщо аргументів не було, він мовчить. Я спробував це виправити, але потрібна петля та лічильник $#. Я лише додав це в кінці:echo "End of args or received quoted null"
Мерлін

8

У мого SUN Unix дуже багато обмежень, навіть "$ @" не трактувались за бажанням. Моє вирішення - $ {@}. Наприклад,

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

До речі, мені довелося мати саме цей сценарій, оскільки мій Unix також не підтримує grep -r


Це питання Баша; ви використовуєте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'

3
Я не заперечу, що це викликає сумніви, але це насправді має сенс в рамках семантики "$@"в баші. Це також допомагає проілюструвати ключову різницю між $@і $*, чому вони корисні. З bash(1)розділу "Спеціальні параметри" на сторінці "Параметри": " *- Коли розширення відбувається в межах подвійних лапок, воно розширюється на одне слово зі значенням кожного параметра [...] Тобто "$*"еквівалентно "$1c$2c...", де cє [ $IFS]." І справді, використовуючи $*замість $@у своєму першому прикладі, виводиться сітка, ідентична другій версії.
FeRD

2
А тепер порівняйте це "$@". Знову зі сторінки man: " @- Коли розширення відбувається в межах подвійних лапок, кожен параметр розширюється на окреме слово. Тобто "$@"еквівалентно "$1" "$2"... Якщо розширення з подвійним цитуванням відбувається в межах слова, розширення першого параметра приєднується з початковою частиною початкового слова, а розширення останнього параметра з'єднується з останньою частиною початкового слова. " ... І справді, якби ваш код був bash -c "true foo $@ bar baz", то запускайте його так, як test.sh one twoчистий bash -c 'true foo one' 'two bar baz'.
FeRD

1
Дякую за цитування та інформацію про документи $*, я, здається, забув, що вона існує ..
ColinM

Хе. Я навпаки, $@тільки набирав тяги, коли я вперше почав розробляти сценарії оболонки, я все одно повинен нагадувати собі, що він там є. Це було звичайно бачити, що "$*"використовується в сценаріях ... тоді автор зрозумів би, що він розбиває всі їхні аргументи разом, тож вони спробують усілякі складні дурниці з розбиттям слів "$*"або [пере] складання списку аргументів циклами над тим, shiftщоб тягнути їх по одному ... просто за допомогою $@вирішує це. (Допомагає тому, що bash використовує ту саму мнеміку для доступу до членів масиву: також ${var[*]}для всіх, як для слова, ${var[@]}для списку слів.)
FeRD,

Справжня проблема тут полягає в тому, що ви користуєтесь bash -cтаким чином, який абсолютно не має сенсу.
трійка

4

Іноді ви хочете передати всі свої аргументи, але перед цим прапор (наприклад --flag)

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

Це можна зробити наступним чином:

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

Примітка: щоб уникнути зайвого розщеплення поля, ви повинні цитувати %sта $@, а щоб не мати жодної рядки, не можна цитувати підскладу printf.


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 '@' \' )

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" ' "$@")"

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.