Wie kann ich verhindern, dass nicht unterstützte 'shopt'-Optionen Fehler in meiner .bashrc verursachen?

9

Ich arbeite in einer relativ heterogenen Umgebung, in der ich möglicherweise verschiedene Versionen von Bash auf verschiedenen HPC-Knoten, VMs oder meiner persönlichen Workstation ausführe. Da ich meine Anmeldeskripte in ein Git-Repo eingefügt habe, möchte ich dasselbe (ish) .bashrcauf der ganzen Linie verwenden, ohne viel Unordnung vom Typ "Wenn dieser Host, dann ...".

Ich mag das Standardverhalten von Bash ≤ 4.1, das cd $SOMEPATHsich cd /the/actual/pathbeim Drücken der TabTaste erweitert. In Bash 4.2 und höher müssten Sie shopt -s direxpanddieses Verhalten erneut aktivieren, und das wurde erst in 4.2.29 verfügbar . Dies ist jedoch nur ein Beispiel; Eine andere, möglicherweise verwandte shoptOption complete_fullquote(obwohl ich nicht genau weiß , was sie tut) hat möglicherweise auch das Standardverhalten in Version 4.2 geändert.

Jedoch direxpandwird nicht von früheren Versionen von Bash erkannt, und wenn ich versuche, shopt -s direxpandin meinem .bashrc, dass die Ergebnisse in einer Fehlermeldung an die Konsole jedes Mal gedruckt wird , ich melden Sie sich an , um einen Knoten mit einem älteren Bash:

-bash: shopt: direxpand: invalid shell option name

Ich möchte eine Bedingung umschließen shop -s direxpand, um diese Option auf Bash> 4.1 auf robuste Weise zu aktivieren, ohne die älteren Versionen von Bash zu scheuern ( dh nicht nur die Fehlerausgabe an umzuleiten /dev/null).

TheDudeAbides
quelle
Wie hat meine Antwort nicht geholfen?
Luciano Andress Martini
@ LucianoAndressMartini Das hat es getan, und das ist die Lösung, mit der ich mich letztendlich selbst befasst habe .bashrc. Ich wollte immer noch eine Aufzeichnung darüber, wie ich $BASH_VERSINFOdie Haupt- / Nebenversion der laufenden Shell für meine eigene Erbauung abfragen kann, weshalb ich meine eigene Antwort fertiggestellt habe. :)
TheDudeAbides
Schauen Sie in meiner Antwort, ich habe etwas über den Vergleich der Programmversion mit dem Shell-Skript.
Luciano Andress Martini

Antworten:

14

Überprüfen Sie, ob direxpandin der Ausgabe von vorhanden ist, shoptund aktivieren Sie sie, wenn:

shopt | grep -q '^direxpand\b' && shopt -s direxpand
Luciano Andress Martini
quelle
4
Machen Sie das besser, grep -q '^direxpand\b'falls eine zukünftige Version oder Gabel von Bash eine Option hat, die diese als Teilzeichenfolge enthält und entfernt direxpand. In diesem speziellen Fall unwahrscheinlich, aber es kostet nicht viel, robust zu sein.
Gilles 'SO - hör auf böse zu sein'
Danke Luciano. Ich wollte meine eigene Frage beantworten, aber ich werde Ihre Antwort akzeptieren, nachdem meine Änderungen einer Peer Review
unterzogen wurden
4
Bash ermöglicht das Abfragen bestimmter Shell-Optionen, sodass diese verwendet werden können [ -z "$(shopt -po direxpand 2>&-)" ] || shopt -s direxpand. Keine Regex-Probleme mehr! :-)
David Foerster
@DavidFoerster Ich würde die Logik umdrehen: [ -n "blah" ] && shopt blahSo wie du es formulierst, sagst du "Wenn direxpand nicht unterstützt wird, dann mach das nicht".
Rich
1
@Rich: Die meisten meiner Shell-Skripte befinden sich set -eoben, daher verwende ich auf diese Weise eher Abkürzungslogik.
David Foerster
16

Ich sehe nicht, was falsch daran ist, Fehler umzuleiten /dev/null. Wenn Sie möchten, dass Ihr Code robust ist set -e, verwenden Sie die allgemeine Redewendung … || true:

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

Wenn Sie einen Fallback-Code ausführen möchten, wenn die Option nicht vorhanden ist, verwenden Sie den Rückgabestatus von shopt:

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

Wenn Sie den Fehler jedoch nicht wirklich umleiten möchten, können Sie den Abschlussmechanismus verwenden, um eine Selbstbeobachtung durchzuführen. Dies setzt voraus, dass Sie keine veralteten Maschinen mit Bash ≤ 2,03 haben, die keine programmierbare Fertigstellung hatten.

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

Diese Methode vermeidet das Gabeln, das in einigen Umgebungen wie Cygwin langsam ist. Also das einfach funktioniert 2>/dev/null, ich glaube nicht , dass Sie auf die Leistung schlagen kann.

Gilles 'SO - hör auf böse zu sein'
quelle
Das ist nicht , wo mein Gehirn gegangen habe, aber ich mag den compgenVorschlag. Das ist genau dort Uni-Level-Zeug! Das Vermeiden der Weiterleitung an /dev/nullist nur eine persönliche Präferenz. Ich bitte lieber um Erlaubnis als um Vergebung, wenn das Sinn macht? :)
TheDudeAbides
+1 für eine völlig unerwartete Schulung in Bash, die programmierbar abgeschlossen werden konnte, was mich dazu zwang, im Handbuch zu entschlüsseln, was compgen -A shopt -X ...überhaupt bedeutete.
TheDudeAbides
4
@TheDudeAbides Ich habe über die Verwendung compgendieser Methode unter Unix und Linux gelesen. Ich weiß nicht, wer sie zuerst vorgeschlagen hat. (Ich habe Bash nicht mehr als Haupt-Shell verwendet, bevor es programmierbar abgeschlossen wurde.) Bei der Programmierung ist es normalerweise eine schlechte Idee, um Erlaubnis zu bitten, da das Risiko besteht, dass die Berechtigungsprüfung aufgrund einer Codierung nicht mit dem übereinstimmt, was Sie tatsächlich tun Fehler (bei dem Sie nicht genau überprüfen, was Sie zu überprüfen glauben) oder weil sich das, was Sie überprüft haben, geändert hat, bevor Sie es verwendet haben .
Gilles 'SO - hör auf böse zu sein'
5

Wenn Sie sicher sind, dass eine bestimmte shoptOption in einer bestimmten Haupt- / Neben- / Patch-Version von Bash verfügbar ist, können Sie die $BASH_VERSIONVariable oder die Elemente des $BASH_VERSINFO[]Arrays überprüfen , um sie bedingt zu aktivieren.

Hier ist ein Test für Bash 4.2.29 oder höher, die Version, in der die 4.2-Serie direxpand erstmals vorgestellt wurde :

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

Bearbeiten: Um es klar auszudrücken, ist dies eine lächerlich überentwickelte Lösung, um eine Fehlermeldung aus Ihren Anmeldeskripten einfach zu ignorieren, aber ich wollte sie trotzdem für meine eigene Bearbeitung dokumentieren.

Beachten Sie die Klammern um , die sind erforderlich, und die Verwendung von und , die ganzen Zahl zu tun , anstatt (locale abhängig) lexikalische Vergleiche. Wenn nicht notierte, die RHS der ist Betreiber als „extglob“ Muster innerhalb Bash behandelt / conditionals, wie erwähnt hier , die ein ästhetischer „beginnt mit“ Vergleich als ein regex wäre IMO macht.${BASH_VERSINFO[index]}-eq-gt==[[]]

Das $BASH_VERSINFOArray enthält alle Informationen, die Sie in der Ausgabe von sehen würden 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")'

Wenn es nicht ist , ergibt sich aus der Dokumentation , shoptan der Bash - Version (en) wurde ihr Verhalten unterstützt oder geändert wird , ist das Verfahren von Luciano vorgeschlagen fein:

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

... ebenso wie die von Gilles vorgeschlagene Lösung, den Fehler ( shopt -s direxpand 2>/dev/null) einfach zu ignorieren und möglicherweise zu prüfen, $?ob dies unbedingt erforderlich ist.

Referenzen: 1 , 2 , 3
Verwandte Lektüre: Set and Shopt - Warum zwei?

TheDudeAbides
quelle
Sie könnten in der Lage, auch so etwas wie zu verwenden if [[ $BASH_VERSION > 4.3 ]];(Spiele , die 4.3.0, 5.0etc., aber auch 4.3.0-alphaich weiß nicht , ob die später tatsächlich Angelegenheiten..)
ilkkachu
Hallo @ilkkachu. Vielen Dank für Ihre Bearbeitung von Bash v5.x. Die direxpandOption ist jedoch tatsächlich für Bash 4.2 verfügbar. Ich habe dies mit einem Docker-Image in Version 4.2.53 durch Ausführen überprüft docker run --rm bash:4.2 bash -c shopt | grep direxpand(und zum guten Teil, dass es in Version 4.1.17 tatsächlich nicht durch Ausführen verfügbar ist docker run --rm bash:4.1 bash -c shopt | grep direxpand).
TheDudeAbides
ah ok, ich habe getestet 4.2.0und bin auf die Tatsache gestoßen, dass es dort nicht funktioniert hat. Das Changelog erwähnt auch, dass es hinzugefügt wurde bash-4.3-alpha. Ich nehme dann an, dass man überprüfen müsste ${BASH_VERSINFO[2]}, um genau zu sein, aber ich weiß nicht, welcher Punkt Release es hinzugefügt hat ...
ilkkachu
Ich denke, wir haben im Grunde den Punkt bewiesen, den Gilles oben gemacht hat. Es ist in der Tat besser, nur zu versuchen , die Shell-Option zu aktivieren, und dann den Fehler zu behandeln (oder zu unterdrücken), wenn er nicht unterstützt wird.
TheDudeAbides