Bash komut dosyası bağımsız değişkenlerini alt kabuğa geçirme


13

Bazı işler yapar ve sonra özgün parametreleri başka bir araca geçiren bir sarıcı komut dosyası var:

#!/bin/bash
# ...
other_tool -a -b "$@"

"Diğer araç" bir alt kabukta çalıştırılmadığı sürece, bu iyi çalışır:

#!/bin/bash
# ...
bash -c "other_tool -a -b $@"

Benim sarmalayıcı komut dosyası böyle çağırırsanız:

wrapper.sh -x "blah blup"

daha sonra "other_tool" a yalnızca ilk orjinal argüman (-x) verilir. Gerçekte, bir alt kabuk oluşturmuyorum, ancak orijinal argümanları bir Android telefonundaki bir kabuğa geçiriyorum, ki bu hiçbir fark yaratmamalıdır:

#!/bin/bash
# ...
adb sh -c "other_tool -a -b $@"

Yanıtlar:


14

Bash'in printfkomutunda, hem üst hem de alt kabuk aslında bash olduğu sürece, bir dize alıntı / kaçış / ne yapacak bir özelliği vardır:

[Düzenle: siegi'nin bir yorumda belirttiği gibi, bunu açıkça yaparsanız, hiçbir argüman sağlanmadığında bir sorun vardır, burada tek bir boş argüman varmış gibi davranır. Ben ${1+}sadece ilk argüman tanımlanmışsa biçim dizesini içeren , biçim dizesi ile kaydırma aşağıda bir geçici çözüm ekledim . Biraz kluge, ama işe yarıyor.]

#!/bin/bash

quoted_args="$(printf "${1+ %q}" "$@")" # Note: this will have a leading space before the first arg
# echo "Quoted args:$quoted_args" # Uncomment this to see what it's doing
bash -c "other_tool -a -b$quoted_args"

Tek bir satırda da yapabileceğinizi unutmayın: bash -c "other_tool -a -b$(printf "${1+ %q}" "$@")"


Teşekkür ederim! Bu benim için çok işe yaradı.
DribblzAroundU82

Bu, hiçbir argüman geçirilmediğinde doğru çalışmaz (argüman yerine tek bir boş argüman geçirir).
siegi

1
@siegi İyi yakalama; Bunun için bir çözüm ekledim.
Gordon Davisson

4

Çözümlerin hiçbiri iyi çalışmıyor. Sadece x / \ \ \ "b \" / aaaaa / \ 'xxx \ yyyy \' / zz \ ​​"offf \" parametresini iletin ve başarısız olurlar.

İşte her durumu ele alan basit bir ambalaj. Her bir argümanın iki kez nasıl kaçtığına dikkat edin.

#!/usr/bin/env bash
declare -a ARGS
COUNT=$#
for ((INDEX=0; INDEX<COUNT; ++INDEX))
do
    ARG="$(printf "%q" "$1")"
    ARGS[INDEX]="$(printf "%q" "$ARG")"
    shift
done

ls -l ${ARGS[*]}

1
$ @ '/ bin / foo' "$ @" '--opt' gibi alıntılanmış dize bağımsız değişkeninin bir parçası olarak $ @'ı birleştirmeye çalışıyorsanız ve bunun beklediğiniz gibi çıkmadığını keşfederseniz de yararlıdır .
jrg

1
Aman tanrım. Bu sorunla mücadele ediyor ve görünüşe göre hiçbir şey işe yaramadı. Bu çözdü. Bir şekilde bu sorunu çok gelir düşünüyor çift kaçar çağırmak için bir bash yerleşik olsaydı iyi olurdu.
MooGoo

2

Değişim $@için $*. Küçük bir yerel test yaptım ve benim durumumda çalışıyor.

#!/bin/sh
bash -c "echo $*"
bash -c "echo $@"

Farklı kaydetme test.shve yürütülebilir yapma özelliği

$ ./test.sh foo bar
foo bar
foo

Gördüğünüz gibi $*ve arasında ince bir fark var $@. Bkz. Örneğin http://ss64.com/bash/syntax-parameters.html


Yorumlarda yer alan takip sorusu için: örn test.sh. Bir wcsarmalayıcıda modifiye edilmiş olarak, bir ayırıcılı bir dizeyi birleşik argüman olarak geçirmek için örneğin "boşluk" iki kez "kaçmanız gerekir :

#!/bin/sh
bash -c "wc $*"

Bu çalışıyor:

$ touch test\ file
$ ./test.sh -l "test\ file"
0 test file

fakat:

$ ./test.sh -l "test file"
wc: test: No such file or directory
wc: file: No such file or directory
0 total

Ne yazık ki, bu da işe yaramıyor. wrapper.sh -x "blah blup"Sarıcıyı böyle çağırırsanız: alt kabuk İKİ yerine (-x, blah, blup) ÜÇ parametre (-x, blah, blup) alır
Ralf Holly

Alan ayırıcısının korunmasını istiyorsanız, argümanı "çift kaçmanız" gerekir "Blah\ blup". Deneyin ve çalışıp çalışmadığını görün.
Daniel Andersson

2
Çalıştığı gibi görünüyor, ancak, bu kaçışları eklemek için wrapper.sh arayan gerektirir ve şeffaf bir çözüm arıyorum.
Ralf Holly

2

Bir diziyi (konumsal parametreler) bir dizeye zorladığınız için başarısız oluyor. "$@"büyülü çünkü size her bir ayrı parametreyi uygun şekilde alıntılanmış bir dize olarak veriyor. Ek metin eklemek sihri kırar: "blah $@"sadece tek bir dizedir.

Bu sizi yakınlaştırabilir:

cmd="other_tool -a -b"
for parm in "$@"; do cmd+=" '$parm'"; done
adb sh -c "$cmd"

Tabii ki, tek bir alıntı içeren herhangi bir parametre soruna neden olacaktır.


2

Tamam, daha fazla açıklama:

$ cat /tmp/test.sh
#! /bin/bash

echo '$@='"$@"

set -v # "debug"

sh -c 'echo $@' "$@" # gremlins steal the first option

sh -c 'echo $@' -- "$@" # We defend the option with two knifes

$ bash -e /tmp/test.sh first second third
$@=first second third

sh -c 'echo $@' "$@" # gremlins steal the first option
second third

sh -c 'echo $@' -- "$@" # We defend the option with two knifes
first second third

1

Birçok farklı çözüm denedim, arka plan bilgileri ve alternatifler de dahil olmak üzere iyi bir kaynak, örneğin Greg'in (aka. GreyCat'ın) Wiki'sinde BashFAQ / 096 . Toplamda aşağıdaki ikisinin en okunabilir olduğunu gördüm (çalışanlardan):


Bash 4.4'ten beri ( HABERLERDEN anlatabildiğim kadarıyla ) parametre genişletmeyi şu şekilde kullanmak mümkündür @Q:

adb sh -c "other_tool -a -b ${*@Q}"

$*Burada yerine kullanılan her argüman için bir dize yerine tek bir dize olmasını $@istediğiniz "other_tool -a -b ${*@Q}"için kullanın.

Aynı şeyi bir bash dizi değişkeni ile yapmak istiyorsanız sözdizimine ${ARRAY[*]@Q}(tırnak içinde) ihtiyacınız olacaktır .


Eğer varsa Bash 4.4 veya daha sonra mevcut olmayan veya emin değilseniz, bu benim tercih çözümdür:

function escapeBashArgs() {
    local arg separator=""
    for arg
    do
        printf "%s%q" "$separator" "$arg"
        separator=" "
    done
}

adb sh -c "other_tool -a -b $(escapeBashArgs "$@")"

Not Burada bu kullanmak gerekmez "$@"yerine $@veya "$*"veya $*kullanılamaz alıntı olmadan varyantları böylece, argümanlar içinde kelime bölme istemiyoruz ve bunu, argüman sayısı korunacak istiyorum, çünkü "$*"o katılacağını olarak kullanılamaz tüm argümanlar tek bir dizeye. İşlev daha sonra tüm bağımsız değişkenleri tek bir dizede döndürür.

İlk bağımsız değişkenin önündeki ek boşluğu önemsemiyorsanız, printfbiçim dizesini değişken olarak değiştirebilir " %q"ve kaldırabilirsiniz separator. Veya Gordon Davissons'ın cevabındaki tek astarı kullanabilirsiniz .


Bu çözümler, özellikle karşılaşabileceğim tüm vakalarla çalışır:

  • Argüman yok: escapeBashArgshiçbir şey
  • Boş argümanlar: escapeBashArgs "" ""'' ''
  • Sadece boşluklu argümanlar: escapeBashArgs " " " "' ' ' 'veya \ \ \ \ \( son alan bu site oluşturucu tarafından yenilir )
  • Özel boşluk ve yeni satır içeren bağımsız değişkenler: escapeBashArgs "a b" c\ d "arg with newline"'a b' 'c d' $'arg with\nnewline'veya a\ \ \ \ \ \ b c\ d $'arg with\nnewline'( satırsonu arasında withve newlinediğer konumlarda bu sitedeki satır kaydırma nedeniyle )
  • Özel karakterli bağımsız değişkenler: escapeBashArgs '$"'\''({:})''$"'\''({:})'veya\$\"\'\(\{:\}\)
  • Jcayzacs cevap örneği : escapeBashArgs x/\ \ \"b\"/aaaaa/\'xxx\ yyyy\'/zz\"offf\"'x/ "b"/aaaaa/'\''xxx yyyy'\''/zz"offf"'veyax/\ \ \"b\"/aaaaa/\'xxx\ yyyy\'/zz\"offf\"

(GNU bash 5.0.3 (1) -release ile test edilmiştir.)


-2
#! /bin/bash
sh -c 'other_tool -a -b "$@"' -- "$@"

3
Süper Kullanıcıya Hoş Geldiniz! Bazı açıklama ve bağlam sağlayan uzun cevaplar arıyoruz. Sadece tek satırlık bir cevap vermeyin; cevabınızın neden doğru olduğunu, ideal olarak alıntılarla açıklayın. Açıklama içermeyen yanıtlar kaldırılabilir.
G-Man '
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.