Mit der Nachricht, dass Catalina standardmäßig Zsh anstelle von Bash verwenden wird , finde ich viele Ergebnisse, die mich über den Wechsel informieren und dass er Probleme mit Shell-Skripten verursachen kann, aber ich bin mit Zsh nicht vertraut genug, um zu wissen, was diese Probleme sind mag sein.
Meine Shell-Skripte sind wirklich nicht so kompliziert, aber ich habe Bash bisher nur unter macOS und Linux verwendet - null Erfahrung mit Zsh. Kann jemand einen einfachen praktischen Vergleich oder spezifische Stolpersteine liefern, die ich kennen muss, damit ich anfangen kann, darauf hinzuarbeiten, für die neue Shell bereit zu sein, wenn Catalina veröffentlicht wird?
Zunächst einige wichtige Dinge:
#!/bin/bash
oder #!/bin/sh
oder beginnt #!/usr/bin/env bash
, funktioniert es genau wie zuvor.Angenommen, Sie erwägen, zu zsh zu wechseln, was seit Jahren möglich ist, hier sind die Hauptunterschiede, auf die Sie stoßen werden. Dies ist keine vollständige Liste!
Konfigurationsdateien : bash liest (hauptsächlich) .bashrc
in interaktiven Shells ohne Anmeldung (aber macOS startet standardmäßig eine Anmelde-Shell in Terminals) .profile
oder .bash_profile
in Anmelde-Shells und .inputrc
. Zsh liest (hauptsächlich) .zshrc
(in allen interaktiven Shells) und .zprofile
(in Login-Shells). Das bedeutet, dass keine Ihrer Bash-Anpassungen angewendet wird: Sie müssen sie portieren. Sie können die Dateien nicht einfach kopieren, da viele Dinge angepasst werden müssen.
Tastenbelegungen verwenden eine völlig andere Syntax. Bash verwendet .inputrc
und das bind
eingebaute , um Schlüssel an readline - Befehle zu binden . Zsh verwendet das bindkey
eingebaute , um Schlüssel an zle - Widgets zu binden . Die meisten readline-Befehle haben ein zsh-Äquivalent, aber es ist nicht immer eine perfekte Entsprechung.
Apropos Tastenbelegung: Wenn Sie Vi(m) als In-Terminal-Editor, aber nicht als Befehlszeilenmodus in der Shell verwenden, werden Sie feststellen, dass zsh standardmäßig den vi-Bearbeitungsmodus (dh mit Befehls- und Einfügemodus) verwendet, wenn EDITOR
oder VISUAL
ist auf vi
oder setzen vim
. bindkey -e
wechselt in den emacs-Modus (dh wo Sie immer direkt tippen können).
Eingabeaufforderung : bash legt die Eingabeaufforderung (hauptsächlich) fest, von der Backslash-EscapesPS1
enthalten sind . Zsh legt die Eingabeaufforderung hauptsächlich fest, von der Prozente entkommen . Obwohl die Konzepte ähnlich sind, sind die Escape-Codes völlig unterschiedlich. Die Funktionalität von bash ist in zsh über die Hook-Funktionen und verfügbar . Zsh hat mehr Bequemlichkeitsmechanismen, um ausgefallene Eingabeaufforderungen zu erstellen, einschließlich eines Mechanismus für Eingabeaufforderungsthemen .PS1
PROMPT_COMMAND
precmd
preexec
Die grundlegenden Befehlszeilenverlaufsmechanismen (Navigation mit Up/ Down, Suche mit Ctrl+ R, Verlaufserweiterung mit !!
und Freunden, Abrufen des letzten Arguments mit Alt+ .oder $_
) funktionieren auf die gleiche Weise, aber es gibt viele Unterschiede in den Details, zu viele, um sie hier aufzulisten . Sie können Ihre .bash_history
nach kopieren .zsh_history
, wenn Sie keine Shell-Option geändert haben, die das Dateiformat ändert.
Vervollständigung : Beide Shells verwenden standardmäßig einen grundlegenden Vervollständigungsmodus, der Befehls- und Dateinamen größtenteils vervollständigt, und wechseln in einen ausgefallenen Modus, indem sie bash_completion
in bash eingebunden oder compinit
in zsh ausgeführt werden. Sie werden einige Befehle finden, die bash besser handhabt, und einige, die zsh besser handhabt. Zsh ist normalerweise präziser, gibt aber manchmal auf, wenn bash etwas macht, das nicht korrekt, aber sinnvoll ist. Um mögliche Vervollständigungen für einen Befehl anzugeben, verfügt zsh über drei Mechanismen:
compctl
können Sie vergessen.compadd
vielen Funktionen, die mit Unterstrichen beginnen, und einem leistungsstarken, aber komplexen Benutzerkonfigurationsmechanismus .bashcompinit
. Die Emulation ist nicht 100% perfekt, aber sie funktioniert normalerweise.Viele der shopt
Einstellungen von Bash haben eine Entsprechung setopt
in zsh.
Zsh behandelt standardmäßig keinen #
Kommentarstart auf der Befehlszeile, sondern nur in Skripten (einschließlich .zshrc
und dergleichen). Um interaktive Kommentare zu aktivieren, führen Sie setopt interactive_comments
.
(und natürlich für Power-User auf der Kommandozeile)
Nimmt in bash $foo
den Wert von foo
, teilt ihn bei Leerzeichen und ersetzt für jeden durch Leerzeichen getrennten Teil, wenn er Platzhalterzeichen enthält und mit einer vorhandenen Datei übereinstimmt, das Muster durch die Liste der Übereinstimmungen. Um nur den Wert von zu erhalten foo
, benötigen Sie "$foo"
. Gleiches gilt für die Befehlsersetzung $(foo)
. In zsh $foo
ist der Wert von foo
und $(foo)
die Ausgabe von foo
minus der letzten Zeilenumbrüche, mit zwei Ausnahmen. Wenn ein Wort leer wird, weil leere, nicht in Anführungszeichen gesetzte Variablen erweitert werden, wird es entfernt (z. B. a=; b=; printf "%s\n" one "$a$b" three $a$b five
gibt one
, eine leere Zeile, three
, five
) aus. Das Ergebnis einer Befehlsersetzung ohne Anführungszeichen wird an Leerzeichen geteilt, aber die Teile werden keinem Wildcard-Abgleich unterzogen.
Bash- Arrays werden von 0 bis (Länge-1) indiziert. Zsh-Arrays werden von 1 bis Länge indiziert. Sie können die 0-Indizierung mit zum Standard machen setopt ksh_arrays
. Zsh erfordert weniger geschweifte Klammern (es sei denn, ksh_arrays
es ist aktiviert). Angenommen zum Beispiel a=(first second third "" last)
.
Funktionalität | Bash-Syntax | Idiomatische zsh-Syntax | Erweiterung |
---|---|---|---|
Erstes Element | ${a[0]} |
$a[1] |
first |
Zweites Element | ${a[1]} |
$a[2] |
second |
Letztes Element | ${a[${#a[@]}-1]} |
$a[-1] |
last |
Länge | ${#a[@]} |
$#a |
5 |
Alle Elemente | "${a[@]}" |
"${a[@]}" oder"${(@)a}" |
first second third (leeres Wort)last |
Alle nicht leeren Elemente | $a |
first second third last |
Bash hat zusätzliche Wildcard-Muster wie zum Beispiel @(foo|bar)
to match foo
oder bar
, die nur mit aktiviert werden shopt -s extglob
. In zsh können Sie diese Muster mit aktivieren , aber es gibt auch setopt ksh_glob
eine einfacher zu schreibende native Syntax wie z . Zsh ist für das rekursive Verzeichnistraversal vorgesehen (ebenso wie die moderne Bash, aber nicht die Bash 3.2, die mit macOS geliefert wird).(foo|bar)
setopt extended_glob
.zshrc
**/
Wenn ein Platzhaltermuster in Bash standardmäßig mit keiner Datei übereinstimmt, bleibt es unverändert. In zsh erhalten Sie standardmäßig einen Fehler, was normalerweise die sicherste Einstellung ist. Wenn Sie einen Platzhalterparameter an einen Befehl übergeben möchten, verwenden Sie Anführungszeichen. Mit können Sie zum Bash-Verhalten wechseln setopt no_nomatch
. Sie können nicht übereinstimmende Wildcard-Muster stattdessen mit zu einer leeren Liste erweitern setopt null_glob
.
In Bash läuft die rechte Seite einer Pipeline in einer Subshell. In zsh läuft es in der übergeordneten Shell, sodass Sie Dinge schreiben können wie somecommand | read output
.
Hier sind ein paar nette zsh-Features, die bash nicht hat (zumindest nicht ohne ernsthaftes Ellbogenfett). Noch einmal, dies ist nur eine Auswahl derjenigen, die ich für die nützlichsten halte.
Glob-Qualifizierer ermöglichen das Abgleichen von Dateien basierend auf Metadaten wie Zeitstempel, Größe usw. Sie ermöglichen auch das Optimieren der Ausgabe. Die Syntax ist ziemlich kryptisch, aber äußerst praktisch. Hier sind ein paar Beispiele:
foo*(.)
: Nur Übereinstimmung mit regulären Dateien foo*
und symbolische Links zu regulären Dateien, nicht zu Verzeichnissen und anderen speziellen Dateien.foo*(*.)
: nur ausführbare reguläre Dateien, die mit foo*
.foo*(-.)
: Nur reguläre Dateien, die übereinstimmen foo*
, keine symbolischen Links und andere spezielle Dateien.foo*(-@)
: Nur baumelnde symbolische Links passen foo*
.foo*(om)
: die passenden Dateien foo*
, sortiert nach Datum der letzten Änderung, neueste zuerst. Beachten Sie, dass es seine eigene Sortierung durchführt, wenn Sie dies an übergeben ls
. Dies ist besonders nützlich bei …foo*(om[1,10])
: die 10 neuesten Dateien, die mit übereinstimmen foo*
, die neuesten zuerst.foo*(Lm+1)
: übereinstimmende Dateien foo*
, deren Größe mindestens 1 MB beträgt.foo*(N)
: wie foo*
, aber wenn dies zu keiner Datei passt, wird eine leere Liste erzeugt, unabhängig von der Einstellung der null_glob
Option (siehe oben).*(D)
: Übereinstimmung mit allen Dateien, einschließlich Punktdateien (außer .
und ..
).foo/bar/*(:t)
(unter Verwendung eines Verlaufsmodifikators ): die Dateien in foo/bar
, aber nur mit dem Basisnamen der Datei. Wenn es zB ein gibt foo/bar/qux.txt
, wird es als erweitert qux.txt
.foo/bar/*(.:r)
: Nehmen Sie normale Dateien unter foo/bar
und entfernen Sie die Erweiterung. ZB foo/bar/qux.txt
wird erweitert als foo/bar/qux
.foo*.odt(e\''REPLY=$REPLY:r.pdf'\')
: Nimm die Liste der passenden Dateien foo*.odt
und ersetze sie .odt
durch .pdf
(unabhängig davon, ob die PDF-Datei existiert).Hier sind ein paar nützliche zsh-spezifische Wildcard-Muster .
foo*.txt~foobar*
: alle .txt
Dateien, deren Name mit beginnt, foo
aber nicht foobar
.image<->.jpg(n)
: alle .jpg
Dateien, deren Basisname image
eine Zahl folgt, zB image3.jpg
und image22.jpg
aber nicht image-backup.jpg
. Der Glob-Qualifizierer (n)
bewirkt, dass die Dateien in numerischer Reihenfolge aufgelistet werden, dh image9.jpg
kommt vor (Sie können dies auch ohne mit image10.jpg
festlegen ).-n
setopt numeric_glob_sort
Um Dateien massenweise umzubenennen , bietet zsh ein sehr praktisches Werkzeug: die zmv
Funktion . Empfohlen für Ihre .zshrc
:
autoload zmv
alias zcp='zmv -C' zln='zmv -L'
Beispiel:
zmv '(*).jpeg' '$1.jpg'
zmv '(*)-backup.(*)' 'backups/$1.$2'
Bash hat einige Möglichkeiten, Transformationen anzuwenden, wenn der Wert einer Variablen übernommen wird . Zsh hat einige davon und noch viel mehr .
Zsh hat eine Reihe kleiner praktischer Funktionen, um Verzeichnisse zu wechseln . Schalten Sie ein setopt auto_cd
, um in ein Verzeichnis zu wechseln, wenn Sie seinen Namen eingeben, ohne tippen zu müssen cd
(bash hat dies heutzutage auch). Sie können die Form mit zwei Argumentencd
verwenden , um in ein Verzeichnis zu wechseln, dessen Name dem aktuellen Verzeichnis nahe kommt. Wenn Sie beispielsweise in sind /some/where/foo-old/deeply/nested/inside
und zu gehen möchten /some/where/foo-new/deeply/nested/inside
, geben Sie einfach ein cd old new
.
Um einer Variablen einen Wert zuzuweisen, schreibt man natürlich VARIABLE=VALUE
. Um den Wert einer Variablen interaktiv zu bearbeiten, führen Sie einfach vared VARIABLE
.
Zsh verfügt über eine Konfigurationsschnittstelle, die einige der gängigsten Einstellungen unterstützt, einschließlich vorgefertigter Rezepte für Dinge wie die Vervollständigung ohne Berücksichtigung der Groß- und Kleinschreibung. So führen Sie diese Schnittstelle (erneut) aus (die erste Zeile wird nicht benötigt, wenn Sie eine Konfigurationsdatei verwenden, die von bearbeitet wurde zsh-newuser-install
):
autoload -U zsh-newuser-install
zsh-newuser-install
Aus Gründen der Abwärtskompatibilität mit Versionen aus den 1990er Jahren sind viele der nützlichen Funktionen von zsh standardmäßig deaktiviert, da überhaupt keine Konfigurationsdatei vorhanden ist. zsh-newuser-install
schlägt einige empfohlene Funktionen zum Aktivieren vor.
Es gibt viele zsh-Konfigurationsframeworks im Web (viele davon sind auf Github ). Sie können eine bequeme Möglichkeit sein, mit einigen leistungsstarken Funktionen zu beginnen. Die Kehrseite der Medaille ist, dass sie Sie oft daran hindern, die Dinge so zu tun, wie der Autor es tut, also hindern sie Sie manchmal daran, die Dinge so zu tun, wie Sie es möchten. Verwenden Sie sie auf eigene Gefahr.
Das zsh-Handbuch enthält viele Informationen, ist jedoch häufig knapp und schwer verständlich geschrieben und enthält nur wenige Beispiele. Zögern Sie nicht, online nach Erklärungen und Beispielen zu suchen: Wenn Sie nur den leicht verständlichen Teil von zsh im Handbuch verwenden, werden Sie etwas verpassen. Zwei gute Ressourcen sind die Mailingliste zsh-users und Unix Stack Exchange . Eine umfangreiche Sammlung von Artikeln zum Wechseln zu zsh auf dem Mac finden Sie auf scriptingosx.com und ein nützliches Ruby-Skript, um Ihre Befehlshistorie mitzunehmen , finden Sie auf Github.
#!
und sind von der Standard-Login-Shell unbeeinflusst)bash
, aber eine aktuelle Version via bekomme, MacPorts
die neben der von Apple bereitgestellten Version 3.2 läuft bash
. Meiner Meinung nach hat sie zsh
viele großartige Funktionen, die ich wahrscheinlich nie verwenden werde ... wie die teure Kamera, die ich kürzlich gekauft habe!one[1]
? Es ist kein one
Array definiert.a
. Erweitert in bash $a
immer zum ersten Element des Arrays, selbst wenn darauf etwas folgt, das wie ein Array-Index aussieht.a=(one two); echo $a[1]
gedruckt wird one[1]
(das haben Sie dort oben in der Antwort geschrieben), stattdessen wird in zsh nur gedruckt one
. Ich musste wirklich viel neu lesen, um diese Bedeutung zu verstehen. Das Wechseln der Shell alle paar Wörter macht die Aussage meiner Meinung nach schwer lesbar. Könnte man es einfacher und weniger kompliziert ausdrücken?Wechseln Sie jetzt Ihre Schale und testen Sie – ohne Wartezeit.
chsh -s /bin/zsh
bash
der Syntax abhängen, werden weiterhin bash finden und aufrufen.Außerdem würde ich schätzen, dass 95 % der macOS-Benutzer keine Befehlszeile verwenden, und von denen, die dies tun, müssen weitere 95 % nichts Wesentliches oder überhaupt nichts ändern. (Ich wette, es ist eher so, dass 10% der 1%, die wissen, dass Shells existieren, etwas anderes tun müssen, als ein paar Zeilen in ihren .dot-Dateien zu portieren.)
Ihre Eingabeaufforderung wird sich ändern, und wenn Sie Ihre Eingabeaufforderung auf geändert haben bash
, ist der Weg, sie zu ändern, zsh
nicht schwieriger und nicht weniger dokumentiert als bash.
Die neueren Granaten würden niemals vom Boden abheben, wenn sie wichtige Gegenstände zerschmetterten oder eine schmerzhafte Anpassungsphase verursachten. Wenn Sie eine grundlegendere Veränderung wünschen und wirklich eine Muschel wollen, müssen Sie darüber nachdenken und es erfordert Training und die Absicht, sie anzunehmen – versuchen Sie es mit Fisch .
fish
. Ich benutze es jetzt seit zwei Jahren, aber die Inkompatibilität einiger kopierter/eingefügter Zeilen ist ermüdend. Andererseits ist es eine sehr praktische Shell (die automatischen Vorschläge ohne <kbd>Tab</kbd> sind ausgezeichnet)ksh
diese Falte jemals wirklich zu verlassen. Ich werde bash gerne hinter mir lassen und mich jetzt ganz persönlich mit zsh befassen.fish
ist nichts. Sie möchten wirklich ein einzigartiges Shell-Erlebnis, verwenden Sie tclsh
.Meine Shell-Skripte sind wirklich nicht so kompliziert
Haben Ihre Shell-Skripte Shebang-Zeilen (beginnen mit #! /bin/bash
oder ähnlich)? Wenn nicht, haben Sie möglicherweise unbeabsichtigt eine Bash-Funktion verwendet, bei der Skripts ohne einen Shebang mit Bash ausgeführt werden. Andere Shells wie dash oder zsh überlassen dies dem Betriebssystem, das normalerweise /bin/sh
stattdessen verwendet wird. /bin/sh
unter macOS ist und wird wahrscheinlich eine Kopie von bleiben /bin/bash
, aber die Ausführung von bash mit dem Namen sh
führt zu einem anderen Verhalten. Die Einzelheiten sind das Bash-Handbuch, 6.11 Bash POSIX mode . Ein paar Punkte:
- Bash sorgt dafür, dass die
POSIXLY_CORRECT
Variable gesetzt wird.
Diese Umgebungsvariable kann das Verhalten einer Reihe anderer Tools beeinflussen, insbesondere wenn Sie GNU-Tools installiert haben.
- Prozesssubstitution ist nicht verfügbar.
Prozesssubstitution ist die <(...)
or- >(...)
Syntax.
- Die eingebauten Funktionen
.
undsource
durchsuchen das aktuelle Verzeichnis nicht nach dem Dateinamen-Argument, wenn es bei der Suche nach PATH nicht gefunden wird.
Wenn Ihr Skript also . foo
erwartet hat, dass es eine Datei mit dem Namen im aktuellen Verzeichnis bezieht foo
, funktioniert das nicht. Sie sollten . ./foo
stattdessen tun.
Wie Sie anhand der Zahlen erraten können, gibt es viele kleine Unterschiede im Verhalten von bash im POSIX-Modus. Verwenden Sie am besten einen Shebang, wenn Sie Bash für Ihre Skripte verwenden möchten.
/bin/sh
oder /bin/bash
im Shebang verwenden. Zsh ist nur die standardmäßige interaktive Shell, sie hat Bash nicht überall ersetztzsh
😅
nicht verfügbar istIm Geiste, die Dinge einfach zu halten ...
Kann jemand einen einfachen praktischen Vergleich oder spezifische Stolpersteine liefern, die ich kennen muss, damit ich anfangen kann, darauf hinzuarbeiten, für die neue Shell bereit zu sein, wenn Catalina veröffentlicht wird?
Wenn Sie daran denken, die neue Standard-Shell zu verwenden, sollten Sie Folgendes in Betracht ziehen:
Auch wenn kein bestimmter Weg klar ist, sollten Ihnen diese drei Ansätze genug Vertrauen geben, um eine fundierte Entscheidung zu treffen, ohne den Problem- oder Lösungsraum zu überarbeiten.
fd0
Charles Duffy
dr.nixon
Seamus
bash version 3.2.57(1)
: Wissen Sie, ob Applebash
etwas "wichtiges" auf dem System verwendet?David Everitt