Bir Bash betiğine parametreleri doğrulama


96

Gereksiz hale geldiklerinde birkaç klasörü kaldırma işlemini otomatikleştirmeye yardımcı olacak temel bir tane buldum.

#!/bin/bash
rm -rf ~/myfolder1/$1/anotherfolder
rm -rf ~/myfolder2/$1/yetanotherfolder
rm -rf ~/myfolder3/$1/thisisafolder

Bu şu şekilde çağrılır:

./myscript.sh <{id-number}>

Sorun şu ki, id-number (tam o sırada yaptığım gibi) yazmayı unutursanız, gerçekten silinmesini istemediğiniz birçok şeyi potansiyel olarak silebilirsiniz.

Komut satırı parametrelerine herhangi bir şekilde doğrulama eklemenin bir yolu var mı? Benim durumumda, a) bir parametrenin olup olmadığını, b) bunun sayısal olduğunu ve c) klasörün var olduğunu kontrol etmek iyi olacaktır; senaryoya devam etmeden önce.

Yanıtlar:


160
#!/bin/sh
die () {
    echo >&2 "$@"
    exit 1
}

[ "$#" -eq 1 ] || die "1 argument required, $# provided"
echo $1 | grep -E -q '^[0-9]+$' || die "Numeric argument required, $1 provided"

while read dir 
do
    [ -d "$dir" ] || die "Directory $dir does not exist"
    rm -rf "$dir"
done <<EOF
~/myfolder1/$1/anotherfolder 
~/myfolder2/$1/yetanotherfolder 
~/myfolder3/$1/thisisafolder
EOF

düzenleme : İlk başta dizinlerin var olup olmadığını kontrol etme kısmını kaçırdım, bu yüzden betiği tamamlayarak bunu ekledim. Ayrıca, yorumlarda dile getirilen sorunları ele aldık; normal ifade, sabit anahtarlamalı ==için eq.

Anladığım kadarıyla bu taşınabilir, POSIX uyumlu bir betik olmalı; herhangi bir bashism kullanmaz, ki bu aslında önemlidir çünkü /bin/shUbuntu'da aslında dashbugünlerde değil bash.


Tamsayı karşılaştırmaları için + e'yi ayarlamayı ve '==' yerine '-eq' kullanmayı unutmayın
guns

-Eq olarak değiştirildi; set + e sizi burada ne satın alıyor?
Brian Campbell

Cevabımda sizinkinde düzeltmek isteyebileceğiniz iki şey buldum: ilk olarak SO hilighter $ # için çıldırıyor (bunu bir yorum olarak ele alarak). düzeltmek için "$ #" yaptım. ikinci olarak normal ifade "foo123bar" ile de eşleşir. ^ [0-9] + $ yaparak düzelttim. grep'in -x seçeneğini kullanarak da düzeltebilirsiniz
Johannes Schaub - litb

1
@ojblass Sorduğu testlerden biri eksikti. Bunu eklemek, aynı zamanda test etmek için dizinlerini de eklemek anlamına geliyordu, bu da tek bir satıra sığamayacakları için cevabın boyutunu önemli ölçüde genişletti. Her dizinin varlığını test etmenin daha kompakt bir yolunu önerebilir misiniz?
Brian Campbell

1
@Morten Nielsen'in aşağıdaki cevap yorumuna göre, grep '$ [0-9] + ^' gerçekten tuhaf görünüyor. "^ [0-9] + $" olması gerekmez mi?
martin jakubik 01

21

shTarafından çözüm Brian Campbellasil ve de yürütülen, birkaç sorun varken, bu yüzden ben kendi güvenliğimi düşündüm bashçözümü.

Biriyle ilgili sorunlar sh:

  • Tilde, ~/fooyorumlu metinler içindeki ana yönlendirmenize genişlemez. Ve ne readifade tarafından okunduğunda ne de ifadede alıntı yapıldığında rm. Bu, No such file or directoryhata alacağınız anlamına gelir .
  • Kapalı çatallamak greptemel işlemler için ve bu tür aptal olduğunu. Özellikle bash'ın "ağır" ağırlığından kaçınmak için berbat bir kabuk kullandığınızda.
  • Aynı zamanda birkaç alıntı sorununu da fark ettim, örneğin onunkinde bir parametre genişletmesi echo.
  • Nadir de olsa çözüm, satırsonu içeren dosya adlarıyla baş edemez. (Neredeyse hiçbir çözüm shonlarla baş edemez - bu yüzden neredeyse her zaman tercih ederim bash, çok daha kurşun geçirmez ve iyi kullanıldığında sömürülmesi daha zordur).

Evet, /bin/shhashbang'iniz için kullanmak her ne pahasına olursa olsun izmlerden kaçınmanız gerektiği anlamına gelirken, sevdiğiniz bashtüm ismleri bashUbuntu'da veya dürüst olduğunuzda ve #!/bin/bashzirveye koyduğunuzda bile kullanabilirsiniz .

İşte bashsize daha küçük, daha temiz, daha şeffaf, muhtemelen "daha hızlı" ve daha kurşun geçirmez bir çözüm.

[[ -d $1 && $1 != *[^0-9]* ]] || { echo "Invalid input." >&2; exit 1; }
rm -rf ~/foo/"$1"/bar ...
  1. Sonuna tırnak işareti dikkat $1içinde rmdeyimi!
  2. -dOnay da başarısız olacaktır $1boş olduğunu birinde iki çek yani.
  3. Normal ifadelerden bir nedenle kaçındım. =~Bash'de kullanmanız gerekiyorsa, normal ifadeyi bir değişkene koymalısınız. Her durumda, benimki gibi küreler her zaman tercih edilir ve çok daha fazla bash sürümünde desteklenir.

1
Öyleyse globbing parçası $1 != *[^0-9]*bash özel mi?
grinch

15

Bash'leri kullanırım [[:

if [[ ! ("$#" == 1 && $1 =~ ^[0-9]+$ && -d $1) ]]; then 
    echo 'Please pass a number that corresponds to a directory'
    exit 1
fi

Bu SSS'yi iyi bir bilgi kaynağı olarak buldum .


13

Yukarıdaki cevap kadar kurşun geçirmez değil, ancak yine de etkili:

#!/bin/bash
if [ "$1" = "" ]
then
  echo "Usage: $0 <id number to be cleaned up>"
  exit
fi

# rm commands go here


9

Test ( man test) için man sayfası , bash'de boole operatörleri olarak kullanabileceğiniz tüm kullanılabilir operatörleri sağlar. Bu bayrakları, başka herhangi bir programlama dilinde olduğu gibi, giriş doğrulaması için komut dosyanızın (veya işlevlerin) başında kullanın. Örneğin:

if [ -z $1 ] ; then
  echo "First parameter needed!" && exit 1;
fi

if [ -z $2 ] ; then
  echo "Second parameter needed!" && exit 2;
fi

8

Boş dizeleri test etmek için '-z' ve dizinleri kontrol etmek için '-d kullanın.

if [[ -z "$@" ]]; then
    echo >&2 "You must supply an argument!"
    exit 1
elif [[ ! -d "$@" ]]; then
    echo >&2 "$@ is not a valid directory!"
    exit 1
fi

2
neden çift [[]] 'e ihtiyacınız var?
vehomzzz

5

Aşağıdaki gibi bir şey yaparak a ve b noktasını kısaca doğrulayabilirsiniz:

#!/bin/sh
MYVAL=$(echo ${1} | awk '/^[0-9]+$/')
MYVAL=${MYVAL:?"Usage - testparms <number>"}
echo ${MYVAL}

Bu da bize ...

$ ./testparams.sh 
Usage - testparms <number>

$ ./testparams.sh 1234
1234

$ ./testparams.sh abcd
Usage - testparms <number>

Bu yöntem sh içinde iyi çalışmalıdır.


2

dizin doğrulamalı veya doğrulamasız tek satırlık Bash bağımsız değişken doğrulaması

İşte benim için işe yarayan bazı yöntemler. Bunları her iki global kod ad alanında da kullanabilirsiniz (global ad alanında ise, işlev yerleşik değişkenlerine başvuramazsınız)

hızlı ve kirli tek astar

: ${1?' You forgot to supply a directory name'}

çıktı:

./my_script: line 279: 1: You forgot to supply a directory name

Fancier - arz işlevi adı ve kullanımı

${1? ERROR Function: ${FUNCNAME[0]}() Usage: " ${FUNCNAME[0]} directory_name"}

çıktı:

./my_script: line 288: 1:  ERROR Function: deleteFolders() Usage:  deleteFolders directory_name

Mevcut işlevinizi karmaşık hale getirmeden karmaşık doğrulama mantığı ekleyin

Bağımsız değişkeni alan fonksiyon veya komut dosyasına aşağıdaki satırı ekleyin.

: ${1?'forgot to supply a directory name'} && validate $1 || die 'Please supply a valid directory'

Daha sonra şuna benzer bir şey yapan bir doğrulama işlevi oluşturabilirsiniz:

validate() {

    #validate input and  & return 1 if failed, 0 if succeed
    if [[ ! -d "$1" ]]; then
        return 1
    fi
}

ve hata durumunda komut dosyasını iptal eden bir kalıp işlevi

die() { echo "$*" 1>&2 ; exit 1; }

Ek argümanlar için, formatı kopyalayan ek bir satır eklemeniz yeterlidir.

: ${1?' You forgot to supply the first argument'}
: ${2?' You forgot to supply the second argument'}

1

Eski gönderi ama yine de katkıda bulunabileceğimi düşündüm.

Bir komut dosyası tartışmasız gerekli değildir ve bazı joker karakterlere tolerans gösterilerek komut satırından gerçekleştirilebilir.

  1. vahşi her yerde eşleşen. Alt "klasör" oluşumunu kaldıralım

    $ rm -rf ~/*/folder/*
    
  2. Kabuk yinelendi. Belirli ön ve posta klasörlerini tek satırla kaldıralım

    $ rm -rf ~/foo{1,2,3}/folder/{ab,cd,ef}
    
  3. Kabuk yinelenmiş + var (BASH test edildi).

    $ var=bar rm -rf ~/foo{1,2,3}/${var}/{ab,cd,ef}
    
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.