Bağımsız değişkenli veya bağımsız değişkenli bir komut dosyası nasıl yazılır?


13

Ben böyle bir şey gider bir bash komut dosyası var:

#!/bin/bash

if [ $1 = "--test" ] || [ $1 = "-t" ]; then
    echo "Testing..."
    testing="y"

else
    testing="n"
    echo "Not testing."

fi

Yani ne yapabilmek istiyorum sadece ./script --testveya ile değil ./script -t, aynı zamanda hiçbir argüman (sadece ./script) ile çalıştırmak, ama görünüşte ben geçerli kod ile bunu yaparsanız çıkış sadece:

./script: line 3: [: =: unary operator expected
./script: line 3: [: =: unary operator expected
Not testing.

Peki, hiçbir argüman elseolmadan çalıştırmanın sadece hatayı atmadan yapmasını sağlayacak şekilde nasıl programlayabilirim ? Neyi yanlış yapıyorum?


1
Çeklerinizin etrafında çift parantez gerekir. [[]]
Terrance


@Terrance onlara ihtiyacınız yok, ancak bash'ı hedefliyorsanız onları kullanmalısınız.
Ruslan

Yanıtlar:


1

Kabuk komut dosyalarında uzun seçenekleri kullanmanın "doğru yolu" getopt GNU yardımcı programıdır . Yerleşik bir bash olan getopts da var , ancak sadece kısa seçeneklere izin veriyor -t. Birkaç getoptkullanım örneği burada bulunabilir .

İşte sorunuza nasıl yaklaşacağımı gösteren bir senaryo. Çoğu adımın açıklaması komut dosyasının içinde yorum olarak eklenir.

#!/bin/bash

# GNU getopt allows long options. Letters in -o correspond to
# comma-separated list given in --long.

opts=$(getopt -o t --long test -- "$*")
test $? -ne 0 && exit 2 # error happened

set -- $opts # some would prefer using eval set -- "$opts"
# if theres -- as first argument, the script is called without
# option flags
if [ "$1" = "--"  ]; then
    echo "Not testing"
    testing="n"
    # Here we exit, and avoid ever getting to argument parsing loop
    # A more practical case would be to call a function here
    # that performs for no options case
    exit 1
fi

# Although this question asks only for one 
# option flag, the proper use of getopt is with while loop
# That's why it's done so - to show proper form.
while true; do
    case "$1" in
        # spaces are important in the case definition
        -t | --test ) testing="y"; echo "Testing" ;;
    esac
    # Loop will terminate if there's no more  
    # positional parameters to shift
    shift  || break
done

echo "Out of loop"

Birkaç basitleştirme ve kaldırılan yorum ile bu, aşağıdakilere yoğunlaştırılabilir:

#!/bin/bash
opts=$(getopt -o t --long test -- "$*")
test $? -ne 0 && exit 2 # error happened
set -- $opts    
case "$1" in
    -t | --test ) testing="y"; echo "Testing";;
    --) testing="n"; echo "Not testing";;
esac

16

Birkaç yol; en belirgin ikisi:

  • $1Çift tırnak işareti koyun :if [ "$1" = "--test" ]
  • $ # Kullanarak bağımsız değişken sayısını kontrol edin

Daha da iyisi, getopts kullanın.


2
Getopts kullanmak için +1. Bu en iyi yol.
Seth

3
Bir başka yaygın deyim, [ "x$1" = "x--test" ]komut için geçerli işleçler olan [komut satırı bağımsız değişkenlerini yeniden savunmaktır . (Bu [[ ]]tercih edilmesinin bir başka nedenidir : sadece yerleşik bir komutun değil, kabuk sözdiziminin bir parçasıdır.)
Peter Cordes

7

Değişkenlerinizi if koşulu içinde belirtmeniz gerekir. Değiştir:

if [ $1 = "--test" ] || [ $1 = "-t" ]; then

ile:

if [ "$1" = '--test' ] || [ "$1" = '-t' ]; then  

Sonra işe yarayacak:


. ~ ./Test.sh Test yapmıyor .

Her zaman değişkenlerinizi her zaman iki katına alın!


Tek tırnaklar gerekli mi yoksa bunun yerine çift tırnak kullanılabilir mi?

Kesin cevap hangi kabuğu kullandığınıza bağlıdır, ancak sorunuzdan Bourne-shell soyundan kullandığınızı varsayıyorum. Tek ve çift tırnaklar arasındaki temel fark, değişken genişlemenin çift tırnak içinde gerçekleşmesi, ancak tek tırnak içinde olmamasıdır: $ FOO = bar $ echo "$ FOO" bar $ echo '$ FOO' $ FOO (hmm .... can ' t biçim kodu yorumlarda ...)
JayEye

@ParanoidPanda Tek tırnak işareti kullanmanıza gerek yok, hayır, ancak dizgi değişmez değerlerinde kullanmak iyi bir uygulamadır. Bu şekilde, dize verilerinizde genişlemek isteyen bir sembol varsa daha sonra şaşırmayacaksınız.
Seth

@ParanoidPanda @JayEye ne dedi: Tek tırnakla çıktıya değişken genişleme foo=bar echo '$foo'baskısı yoktur $foo, bunun yerine foo=bar echo "$foo"yazdırır bar.
EKons

4

Sen bash kullanıyorsun. Bash için mükemmel bir alternatif vardır [: [[. İle [[, alıntı hakkında endişelenmenize gerek yoktur ve [aşağıdakiler için uygun destek de dahil olmak üzere çok daha fazla operatör elde edersiniz ||:

if [[ $1 = "--test" || $1 = "-t" ]]
then
    echo "Testing..."
    testing="y"
else
    testing="n"
    echo "Not testing."
fi

Veya normal ifadeleri kullanabilirsiniz:

if [[ $1 =~ ^(--test|-t) ]]
then
    echo "Testing..."
    testing="y"
else
    testing="n"
    echo "Not testing."
fi

Tabii ki, uygun alıntılar her zaman yapılmalıdır, ancak [bash kullanırken sadık kalmak için hiçbir neden yoktur .


3

Argümanların var olup olmadığını test etmek (genel olarak ve eylemleri buna göre yapın) Bu işe yaramalıdır:

#!/bin/bash

check=$1

if [ -z "$check" ]; then
  echo "no arguments"
else echo "there was an argument"
fi

Neden olmasın [ -z "$1" ]?
EKons

@ ΈρικΚωνσταντόπουλος Bu durumda, normalde, ancak komut dosyalarında, değişkeni tek bir kez çağırıyorum, genellikle bir kereden fazla olan yordamlara bakın. Protokolü örnekte tutmaya karar verdi.
Jacob Vlijm

Bu örnekte $check, herhangi bir shifts'den önce bir kez kullanıyorsunuz, $1bunun yerine kullanmak daha iyi olacaktır .
EKons

@ ΈρικΚωνσταντόπουλος Elbette belirtildiği gibi, senaryolarda kullanılacak bir örnek değildir . Senaryolarda, tekrar tekrar aynı şeyi yapmak kötü bir uygulamadır.
Jacob Vlijm

Ne demek istediğini anlıyorum, ancak #!örnekler verirken shebang'ları kullanmamak daha iyi bir uygulamadır (kodun dışındaki kabuğu tanımlamanız gerekir).
EKons
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.