Convalida dei parametri in uno script Bash


95

Ne ho ideato uno di base per aiutare ad automatizzare il processo di rimozione di un certo numero di cartelle man mano che diventano inutili.

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

Questo è evocato in questo modo:

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

Il problema è che se ti dimentichi di digitare id-number (come ho fatto io in quel momento) , potrebbe potenzialmente eliminare molte cose che in realtà non desideri vengano eliminate.

C'è un modo per aggiungere qualsiasi forma di convalida ai parametri della riga di comando? Nel mio caso, sarebbe bene controllare che a) ci sia un parametro, b) sia numerico ec) quella cartella esista; prima di continuare con lo script.

Risposte:


158
#!/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

modifica : all'inizio mi sono perso la parte relativa al controllo se le directory esistono, quindi l'ho aggiunta, completando lo script. Inoltre, hanno affrontato le questioni sollevate nei commenti; corretta l'espressione regolare, passata da ==a eq.

Questo dovrebbe essere uno script portatile, compatibile con POSIX per quanto ne so; non usa alcun bashismo, il che è davvero importante perché /bin/shsu Ubuntu è in realtà in dashquesti giorni, no bash.


ricordati di impostare + e e usa '-eq' invece di '==' per i confronti di interi
pistole

Modificato in -eq; cosa ti compra set + e qui?
Brian Campbell

ho trovato due cose nella mia risposta che potresti voler correggere anche nella tua: prima il SO hilighter impazzisce per $ # (trattandolo come un commento). ho fatto "$ #" per risolverlo. secondo, la regex trova anche "foo123bar". l'ho risolto facendo ^ [0-9] + $. puoi anche risolverlo usando l'opzione -x di grep
Johannes Schaub - litb

1
@ojblass mi mancava uno dei test di cui stava chiedendo. Aggiungerlo significava anche aggiungere le sue directory su cui testare, il che ha notevolmente ampliato la dimensione della risposta poiché non possono stare su una riga. Potete suggerire un modo più compatto per verificare l'esistenza di ciascuna directory?
Brian Campbell

1
secondo il commento di risposta di @Morten Nielsen di seguito, il grep '$ [0-9] + ^' sembra davvero strano. Non dovrebbe essere "^ [0-9] + $"?
martin jakubik

21

La shsoluzione di Brian Campbell, sebbene nobile e ben eseguita, ha alcuni problemi, quindi ho pensato di fornire la mia bashsoluzione.

I problemi con shquello:

  • La tilde in ~/foonon si espande nella tua home directory all'interno di heredocs. E nemmeno quando viene letto dalla readdichiarazione o citato nella rmdichiarazione. Il che significa che riceverai No such file or directoryerrori.
  • Forking off grepe simili per le operazioni di base è sciocco. Soprattutto quando usi un guscio schifoso per evitare il peso "pesante" di bash.
  • Ho anche notato alcuni problemi con le citazioni, ad esempio intorno all'espansione di un parametro nel suo echo.
  • Sebbene rara, la soluzione non è in grado di gestire i nomi di file che contengono nuove righe. (Quasi nessuna soluzione in shgrado di affrontarli, motivo per cui preferisco quasi sempre bash, è molto più a prova di proiettile e più difficile da sfruttare se usato bene).

Mentre, sì, usare /bin/shper il tuo hashbang significa che devi evitare gli bashismi a tutti i costi, puoi usare tutti gli bashismi che ti piacciono, anche su Ubuntu o altro quando sei onesto e messo #!/bin/bashin cima.

Quindi, ecco una bashsoluzione più piccola, più pulita, più trasparente, probabilmente "più veloce" e più a prova di proiettile.

[[ -d $1 && $1 != *[^0-9]* ]] || { echo "Invalid input." >&2; exit 1; }
rm -rf ~/foo/"$1"/bar ...
  1. Notare le virgolette in giro $1nella rmdichiarazione!
  2. Il -dcontrollo fallirà anche se $1è vuoto, quindi sono due controlli in uno.
  3. Ho evitato le espressioni regolari per un motivo. Se devi usare =~in bash, dovresti inserire l'espressione regolare in una variabile. In ogni caso, i glob come il mio sono sempre preferibili e supportati in molte più versioni bash.

1
Quindi il pezzo di globbing $1 != *[^0-9]*bash è specifico allora?
grinch

15

Userei bash's [[:

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

Ho trovato questa faq una buona fonte di informazioni.


13

Non a prova di proiettile come la risposta di cui sopra, tuttavia ancora efficace:

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

# rm commands go here


9

La pagina man di test ( man test) fornisce tutti gli operatori disponibili che puoi usare come operatori booleani in bash. Usa questi flag all'inizio del tuo script (o funzioni) per la convalida dell'input proprio come faresti in qualsiasi altro linguaggio di programmazione. Per esempio:

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

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

8

Usa '-z' per verificare la presenza di stringhe vuote e '-d per verificare la presenza di directory.

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
perché hai bisogno del doppio [[]]?
vehomzzz

5

Puoi convalidare il punto a e b in modo compatto facendo qualcosa di simile a quanto segue:

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

Il che ci dà ...

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

$ ./testparams.sh 1234
1234

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

Questo metodo dovrebbe funzionare bene in sh.


2

convalida dell'argomento Bash di una riga, con e senza convalida della directory

Ecco alcuni metodi che hanno funzionato per me. Puoi usarli nello spazio dei nomi dello script globale (se nello spazio dei nomi globale, non puoi fare riferimento alle variabili incorporate della funzione)

una fodera veloce e sporca

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

produzione:

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

Amante: fornisci nome e utilizzo della funzione

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

produzione:

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

Aggiungi una logica di convalida complessa senza ingombrare la funzione corrente

Aggiungere la riga seguente all'interno della funzione o dello script che riceve l'argomento.

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

È quindi possibile creare una funzione di convalida che faccia qualcosa di simile

validate() {

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

e una funzione die che interrompe lo script in caso di errore

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

Per argomenti aggiuntivi, aggiungi semplicemente una riga aggiuntiva, replicando il formato.

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

1

Vecchio post ma ho pensato di poter contribuire comunque.

Probabilmente uno script non è necessario e con una certa tolleranza ai caratteri jolly potrebbe essere eseguito dalla riga di comando.

  1. selvaggia ovunque corrispondenza. Consente di rimuovere qualsiasi occorrenza della sotto "cartella"

    $ rm -rf ~/*/folder/*
  2. Shell iterato. Consente di rimuovere le cartelle pre e post specifiche con una riga

    $ rm -rf ~/foo{1,2,3}/folder/{ab,cd,ef}
  3. Shell iterato + var (testato BASH).

    $ var=bar rm -rf ~/foo{1,2,3}/${var}/{ab,cd,ef}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.