bash betiğine dd stili parametreler


19

Paraşütleri bir bash senaryosuna, dd tarzı aktarmak istiyorum. Temel olarak, istiyorum

./script a=1 b=43

ile aynı etkiye sahip olmak

a=1 b=43 ./script

Ben bunu ile başarabilirim düşündüm:

for arg in "$@"; do
   eval "$arg";
done

evalGüvenli olmasını sağlamanın iyi bir yolu nedir , yani "$arg"statik (kod yürütme yok), değişken atamasıyla eşleşir?

Yoksa bunu yapmanın daha iyi bir yolu var mı? (Bunu basit tutmak istiyorum).


Bu, bash ile etiketlenir. Posix uyumlu bir çözüm ister misiniz, yoksa bash çözümlerini kabul eder misiniz?
rici

Etiketin söylediği şey demek istediğim :)
PSkocik

Bir =ayırıcı ile bir desen olarak ayrıştırabilir ve daha dikkatli bir şekilde oluşturulmuş bir eval ile ödevi yapabilirsiniz. Sadece güvenlik için, özel kullanım için, senin yaptığın gibi yapardım.
orion

Yanıtlar:


16

Bunu eval olmadan (ve yapay kaçış olmadan) bash'da yapabilirsiniz:

for arg in "$@"; do
  if [[ $arg =~ ^[[:alpha:]_][[:alnum:]_]*= ]]; then
    declare +i +a +A "$arg"
  fi
done

Düzenleme: Stéphane Chazelas bir yorum dayanarak declare, key=valbağımsız değişken değer kısmını değerlendirecek bir dizi durumda kaçınacak atanan değişken zaten bir dizi veya tamsayı değişkeni olarak bildirilmesini önlemek için bildirime bayrakları ekledi . (Ayarlanacak +adeğişken zaten bir dizi değişkeni olarak bildirilirse hataya neden olur.) Bu güvenlik açıklarının tümü, genellikle iyi bilinen mevcut (dizi veya tamsayı) değişkenleri yeniden atamak için bu sözdizimini kullanmakla ilgilidir. kabuk değişkenleri.

Aslında, bu sadece evaltemelli çözümleri eşit olarak etkileyecek bir enjeksiyon saldırıları sınıfının bir örneğidir : sadece bilinen argüman adlarına izin vermek, komut satırında hangi değişkenin mevcut olduğunu körü körüne ayarlamaktan çok daha iyi olurdu. ( PATHÖrneğin komut satırı ayarlanırsa ne olacağını düşünün . Veya bir PS1sonraki bilgi istemi ekranında gerçekleşecek bazı değerlendirmeleri dahil etmek için sıfırlar .)

Bash değişkenlerini kullanmak yerine, hem ayarlanması daha kolay hem de çok daha güvenli olan adlandırılmış bağımsız değişkenler dizisini kullanmayı tercih ederim. Alternatif olarak, gerçek bash değişkenlerini ayarlayabilir, ancak yalnızca adları meşru bağımsız değişkenlerin ilişkilendirilebilir bir dizisindeyse.

İkinci yaklaşıma örnek olarak:

# Could use this array for default values, too.
declare -A options=([bs]= [if]= [of]=)
for arg in "$@"; do
  # Make sure that it is an assignment.
  # -v is not an option for many bash versions
  if [[ $arg =~ ^[[:alpha:]_][[:alnum:]_]*= &&
        ${options[${arg%%=*}]+ok} == ok ]]; then
    declare "$arg"
    # or, to put it into the options array
    # options[${arg%%=*}]=${arg#*=}
  fi
done

1
Normal ifadenin parantezleri yanlış gibi görünüyor. Belki yerine bunu kullanın: ^[[:alpha:]_][[:alnum:]_]*=?
lcd047

1
@ lcd047: fooyu foo=boş dizgiye ayarlamanın tek yolu bu yüzden izin verilmelidir (IMHO). Parantezleri sabitledim, teşekkürler.
rici

3
declareeval( tehlikeli olduğu kadar belirgin olmadığı için daha da kötüsü bile söylenebilir). Örneğin, bunu 'DIRSTACK=($(echo rm -rf ~))'argüman olarak çağırmayı deneyin .
Stéphane Chazelas

1
@PSkocik: +x"değil -x". -a= dizinlenmiş dizi, -A= ilişkisel dizi, -i= tamsayı değişkeni. Böylece: dizinlenmemiş dizi değil, ilişkisel dizi değil, tamsayı değil.
lcd047

1
Bir sonraki sürümünde bileşik değişkenleri devre dışı bırakmak veya değişken olanları devre dışı bırakmak bashiçin eklemeniz gerekebileceğini unutmayın . Hala nerede durduğunu bildiğin yerde kullanacağım . +c+Feval
Stéphane Chazelas

9

Bir POSIX bir (setleri $<prefix>varyerine $varözel değişkenlerle önlemek sorunlarına mi IFS/ PATH...):

prefix=my_prefix_
for var do
  case $var in
    (*=*)
       case ${var%%=*} in
         "" | *[!abcdefghijiklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_]*) ;;
         (*) eval "$prefix${var%%=*}="'${var#*=}'
       esac
  esac
done

Olarak adlandırılır myscript x=1 PATH=/tmp/evil %=3 blah '=foo' 1=2, atar:

my_prefix_x <= 1
my_prefix_PATH <= /tmp/evil
my_prefix_1 <= 2

6

lcd047'nin çözümü sabit kodlu bir DD_OPT_önekle yeniden düzenlendi :

while [[ $1 =~ ^[[:alpha:]_][[:alnum:]_]*= ]]; do
  eval "DD_OPT_${1%%=*}"='${1#*=}'; shift;
done

frostschutz refactoring çoğu için kredi hak ediyor.

Ben küresel değişken olarak bir kaynak dosyaya koymak:

DD_OPTS_PARSE=$(cat <<'EOF'
  while [[ $1 =~ ^[[:alpha:]_][[:alnum:]_]*= ]]; do
    eval "DD_OPT_${1%%=*}"='${1#*=}'; shift;
  done
EOF
)

eval "$DD_OPTS_PARSE" tüm sihri yapar.

İşlevler için bir sürüm:

DD_OPTS_PARSE_LOCAL="${PARSE_AND_REMOVE_DD_OPTS/DD_OPT_/local DD_OPT_}"

Kullanımda:

eval "$DD_OPTS_PARSE_LOCAL"

Ben testler ve bir README.md ile tam bir repo yaptı . Sonra ben yazıyordum sarıcı bir Github API CLI bu kullanılan ve ben kurulum söylenen bir github klonu aynı sarıcı kullanılan repo (önyükleme eğlenceli).

Yalnızca tek bir satırda bash komut dosyaları için güvenli parametre iletimi. Zevk almak. :)


1
ancak *=*, = olmayan anahtar / val komutundan kurtulabilir ve değiştirmeyi durdurabilirsiniz. (yeniden düzenleme yaptığınızdan beri): P
frostschutz

1
Aslında, for döngüsü ve
if'den kurtulabilir

1
Heh, beyin fırtınasının işe yaradığının kanıtı. :)
lcd047

1
Hasat sabah fikirleri: hatta kurtulabilirsiniz keyve valve sadece yazma eval "${1%%=*}"=\${1#*=}. Ama bu eval "$1"@ rici'nin declare "$arg"işe yaramayacağı gibi, açıkçası gittiği kadarıyla . Ayrıca PATHveya gibi şeyleri ayarlamaya dikkat edin PS1.
lcd047

1
Teşekkür ederim - bunun değerlendirilen değişken olduğunu düşündüm. Bana gösterdiğiniz sabır için teşekkür ederim. Her neyse, hayır - tahmin edilenin dışında, iyi görünüyor. Gerçi bunu herhangi bir kabukta çalışmak için genişletebilirsiniz case. Muhtemelen önemli değil, ama bilmiyorsan ...
mikeserv

5

Klasik Bourne kabuğu destekleniyor ve Bash ve Korn kabuğu hala bir -kseçenek destekliyor . Geçerli olduğunda dd, komut satırında herhangi bir ' -like' komut seçeneği otomatik olarak komuta iletilen ortam değişkenlerine dönüştürülür:

$ set -k
$ echo a=1 b=2 c=3
$ 

Çevre değişkenleri olduğuna ikna etmek biraz daha zor; bu benim için çalışıyor:

$ set -k
$ env | grep '^[a-z]='   # No environment a, b, c
$ bash -c 'echo "Args: $*" >&2; env' a=1 b=2 c=3 | grep '^[a-z]='
Args: 
a=1
b=2
c=3
$ set +k
$ bash -c 'echo "Args: $*" >&2; env' a=1 b=2 c=3 | grep '^[a-z]='
Args: b=2 c=3
$

Birincisi env | grep, tek küçük harfli ortam değişkenleri göstermemektedir. Birincisi bash, üzerinden çalıştırılan betiğe argüman aktarılmadığını -cve ortamın üç tek harfli değişkeni içerdiğini gösterir. İptal set +keder -kve aynı komutun artık kendisine iletilen bağımsız değişkenlere sahip olduğunu gösterir. ( Senaryo için a=1olduğu gibi tedavi edildi $0; bunu da uygun yankı ile kanıtlayabilirsiniz.)

Bu, sorunun sorduğunu başarır - yazmanın yazmakla ./script.sh a=1 b=2aynı olması gerekir a=1 b=2 ./script.sh.

Bir betiğin içinde böyle hileler denerseniz sorun yaşarsınız:

if [ -z "$already_invoked_with_minus_k" ]
then set -k; exec "$0" "$@" already_invoked_with_minus_k=1
fi

"$@"Aynen muamele edilmektedir; atama stili değişkenleri (hem bashve hem de ksh) bulmak için yeniden analiz edilmez . Denedim:

#!/bin/bash

echo "BEFORE"
echo "Arguments:"
al "$@"
echo "Environment:"
env | grep -E '^([a-z]|already_invoked_with_minus_k)='
if [ -z "$already_invoked_with_minus_k" ]
then set -k; exec "$0" "$@" already_invoked_with_minus_k=1
fi

echo "AFTER"
echo "Arguments:"
al "$@"
echo "Environment:"
env | grep -E '^([a-z]|already_invoked_with_minus_k)='

unset already_invoked_with_minus_k

ve d komut dosyasında yalnızca already_invoked_with_minus_kortam değişkeni ayarlanır exec.


Çok güzel bir cevap! HOME değiştirilebilir olduğu halde PATH'ı değiştirmeyeceği ilginçtir, bu nedenle bu şekilde ayarlanması çok tehlikeli olabilecek env değişkenlerinin kara listesi (en azından PATH içeren) gibi bir şey olmalıdır. Bunun ne kadar kısa ve soruyu nasıl cevapladığını seviyorum, ancak sanitize + eval + önek çözümü ile devam edeceğim çünkü hala daha güvenli ve böylece daha evrensel olarak kullanılabilir (kullanıcıların çevreyi karıştırmasını istemediğiniz ortamlarda) ). Teşekkürler ve +1.
PSkocik

2

Girişimim:

#! /usr/bin/env bash
name='^[a-zA-Z][a-zA-Z0-9_]*$'
count=0
for arg in "$@"; do
    case "$arg" in
        *=*)
            key=${arg%%=*}
            val=${arg#*=}

            [[ "$key" =~ $name ]] && { let count++; eval "$key"=\$val; } || break

            # show time
            if [[ "$key" =~ $name ]]; then
                eval "out=\${$key}"
                printf '|%s| <-- |%s|\n' "$key" "$out"
            fi
            ;;
        *)
            break
            ;;
    esac
done
shift $count

# show time again   
printf 'arg: |%s|\n' "$@"

RHS'de (neredeyse) keyfi çöplerle çalışır:

$ ./assign.sh Foo_Bar33='1 2;3`4"5~6!7@8#9$0 1%2^3&4*5(6)7-8=9+0' '1 2;3`4"5~6!7@8#9$0 1%2^3&4*5(6)7-8=9+0=33'
|Foo_Bar33| <-- |1 2;3`4"5~6!7@8#9$0 1%2^3&4*5(6)7-8=9+0|
arg: |1 2;3`4"5~6!7@8#9$0 1%2^3&4*5(6)7-8=9+0=33|

$ ./assign.sh a=1 b=2 c d=4
|a| <-- |1|
|b| <-- |2|
arg: |c|
arg: |d=4|

İlk x = y parametresinde
döngüyü kırmazsanız

@frostschutz İyi bir nokta, düzenlendi.
lcd047

Genelleme iyi iş. Bence biraz basitleştirilebilir.
PSkocik

Düzenlememe bakma şansı buldunuz mu?
PSkocik

Lütfen düzenlememe bir göz atın. Bu benim hoşuma gitti (+ belki sadece shiftyerine shift 1). Aksi takdirde teşekkürler!
PSkocik

0

Bir süre önce aliasbu tür bir işe karar verdim . İşte başka bir cevabım:


Bununla birlikte, bazen bu tür açıklamaların değerlendirilmesini ve yürütülmesini ayırmak mümkün olabilir. Örneğin, aliasbir komutu önceden değerlendirmek için kullanılabilir. Aşağıdaki örnekte değişken tanımı, yalnızca $vardeğerlendirdiği değişken ASCII alfasayısal karakterleri veya _ ile eşleşmeyen bayt içermiyorsa başarıyla bildirilebilecek bir takma ada kaydedilir .

LC_OLD=$LC_ALL LC_ALL=C
for var do    val=${var#*=} var=${var%%=*}
    alias  "${var##*[!_A-Z0-9a-z]*}=_$var=\$val" &&
    eval   "${var##[0-9]*}" && unalias "$var"
done;       LC_ALL=$LC_OLD

evalburada yeni aliasbir alıntı varname bağlamından çağırmak için kullanılır - atama için tam olarak değil. Ve evalsadece önceki aliastanım başarılı olursa çağrılır ve birçok farklı uygulamanın takma adlar için birçok farklı türde değeri kabul edeceğini bilsem de, henüz tamamen boş olanı kabul edecek bir kabuk bulamadım .

_$varAncak takma ad içindeki tanım , hiçbir önemli ortam değerinin üzerine yazılmamasını sağlamak içindir. _ İle başlayan kayda değer çevre değerlerini bilmiyorum ve genellikle yarı özel beyan için güvenli bir bahis.

Her neyse, takma ad tanımı başarılı olursa, $vardeğeri için adlandırılmış bir takma ad bildirir . Ve evalbunu sadece aliasbir sayı ile başlamazsa çağırır - başka evalsadece boş bir argüman alır. Yani her iki şartlar yerine getirildiği takdirde evalçağrıları aliasve kaydedilen değişken tanımı aliasyeni takma derhal karma tablosundan çıkarılır bundan sonra yapılır.


Ayrıca aliasbu bağlamda yararlı olabilir, çalışmanızı yazdırabilirsiniz. istendiğinde aliasiki kez alıntılanan bir kabuk için güvenli ifadesi yazdırır .

sh -c "IFS=\'
    alias q=\"\$*\" q" -- \
    some args which alias \
    will print back at us

ÇIKTI

q='some'"'"'args'"'"'which'"'"'alias'"'"'will'"'"'print'"'"'back'"'"'at'"'"'us'
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.