Başka bir değişkene bağlı olarak bir değişkeni nasıl kısaca farklı değerler atayabilirim?


20

Bu kabuk betiğini nasıl kısaltabilirim?

CODE="A"

if test "$CODE" = "A"
then
 PN="com.tencent.ig"
elif test "$CODE" = "a"
 then
 PN="com.tencent.ig"
elif test "$CODE" = "B"
 then
 PN="com.vng.pubgmobile"
elif test "$CODE" = "b"
 then
 PN="com.vng.pubgmobile"
elif test "$CODE" = "C"
 then
 PN="com.pubg.krmobile"
elif test "$CODE" = "c"
 then
 PN="com.pubg.krmobile"
elif test "$CODE" = "D"
 then
 PN="com.rekoo.pubgm"
elif test "$CODE" = "d"
 then
 PN="com.rekoo.pubgm"
else
 echo -e "\a\t ERROR!"
 echo -e "\a\t CODE KOSONG"
 echo -e "\a\t MELAKUKAN EXIT OTOMATIS"
 exit
fi

2
Sanırım bu bashkod mu? Yoksa aklınızda başka bir kabuk var mı?
Freddy

3
Gelecekte FYI, URL'ler ve diğer şeyler gibi kişisel bilgileri "com.hello.world" gibi genel bir şeyle değiştirmenizi tavsiye ederim.
Trevor Boyd Smith

1
@IISomeOneII Bunun yerine CodeGolf.SE soruyor olmalısınız: P
mackycheese21

3
@Trevor, tavsiye ederim example.org, example.netvb., Çünkü bu alanlar RFC 2606'da bu amaç için özel olarak ayrılmıştır ve asla gerçek varlıklar için kullanılmayacaktır.
Toby Speight

2
@TrevorBoydSmith Seconding Toby'nin "hello.com" Google'a ait olduğu için com.example vb. Önerisi.
David Conrad

Yanıtlar:


61

Bir caseifade kullanın (taşınabilir, herhangi bir shkabukta çalışır ):

case "$CODE" in
    [aA] ) PN="com.tencent.ig" ;;
    [bB] ) PN="com.vng.pubgmobile" ;;
    [cC] ) PN="com.pubg.krmobile" ;;
    [dD] ) PN="com.rekoo.pubgm" ;;
    * ) printf '\a\t%s\n' 'ERROR!' 'CODE KOSONG' 'MELAKUKAN EXIT OTOMATIS' >&2
        exit 1 ;;
esac

Ben de (gibi tüm büyük harflerle adresinin değişken adlarını değiştirme öneriyoruz CODEşey düşük- veya karma durumda (benzerine) codeveya Code). Özel anlamlara sahip birçok büyük harf adı vardır ve bunlardan birini yanlışlıkla kullanmak sorun yaratabilir.

Diğer notlar: Standart kural, "standart çıktı" yerine "standart hata" ya hata mesajları göndermektir; >&2yönlendirme yapar. Ayrıca, bir komut dosyası (veya program) başarısız olursa, sıfır dışında bir durumla ( exit 1) çıkmak en iyisidir , bu nedenle herhangi bir çağrı içeriği neyin yanlış gittiğini söyleyebilir. Farklı sorunlara işaret (bir "ÇIKIŞ KODLARI" bölümüne bakın farklı durumları kullanmak da mümkündür adam sayfasından iyi örneğin). (Burada öneriler için Stéphane Chazelas ve Monty Harder'a teşekkür ederiz.)curl

İşletim sistemi, sürümler, ayarlar, vb. Arasında daha taşınabilir olduğu için (ve ) printfyerine öneririm . Bir işletim sistemi güncellemesi, farklı seçeneklerle derlenmiş bir bash sürümünü içerdiğinden, davranış biçimini değiştirdi .echo -eecho -necho

Buradaki çift tırnaklara $CODEgerçekten ihtiyaç duyulmaz. A'daki dize, caseonları bırakmanın güvenli olduğu birkaç bağlamdan biridir. Bununla birlikte, belirli referanslar için özel bir neden olmadıkça değişken referansları iki kez tercih etmeyi tercih ederim, çünkü nerede güvenli ve nerede olmadığını takip etmek zor, bu yüzden alışkanlık olarak iki kez alıntı yapmak daha güvenlidir.


5
@IISomeOneII Bu sayı olarak sayılır *(ve hatayı yazdırır) - desen [aA]"a" veya "A" ile eşleşir, ancak her ikisiyle aynı anda eşleşmez.
Gordon Davisson

6
Bu tam olarak doğru şekilde, sonunda joker karaktere kadar çıkışını stderr'a yönlendiriyor ve sıfırdan farklı bir çıkış değeri üretiyor. Değiştirilmesi gerekebilecek tek şey, geri döndürülecek birden fazla hata olabileceğinden çıkış değeridir. Daha büyük bir komut dosyasında, çıkış değerlerini tanımlayan bir bölüm (belki de başka bir dosyadan alınmıştır) readonly Exit_BadCode=1olabilir exit $Exit_BadCode.
Monty Harder

2
Yeni bir bash ile gidiyorsanız, o zaman kullanın case "${CODE,}" in, böylece koşulların her biri basitleşir a), b)vb
steve

2
@MontyHarder Değişir. Bu kodlardan her biri bir dizeye karşılık gelen birkaç yüz varsa, başka bir yaklaşım daha iyi olabilir. Eldeki kesin sorun için bu yeterlidir.
Kusalananda

2
@MontyHarder Üzgünüm, daha net olmalıydım. "Kod" ile kastediyorum $CODE. Ben her zaman "çıkış durumu" tam olarak, sadece "kod" diyoruz. Betiğin dizelere başvurmak için yüzlerce anahtar kullanması gerekiyorsa , bir caseifade kullanmak gereksiz hale gelir.
Kusalananda

19

bashSürüm 4.0 veya daha yenisini kullandığınızı varsayarsak ...

CODE=A

declare -A domain

domain=(
   [a]=com.tencent.ig
   [b]=com.vng.pubgmobile
   [c]=com.pubg.krmobile
   [d]=com.rekoo.pubgm
)

PN=${domain[${CODE,,}]:?ERROR! CODE KOSONG, MELAKUKAN EXIT OTOMATIS}

Kodda, her biri tek harfli küçük harf anahtarıyla ilişkili tüm etki alanı adlarını içeren ilişkilendirilebilir bir dizi tanımlarım.

$PNDeğişken alt kasalı tekabül alan adı atanır $CODE(değer ${CODE,,}döndürür değerini $CODEsadece küçük harfler dönüştü) bu diziden ama eğer $CODEgeçerli bir girişe karşılık gelmiyor domainlistede, bir komut dosyasını çıkar hata.

${variable:?error message}Parametre ikame değerine artıracağı $variable(kodda uygun etki) ama değeri mevcut boş değilse, hata iletisi ile komut çıkış olacaktır. Hata iletisinin kodunuzdaki biçimiyle tam olarak aynı biçimlendirmesini almazsınız, ancak geçersizse esasen aynı davranır$CODE :

$ bash script.sh
script.sh: line 12: domain[${CODE,,}]: ERROR! CODE KOSONG, MELAKUKAN EXIT OTOMATIS

Karakter sayısını önemsiyorsanız, bunu daha da kısaltabiliriz:

CODE=A
declare -A domain=( [a]=tencent.ig [b]=vng.pubgmobile [c]=pubg.krmobile [d]=rekoo.pubgm )
PN=com.${domain[${CODE,,}]:?ERROR! CODE KOSONG, MELAKUKAN EXIT OTOMATIS}

Gereksiz yeni satırları silmenin yanı sıra, com.her alan adından da kaldırdım (bunun yerine, atamaya eklenir PN).

Yukarıdaki tüm kodların çok karakterli bir değer için bile çalışacağını unutmayın $CODE( domaindizide bunlar için daha düşük kasalı anahtarlar varsa ).


Eğer $CODE(sıfır tabanlı) dizin yerine sayısal oldu bu kodu biraz basitleştirmek olacaktır:

CODE=0

domain=( com.tencent.ig com.vng.pubgmobile com.pubg.krmobile com.rekoo.pubgm )
PN=${domain[CODE]:?ERROR! CODE KOSONG, MELAKUKAN EXIT OTOMATIS}

Bu, domaindiziyi her satırda bir giriş içeren yardımcı bir dosyadan okumayı gerçekten kolaylaştıracaktır :

CODE=0

readarray -t domain <domains.txt
PN=${domain[CODE]:?ERROR! CODE KOSONG, MELAKUKAN EXIT OTOMATIS}

1
@IISomeOneII declare -A domainbunun domainilişkisel dizi ("hash") değişkeni olması gerektiğini söylüyor .
Kusalananda

1
@Isaac Artık sizinkinden daha farklı. Söylediğin için teşekkürler.
Kusalananda

1
Zsh veya ksh93 kullanmak daha iyi olur. Bash için son bir sürüme ihtiyacınız vardır ve boş değerleri için başarısız olur $CODE.
Stéphane Chazelas

1
@ StéphaneChazelas Evet, $CODEayarlanmamış veya boş olsaydı , kötü dizi alt dizisi hakkında fazladan bir hata iletisi alırsınız , ancak yine de doğru özel hata iletisini oluşturur.
Kusalananda

1
@Kusalananda Yeni bir (geçerli POSIX) komut dosyası gönderildi. Hata olmadan kontrol çok kısa.
Isaac

11

Kabuğunuz dizilere izin veriyorsa, en kısa yanıt bash'daki bu örnek gibi olmalıdır:

declare -A site
site=( [a]=com.tencent.ig [b]=com.vng.pubgmobile [c]=com.pubg.krmobile [d]=com.rekoo.pubgm )

pn=${site[${code,}]}

Bu $codesadece a, b, c veya d olabileceğini varsayar .
Değilse, aşağıdakine benzer bir test ekleyin:

case ${site,} in
    a|b|c|d)        pn=${site[${code,}]};;
    *)              pn="default site"
                    printf '\a\t %s\n' 'ERROR!' 'CODE KOSONG' 'MELAKUKAN EXIT OTOMATIS'
                    exit 1
                    ;;
esac

Giriş A ise, bu komut dosyasında çalışır mı? Üzgünüm benim İngiliz kötü
IISomeOneII

2
Evet, genişletme ${var,}, ilk karakterini küçük harfe dönüştürür ${var}. @IISomeOneII
Isaac

1
${var,}Bash'a özgü gibi görünüyor. Ben ilişkisel dizi ksh ve zsh de işe yarayacağını düşünüyorum
ilkkachu

@ilkkachu Evet, her iki açıdan da düzeltin.
Isaac

Herkese teşekkürler, Burada bir sürü iyi insan 👍
IISomeOneII

3

Bu cevabı farklı bir yöne götüreceğim. Verilerinizi komut dosyasına kodlamak yerine, bu verileri ayrı bir veri dosyasına koyun, ardından dosyayı aramak için kodu kullanın:

$ cat names.cfg 
a com.tencent.ig
b com.vng.pubgmobile
c com.pubg.krmobile
d com.rekoo.pubgm

$ cat lookup.sh
PN=$(awk -v code="${1:-}" 'tolower($1) == tolower(code) { print $2; }' names.cfg)
if [ -z "${PN}" ]; then
  printf '\a\t%s\n' 'ERROR!' 'CODE KOSONG' 'MELAKUKAN EXIT OTOMATIS' >&2
  exit 1
fi
echo "${PN}"

$ bash lookup.sh A
com.tencent.ig
$ bash lookup.sh a
com.tencent.ig
$ bash lookup.sh x
    ERROR!
    CODE KOSONG
    MELAKUKAN EXIT OTOMATIS

Bu endişelerin ayrılmasının birkaç faydası vardır:

  • Kod mantığı üzerinde çalışmak zorunda kalmadan verileri kolayca ve basit bir şekilde ekleyin ve kaldırın.
  • Diğer programlar belirli bir alt alanda kaç eşleşme olduğunu saymak gibi verileri yeniden kullanabilir.
  • Eğer bir varsa büyük verilerin listesini, disk ve kullanımı hakkında sıralayabilir lookverimli ikili arama o (çizgi yerine-line-by için grepya awk)

1
Bu şekilde giderseniz, yine PNde doğru değere ayarlanacak şekilde ayarlamanız gerekir.
ilkkachu

1
@ilkkachu Adil nokta. OP'de bunu kaçırdım. Düzeltildi.
bishop

2
Verileri koddan ayırmak için +1.
arp

1

Değerleri indekslemek için harfler kullanıyorsunuz, eğer sayıları kullanacaksanız, bu kadar basit olur:

code=1
set -- com.tencent.ig com.vng.pubgmobile com.pubg.krmobile com.rekoo.pubgm

eval pn\=\${"$code"}

Bu taşınabilir kabuk kodu, çoğu kabuk üzerinde çalışacaktır.
Bash için: pn=${!code}veya bash / ksh / zsh için: kullanabilirsiniz pn=${@:code:1}.

harfler

Harfleri kullanmanız gerekiyorsa (a'dan z'ye veya A'dan Z'ye), bunların bir dizine dönüştürülmesi gerekir:

code=a                              # or A, B, C, ... etc.
set -- com.tencent.ig com.vng.pubgmobile com.pubg.krmobile com.rekoo.pubgm
eval pn\=\"\${$(( ($(printf '%d' "'$code")|32)-96  ))}\"

Her parçanın amacını ve anlamını açıklığa kavuşturmak için daha uzun bir kodda:

code=A

set -- com.tencent.ig com.vng.pubgmobile com.pubg.krmobile com.rekoo.pubgm

asciival=$(( $(printf '%d' "'$code") ))      # byte value of the ASCII letter.
upperval=$(( asciival |  32 ))               # shift to uppercase.
indexval=$(( upperval -  96 ))               # convert to an index from a=1.
eval arg\=\"\$\{$indexval\}\"                # the argument at such index.

Küçük harfli değerlere dönüştürmeniz gerekiyorsa, şunu kullanın: $(( asciival & ~32 ))(ascii değerinin 6. bitinin ayarlanmadığından emin olun).

hata kodu

Komut dosyanızın bir hata üzerine yazdırdığı çıktı oldukça uzun (ve özellikle).
Bununla baş etmenin en çok yönlü yolu bir işlev tanımlamaktır:

errorcode(){ exitcode=$1; shift; printf '\a\t %s\n' "$@"; exit "$exitcode"; }

Ve sonra bu işlevi ihtiyacınız olan belirli mesajlarla çağırın.

errorcode 27  "ERROR!" "CODE KOSONG" "MELAKUKAN EXIT OTOMATIS"

Ortaya çıkan çıkış değerinin exitcode(örnek burada 27'dir) verildiğine dikkat edin .

Daha sonra tam bir komut dosyası (hata denetimli):

errorcode(){ exitcode=$1; shift; printf '\a\t %s\n' "$@"; exit "$exitcode"; }

code=${1:-A}

case "$code" in 
    [a-d]|[A-D]) : ;;
    *)           errorcode 27  "ERROR!" "CODE KOSONG" "MELAKUKAN EXIT OTOMATIS" ;;
esac

set -- com.tencent.ig com.vng.pubgmobile com.pubg.krmobile com.rekoo.pubgm
eval pn\=\"\${$(( ($(printf '%d' "'$code") & ~32) - 64  ))}\"

printf 'Code=%s Argument=%s\n' "$code" "$pn"
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.