Bir bash shell betiğindeki tüm bağımsız değişkenleri yayma


851

Başka bir komut dosyası çağıran çok basit bir komut dosyası yazıyorum ve geçerli komut dosyasından yürüttüğüm komut dosyasına parametreleri yaymak gerekiyor.

Örneğin, kod adım foo.shve çağrılarbar.sh

foo.sh:

bar $1 $2 $3 $4

Her bir parametreyi açıkça belirtmeden bunu nasıl yapabilirim?



Yanıtlar:


1405

"$@"Düz yerine kullan$@Parametrelerinizin aynı şekilde geçirilmesini istiyorsanız .

Gözlemek:

$ 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
Bu işlem tırnak içine alınmış / kaçan dizelerden saf geçiş için yapar: Gözlemleyin: cat rsync_foo.sh #! / Bin / bash echo "$ @" rsync "$ @" ./rsync_foo.sh -n "bar me" bar2 bar me bar2skipping directory bar me Gerçek orijinal komut satırını tırnak işaretleri veya çıkış karakter dizileriyle birlikte görebilen kabuk betiği olabilir mi?
Mark Edington

3
Bayrakları geçmeye ne dersiniz? Örneğin, './bar.sh --with-stuff'
Nathan Long

4
Word Splitting konusunu daha iyi anlamak, burada daha fazla okumak
Rany Albeg Wein

4
'arg with spaces'Üç örnek için de yer alırken ne olacağını göstermenizi öneririm . Sonuçlar beni şaşırttı; umarım onları açıklayabilirsin.
Michael Scheper

2
@MichaelScheper, her neyse ./foo.sh "arg with spaces"ve ./foo.sh 'arg with spaces'% 100 aynı, bu yüzden verilen örneklere ekleme önerisinin nasıl yardımcı olacağını görmüyorum.
Charles Duffy

475

İçin bash ve diğer Bourne benzeri kabuklar:

java com.myserver.Program "$@"

13
@Amir: Hayır, csh için değil . Herkesin akıl sağlığı için: csh yazmayın . Ama belki $argv:qbazı csh varyantlarında işe yarayacağını düşünüyorum .
Chris Johnsen

2
Teşekkürler! İstendiği gibi, bu komut dosyası tarafından alınan aynı argüman kümesini iletir - büyük bir argüman değil. Çift tırnak işareti gereklidir. Boşluk içeren tırnak işaretleri ile bile çalışır.
Andy Thomas

24
İlgili: kabuk betiğiniz sadece java'yı çalıştırmak için bir sarıcı olarak davranıyorsa, son satırı yapmayı düşünün exec java com.myserver.Program "$@"Bu, bash'ın tamamlanmasını beklemek yerine java'ya çalıştırılmasına neden olur. Yani, bir tane daha az işlem yuvası kullanıyorsunuz. Ayrıca, üst işlem (komut dosyanızı çalıştıran) pid aracılığıyla izliyor ve 'java' işlemi olmasını bekliyorsa, bir exec yapmazsanız bazı olağandışı şeyler kırılabilir; exec, java'nın aynı pid'i devralmasına neden olur.
greggo

7
@dragonxlwang: Sen, eğer kabuk destekleri onları (örneğin bir dizi değişkeni kullanabilirsiniz bash , zsh kaydet:, diğerleri, ama düz değil Bourne- veya POSIX kabuk) args ile args=("$@")ve ayrı bir kabuk “kelime” olarak her öğeyi genişletmek ( benzer "$@") ile "${args[@]}".
Chris Johnsen

1
Kullanırken akılda tutulması gereken bir "gotchas" var mı "$@", bir argümandaki boşluklardan, boş karakterlerden veya diğer özel karakterlerden kaçarsanız başarısız olur mu?
IQAndreas

97

Kullanım "$@"(tüm POSIX uyumlulukları için çalışır ).

[...], bash, boşluklarla ayrılmış tüm komut satırı parametrelerine genişleyen "$ @" değişkenine sahiptir.

Gönderen Bash örnek .


6
Yani "$ @" sadece $ @ civarında değil, farklı bir yerleşik değişken de mi?
ben

5
@ben Bu tek bir değişkendir, ancak etrafındaki çift tırnaklardan (kırık) farklı bir değere sahip olmasını gerektirir $*. Burada tarihsel ilerleme olduğuna inanıyorum; $*tasarlandığı gibi çalışmadı, $@yerine koymak için icat edildi; ancak alıntı kuralları oldukları şeydir, etrafındaki çift tırnaklar hala gereklidir (veya kırık $*semantiğe dönecektir ).
üçlü

7
"Öyleyse" $ @ "sadece $ @ civarında değil, aynı zamanda farklı bir yerleşik değişken mi? - Tüm niyetler ve amaçlar için evet: stackoverflow.com/a/28099707/162094
Stuart Berg

1
İçerdiği echo "$@"gibi bir komut dosyası çağırırsanız , bunun yerine çok farklı olanı ./script.sh a "b c" dalırsınız . a b c da "b c" d
isarandi

7
@isarandi Çıktının artık tırnak içermediği doğru olsa da, beklediğiniz şey budur ... ancak komut dosyasının içinde echoüç argüman aldığından emin olabilirsiniz : "a" "b c" "d"(sonra kabuk, dize genişletmesinin bir parçası olarak bunları birleştirir). Ama eğer kullansaydın for i in "$@"; do echo $i; done, kazanırdın a⏎b c⏎d.
FeRD

64

Bunun iyi yanıtlandığını anlıyorum ama burada "$ @" $ @ "$ *" ile $ * arasında bir karşılaştırma var

Test komut dosyasının içeriği:

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

Şimdi, test komut dosyasını çeşitli argümanlarla çalıştırın:

# ./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
iyi özlü gösteri ve bu cevaba katkı.
Merlin

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

Arglerin betiğinize nasıl geldiğini test etmeye çalışırken bunun biraz daha yararlı olabileceğini düşündüm.


6
bu kesinlikle sorusunu cevaplamamıza yardımcı olmadı, ama bu gerçekten yararlı. upvoted!
Dario Russo

2
Boş bir parametre iletildiğinde kırılır. Kontrol etmelisin$#
Sebi

İyi Args tester, üzerinde anlaşma "", ''hiçbir bağımsız değişken olsaydı da sessiz bir argüman olarak. Bunu düzeltmeye çalıştım, ama bir döngü ve sayaç için ihtiyacı var $#. Bunu sonuna ekledim:echo "End of args or received quoted null"
Merlin

8

Benim SUN Unix'in birçok sınırlaması var, "$ @" bile istendiği gibi yorumlanmadı. Geçici çözümüm $ {@}. Örneğin,

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

Bu arada, bu belirli komut dosyasına sahip olmalıydım çünkü Unix'im grep -r'yi de desteklemiyor


Bu bir Bash sorusu; kullanıyorsunuzksh
üçlü

6

Eklerseniz $@Diğer karakterlerle tırnak alınmış bir dize , birden fazla argüman olduğunda davranış çok tuhaftır, tırnak içine sadece ilk argüman eklenir.

Misal:

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

Verim:

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

Ama önce farklı bir değişkene atama:

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

Verim:

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

3
Bunun rahatsız edici olduğunu inkar etmeyeceğim, ama aslında bash'ın anlambilimi içinde mantıklı "$@". Ayrıca , ve arasındaki önemli farkı $@ve $*her ikisinin neden faydalı olduğunu göstermeye yardımcı olur . Gönderen bash(1)adam sayfası Özel Parametreler bölümünde: " *- genişleme çift tırnak içine gerçekleştiğinde, her parametre, [...] Yani edilir değeriyle tek bir kelime genişler "$*"eşdeğerdir için "$1c$2c...", nerede colduğunu [ $IFS]." Gerçekten de, ilk örneğiniz $*yerine kullanmak $@, ikinci sürümle aynı çıktıyı netleştirecektir.
FeRD

2
Şimdi bunu karşılaştırın "$@". Yine man sayfasından: " @- Genişletme çift tırnak içinde gerçekleştiğinde, her parametre ayrı bir kelimeye genişler. Yani "$@", eşdeğerdir "$1" "$2"... Çift tırnaklı genişletme bir sözcük içinde gerçekleşirse, ilk parametrenin genişletmesi birleştirilir orijinal kelimenin başlangıç ​​kısmı ile ve son parametrenin genişletilmesi orijinal kelimenin son kısmı ile birleştirilir. " ... Ve gerçekten, eğer kodunuz olsaydı bash -c "true foo $@ bar baz", test.sh one twonet olduğu gibi çalıştırırsınız bash -c 'true foo one' 'two bar baz'.
FeRD

1
Hakkında yapılan dokümanlar alıntı ve bilgi için teşekkürler $*, var olduğunu unutmak gibi görünüyor ..
ColinM

Heh. Ben tersiyim, $@sadece kabuk komut dosyası yazmaya başladığımda çekişiyordum, hala kendime orada olduğunu hatırlatmam gerekiyor. "$*"Komut dosyalarında kullanıldığını görmek yaygındı ... o zaman yazar, tüm argümanlarını bir araya getirdiğini fark edecekti, bu yüzden kelime bölme ile her türlü karmaşık saçmalığı deneyecek "$*"ya da bir arg listesini döngüye sokarak [yeniden] arayacaklardı. üzerinde shiftsadece kullanarak ... teker teker onları aşağı çekmeye $@çözer bunu. ( ${var[*]}${var[@]}
Bash'ın

Buradaki asıl sorun bash -c, kesinlikle mantıklı olmayan bir şekilde kullanmanızdır .
üçlü

4

Bazen (örneğin tüm argümanları geçmek istiyorum ama bir bayrak öncesinde --flag)

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

Bunu aşağıdaki şekilde yapabilirsiniz:

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

not: fazladan alan ayırmayı önlemek için, alıntı yapmanız %sve $@tek bir dizeye sahip olmaktan kaçınmak için alt kabuğunu alıntılayamazsınız printf.


3

Boşluklarınız veya kaçan karakterleriniz dışında iyi çalışır. Bu durumda argümanları yakalamanın ve komut dosyasının içindeki bir ssh'a yol açamıyorum.

Bu yararlı olabilir ama çok çirkin

_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

Burada pek çok yanıt öneriliyor $@ya $*da tırnaksız olarak öneriliyor , ancak hiçbiri bunların gerçekten ne yaptığını ve neden bu şekilde yapmanız gerektiğini açıklamıyor. Öyleyse bu cevaptaki bu mükemmel özeti çalmama izin verin :

resim açıklamasını buraya girin

Tırnakların tüm farkı yarattığını ve bunlar olmadan her ikisinin de aynı davranışa sahip olduğuna dikkat edin.

Benim amacım için, parametreleri bir komut dosyasından diğerine olduğu gibi geçirmem gerekiyordu ve bunun için en iyi seçenek:

# 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 $*

Hiçbir tırnak dikkat edin ve $@yukarıdaki durumda da çalışması gerekir.


2

bar "$@" eşdeğer olacak bar "$1" "$2" "$3" "$4"

Tırnak işaretlerinin önemli olduğuna dikkat edin!

"$@", $@, "$*"Veya $*bu anlatıldığı gibi kaçan ve birleştirme konusunda irade her davranır biraz farklı stackoverflow cevap .

Yakından ilişkili bir kullanım örneği, verilen tüm argümanları aşağıdaki gibi bir argümana geçirmektir:

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

Bunu başarmak için @ kvantour'un cevabının bir varyasyonunu kullanıyorum:

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.