Bir kabuk betiği, kabuk istemine yazdığınız gibi aktarılan argümanını yazdırabilir mi?


9

Bir kabuk komut dosyasında, benim anlayışım, "$@"komut dosyası bağımsız değişkenlerine genişleyerek, bunları gerektiği gibi alıntılamaktır. Örneğin, bu komut dosyası argümanlarını gcc'ye iletir:

gcc -fPIC "$@"

Bash-to-stdin sözdizimini kullanırken <<<, "@$"beklediğim gibi çalışmıyor.

#!/bin/bash
cat <<< "$@"

Olarak senaryoyu çağırma ./test.sh foo "bar baz"verir

foo bar baz

Beklemek isterim

foo "bar baz"

Kabuk komut isteminde yazdığınız gibi argümanlarını yazdıran bir kabuk komut dosyası yazmanın bir yolu var mı? Örneğin: ipucundaki kod bağımsız değişkenleri de dahil olmak üzere bir sonraki komutun nasıl kullanılacağına dair bir ipucu.

Yanıtlar:


5

Eh, "$@"konumsal parametre listesine genişler, konumsal parametre başına bir argüman.

Yaptığınızda:

set '' 'foo bar' $'blah\nblah'
cmd "$@"

cmdşu 3 argümanla çağrılıyor: boş dize foo barve blah<newline>blah. Kabuk, execve()sistem çağrısını şöyle bir şeyle çağırır:

execve("/path/to/cmd", ["cmd", "", "foo bar", "blah\nblah"], [envvars...]);

Aynı çağrıyı yeniden oluşturacak bir kabuk komut satırını (kabuk dilinde kod) yeniden oluşturmak istiyorsanız, şöyle bir şey yapabilirsiniz:

awk -v q="'" '
  function shellquote(s) {
    gsub(q, q "\\" q q, s)
    return q s q
  }
  BEGIN {
    for (i = 1; i < ARGC; i++) {
      printf "%s", sep shellquote(ARGV[i])
      sep = " "
    }
    printf "\n"
  }' cmd "$@"

Veya zshfarklı teklif türlerini sormakla:

set '' 'foo bar' $'blah\nblah'
$ print -r -- cmd "${(q)@}"
cmd '' foo\ bar blah$'\n'blah
$ print -r -- cmd "${(qq)@}"
cmd '' 'foo bar' 'blah
blah'
$ print -r -- cmd "${(qqq)@}"
cmd "" "foo bar" "blah
blah"
$ print -r -- cmd "${(qqqq)@}"
cmd $'' $'foo bar' $'blah\nblah'

Veya zsh, bashveya ksh93(burada bash, diğer kabuklarla YMMV için ):

$ set '' 'foo bar' $'blah\nblah'
$ printf cmd; printf ' %q' "$@"; printf '\n'
cmd '' foo\ bar $'blah\nblah'

Kabuğun yürüteceklerini yazdırmasına neden olan kabuğun xtrace seçeneğini de kullanabilirsiniz:

(PS4=; set -x; : cmd "$@")
: cmd '' 'foo bar' 'blah
blah'

Yukarıda, :no-op komutunu cmdve konum parametrelerini argüman olarak çalıştırdık. Kabuğum onları kabuğa çıkma için uygun güzel bir şekilde bastırdı. Bütün mermiler bunu yapmaz.


5
`"$@"` expands to the script arguments, quoting them as needed

Hayır, olan bu değil. Bir programı çağırmak , her argümanın bir dize olduğu bir argüman listesi alır . Eğer kabuk programını çalıştırdığınızda ./test.sh foo "bar baz": Bu üç argüman ile çağrı kurar ./test.sh, foove bar baz. (Sıfırıncı argüman program adıdır; bu, programların hangi adla adlandırıldığını bilmelerini sağlar.) Alıntı, program çağrılarının bir özelliği değil, kabuğun bir özelliğidir. Kabuk, çağrı yaptığında bu listeyi oluşturur.

"$@"komut dosyasına veya işleve iletilen bağımsız değişkenlerin listesini, çağrının kullanıldığı çağrıdaki bağımsız değişkenler listesine doğrudan kopyalar. Bu listelerde kabuk ayrıştırma yapılmadığından herhangi bir alıntı yapılmamaktadır.

İçinde cat <<< "$@", "$@"tek bir dizenin gerekli olduğu bir bağlamda kullanıyorsunuz . <<<Operator` değil, bir dize dizeleri listesini gerektirir. Bu bağlamda, bash listenin öğelerini alır ve aralarındaki boşlukla birleştirir.

Komut dosyası hata ayıklaması için, çalıştırırsanız set -x( set +xkapatmak için), her komutun daha önce yazdırıldığı bir izleme modunu etkinleştirir. Bash'da bu izlemede, komutu tekrar kabuğa yapıştırmayı mümkün kılan tırnak işaretleri bulunur (bu, her shuygulama için geçerli değildir ).

Bir dizeniz varsa ve orijinal dizeye geri ayrıştırılan kabuk kaynağı sözdizimine dönüştürmek istiyorsanız, onu tek tırnaklarla çevreleyebilir ve dizenin içindeki her tek tırnakla değiştirebilirsiniz '\''.

for x do
  printf %s "'${x//\'/\'\\\'\'}' "
done
echo

Dize değiştirme sözdizimi ksh93 / bash / zsh / mksh'ye özgüdür. Düz sh'de, dize üzerinde döngü yapmanız gerekir.

for raw do
  quoted=
  while case "$raw" in *\'*) true;; *) false;; esac; do
    quoted="$quoted'\\''${raw%%\'*}"
    raw="${raw#*\'}"
  done
  printf %s "'$quoted$raw' "
done
echo

2

"$@" kod bağımsız değişkenlerine genişler ve gerektiğinde alıntılanır

İyi sıralama. Pratik amaçlar için yakın yeterli olmalı ve bu referans kitabı yazıyor"$@" is equivalent to "$1" "$2" ...

Yani, iki parametre foove bar bazbunlar birbirine benzeyecektir:

echo "$@"
echo "$1" "$2"
echo "foo" "bar baz"

(Parametreler yalnızca düz dizeler yerine özel karakterler içeriyorsa, genişletildikten sonra yeniden genişletilmeyecek $@ve $1...)

Ancak $@tırnak işaretleri içindeki parametreler ile değiştirilmeyi düşünsek bile, tırnak işaretleri echogörmek için orada olmazdı , buna benzer şekilde gcctırnak işaretleri de alamaz.

<<<istisna biraz edilir "$@"== "$1" "$2" ...kural açıkça oluyor belirtilen bu The result is supplied as a single string to the command on its standard inputparametre ve değişken genişleme ve diğerleri arasında tırnak kaldırma geçiyor sonra. Yani her zamanki gibi, <<< "foo"sadece foogirdi olarak verir , aynı şekilde somecmd "foo"sadece fooargüman olarak verir .

Senaryo olarak adlandırmak ./test.sh foo "bar baz"[...] beklenir foo "bar baz"

Alıntılar kalırsa, yine de olması gerekirdi "foo" "bar baz". Kabuk veya çalışan herhangi bir komut, komut çalıştırıldığında alıntılamanın ne olduğu hakkında hiçbir fikre sahip değildir. Ya da konuşulacak bir alıntı olsa bile, sistem çağrısı sadece boş sonlandırılmış dizelerin bir listesini alır ve tırnaklar sadece kabuk dilinin bir özelliğidir. Diğer dillerde başka sözleşmeler de olabilir.


0

Bash için alternatif bir çözüm

q='"'; t=( "${@/#/$q}" ); u=( "${t[@]/%/$q}" ); echo ${u[@]}

Bash yuvalanmış ikameleri desteklemez, bu nedenle bir dizinin nasıl yeniden atanacağını gösteren /programming/12303974/assign-array-to-variable#12304017 sayesinde . Diziler, genişletme ve kalıp değiştirme ile ilgili ayrıntılar için man bash( https://linux.die.net/man/1/bash ) bölümüne bakın ( parametre genişletmesi altında).

analiz

Bash, komut satırı parametrelerini bir dizi olarak $@

q tırnak karakteri tutar.

Parametre genişletme etrafındaki çift tırnak işaretleri ${ ... }ayrı parametreleri ayrı elemanlar olarak korur ve bunları sarmak ( )bir değişkene dizi olarak atamanızı sağlar.

/#/$qbir parametre genişlemesinde, desenin başlangıcını (normal ifade gibi ^) tırnak işareti ile değiştirir.

/%/$qbir parametre genişlemesinde, desenin sonunu (normal ifade gibi $) tırnak işareti ile değiştirir.

Kullanım örneği: komut satırından e-posta adreslerinin listesi için MySQL'i sorgulama

Farklı bir alıntı karakteri kullanmak, parametreler arasına virgül eklemek ve son virgülünü kaldırmak için yukarıdaki ifadelerde birkaç değişiklik vardır. Ve elbette şifreyi mysql çağrısına koyarak kötü oluyorum. Beni dava et.

q="'"; t=( "${@/#/$q}" ); u="${t[@]/%/$q,}"; v="u.email in( ${u%,} )"
mysql -uprod_program -h10.90.2.11 -pxxxxxxxxxxxx my_database <<END
select ...
from users u
join ....
where $v # <<<<<<<<<<<<<<<<<< here is where all the hard work pays off :-)
group by user_id, prog_id
;
END

Sadece çift tırnak ile alıntı $yapmanın ters eğik çizgileri, genişleme ve diğer çift tırnakları korumada çok yararlı olmadığını unutmayın . Bu nedenle, diğer yanıtlar , dizenin içindeki tek tırnakları işlemek için bazı uzunluklara giderken tek tırnak kullanır veya dizenin alıntılanmış bir kopyasını oluşturmak için kabuğun kendi özelliklerini kullanır.
ilkkachu

@ilkkachu usulüne uygun kaydedildi! Bu yüzden (diğer nedenlerin yanı sıra) önceki tüm cevapları iptal ettim. Ayrıca neden bu kullanım durumunu ekledim. Umarım mükemmel olan, iyinin düşmanı değildir.
Jeff
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.