Come posso evitare che opzioni "shopt" non supportate causino errori nel mio .bashrc?


9

Lavoro in un ambiente relativamente eterogeneo in cui potrei eseguire diverse versioni di Bash su diversi nodi HPC, VM o workstation personali. Dato che ho inserito i miei script di accesso in un repository Git, mi piacerebbe usare lo stesso (ish) .bashrcsu tutta la linea, senza un sacco di disordine di tipo "if this host, then ...".

Mi piace il comportamento predefinito di Bash ≤ 4.1 che si espande cd $SOMEPATHin cd /the/actual/pathquando si preme il Tabtasto. In Bash 4.2 e versioni successive, è necessario shopt -s direxpandriattivare questo comportamento e che non sarà disponibile fino al 4.2.29 . Questo è solo un esempio, però; un'altra shoptopzione , probabilmente correlata , complete_fullquote(anche se non so esattamente cosa faccia) potrebbe anche aver cambiato il comportamento predefinito alla v4.2.

Tuttavia, direxpandnon è riconosciuto dalle versioni precedenti di Bash, e se provo a shopt -s direxpandnel mio .bashrc, che si traduce in un messaggio di errore in fase di stampa per la console ogni volta che accedo ad un nodo con un vecchio Bash:

-bash: shopt: direxpand: invalid shell option name

Quello che mi piacerebbe fare è avvolgere un condizionale shop -s direxpandper abilitare quell'opzione su Bash> 4.1 in modo robusto, senza sfregare le versioni precedenti di Bash ( cioè , non solo reindirizzare l'output dell'errore a /dev/null).


In che modo la mia risposta non ha aiutato?
Luciano Andress Martini,

@LucianoAndressMartini Lo ha fatto, e questa è la soluzione con cui sono finito nel mio .bashrc. Volevo ancora un record su come utilizzare $BASH_VERSINFOper interrogare la versione maggiore / minore della shell in esecuzione, per la mia edificazione, motivo per cui ho finito di pubblicare la mia risposta. :)
TheDudeAbides

Guarda nella mia risposta ho qualcosa sul confronto tra la versione dei programmi e lo script della shell.
Luciano Andress Martini

Risposte:


14

Controlla se direxpandè presente nell'output di shopte abilitalo se è:

shopt | grep -q '^direxpand\b' && shopt -s direxpand

4
Meglio fare grep -q '^direxpand\b'in modo che nel caso in cui una futura versione o fork di bash abbia un'opzione che lo contenga come sottostringa e lo rimuova direxpand. Improbabile in questo caso specifico, ma non costa molto per essere robusto.
Gilles 'SO- smetti di essere malvagio' il

Grazie Luciano. Avevo intenzione di rispondere alla mia domanda, ma accetterò la tua risposta dopo che le mie modifiche passeranno attraverso la revisione tra pari. Forse puoi approvarle tu stesso?
TheDudeAbides

4
Bash consente di interrogare specifiche opzioni di shell, in modo che si possa usare [ -z "$(shopt -po direxpand 2>&-)" ] || shopt -s direxpand. Niente più problemi di regex! :-)
David Foerster,

@DavidFoerster Rovescerei la logica: [ -n "blah" ] && shopt blahil modo in cui la pronunci, stai dicendo "se direxpand non è supportato, allora non fare questa cosa".
Ricco

1
@Rich: la maggior parte dei miei script di shell include set -ein alto, quindi tendo ad usare la logica di scorciatoia in questo modo.
David Foerster,

16

Non vedo cosa c'è di sbagliato nel reindirizzare gli errori a /dev/null. Se vuoi che il tuo codice sia robusto set -e, usa il linguaggio comune … || true:

shopt -s direxpand 2>/dev/null || true

Se si desidera eseguire un codice di fallback se l'opzione non esiste, utilizzare lo stato di restituzione di shopt:

if shopt -s direxpand 2>/dev/null; then
   # the direxpand option exists
else
   # the direxpand option does not exist
fi

Ma se non ti piace davvero reindirizzare l'errore, puoi utilizzare il meccanismo di completamento per eseguire l'introspezione. Ciò presuppone che non si disponga di macchine antiquate con bash ≤ 2.03 che non avevano un completamento programmabile.

shopt_exists () {
  compgen -A shopt -X \!"$1" "$1" >/dev/null
}
if shopt_exists direxpand; then
  shopt -s direxpand
fi

Questo metodo evita il fork, che è lento in alcuni ambienti come Cygwin. Lo stesso 2>/dev/nullvale per il semplice , non credo che tu possa batterlo durante le esibizioni.


Cioè non dove il mio cervello sarebbe andato, ma mi piace la compgenproposta. È roba di livello varsity proprio lì! Evitare il reindirizzamento a /dev/nullè solo una preferenza personale. Mi piace chiedere il permesso invece del perdono, se questo ha senso? :)
TheDudeAbides

+1 per una scolarizzazione totalmente inaspettata nel completamento programmabile di Bash, che mi ha costretto ad andare al manuale per decifrare cosa compgen -A shopt -X ...volesse dire.
TheDudeAbides

4
@TheDudeAbides ho letto su come utilizzare compgenin questo modo su Unix e Linux , non so che per primo la propose. (Ho smesso di usare bash come shell principale prima che avesse un completamento programmabile.) In programmazione di solito è una cattiva idea chiedere l'autorizzazione perché c'è il rischio che il controllo delle autorizzazioni non corrisponda a quello che stai effettivamente facendo, sia a causa di un codice errore (in cui non stai verificando esattamente ciò che pensi di controllare) o perché ciò che hai controllato è cambiato prima di utilizzarlo .
Gilles 'SO- smetti di essere malvagio' il

5

Quando sai con certezza che è disponibile shoptun'opzione specifica in una determinata versione maggiore / minore / patch di Bash, puoi ispezionare la $BASH_VERSIONvariabile o gli elementi $BASH_VERSINFO[]dell'array per abilitarla in modo condizionale.

Ecco un test per Bash 4.2.29 o versioni successive, la versione in cui è direxpand stata introdotta per la prima volta la serie 4.2:

if [[ $BASH_VERSION == 4.2.* && ${BASH_VERSINFO[2]} -ge 29 ]] ||
   [[ ${BASH_VERSINFO[0]} -eq 4 && ${BASH_VERSINFO[1]} -ge 3 ]] ||
   [[ ${BASH_VERSINFO[0]} -ge 5 ]]; then
    shopt -s direxpand
fi

Modifica: per essere chiari, questa è una soluzione ridicolmente ingegnerizzata per semplicemente ignorare un messaggio di errore proveniente dagli script di accesso, ma volevo documentarlo a prescindere, per la mia modifica.

Nota le parentesi graffe intorno , che sono necessarie e l'uso di e , che fanno confronti lessicali interi anziché (locali-dipendenti). Se non quotato, l'RHS dell'operatore viene trattato come pattern "extglob" all'interno di Bash / condizionali, come indicato qui , il che rende un confronto "estetico" più estetico di quanto farebbe un regex, IMO.${BASH_VERSINFO[index]}-eq-gt==[[]]

L' $BASH_VERSINFOarray contiene tutte le informazioni che vedresti nell'output di bash --version:

bash --version | head -1
# result:
# GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)

declare -p BASH_VERSINFO
# result:
# declare -ar BASH_VERSINFO='([0]="4" [1]="3" [2]="48" [3]="1" [4]="release" [5]="x86_64-pc-linux-gnu")'

Quando non è chiaro dalla documentazione per la shoptquale le versioni di Bash sono state supportate o hanno cambiato il loro comportamento, il metodo proposto da Luciano va bene:

# note the '-q' so that the matched pattern isn't actually printed
shopt | grep -q direxpand && shopt -s direxpand

... come è la soluzione proposta da Gilles di ignorare l'errore ( shopt -s direxpand 2>/dev/null) e forse verificare $?se assolutamente necessario.

Riferimenti: 1 , 2 , 3
Lettura correlata: Set and Shopt - Why Two?


Potresti anche essere in grado di usare qualcosa di simile if [[ $BASH_VERSION > 4.3 ]];(che corrisponde 4.3.0, 5.0ecc., Ma anche 4.3.0-alpha. Non so se il fatto successivo sia importante.)
ilkkachu

Ciao @ilkkachu. Grazie per la tua modifica su Bash v5.x. L' direxpandopzione è effettivamente disponibile per Bash 4.2, tuttavia; Ho verificato questa una con immagine Docker alla v4.2.53 eseguendo docker run --rm bash:4.2 bash -c shopt | grep direxpand(e, per buona misura, che è davvero non è disponibile al v4.1.17 eseguendo docker run --rm bash:4.1 bash -c shopt | grep direxpand).
TheDudeAbides,

ah ok, ho testato 4.2.0e inciampato sul fatto che non ha funzionato lì. Il log delle modifiche menziona anche l'aggiunta bash-4.3-alpha. Suppongo quindi che si dovrebbe verificare ${BASH_VERSINFO[2]}per essere esatti, ma non so quale rilascio del punto lo abbia aggiunto ...
ilkkachu,

Penso che abbiamo sostanzialmente dimostrato il punto sollevato da Gilles; che, in effetti, è meglio provare ad abilitare l'opzione shell e quindi gestire l'errore (o eliminarlo) se non è supportato.
TheDudeAbides,
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.