Met het nieuws dat Catalina standaard Zsh zal zijn in plaats van Bash , “m het vinden van veel resultaten die me vertellen over de switch, en dat het problemen kan veroorzaken met shell-scripts, maar ik “ben niet bekend genoeg met Zsh om te weten wat die problemen kunnen zijn.
Mijn shell-scripts zijn dat echt niet dat ingewikkeld, maar ik heb Bash alleen op macOS en Linux gebruikt – geen ervaring met Zsh. Kan iemand een eenvoudige praktische vergelijking geven, of specifieke struikelblokken die ik moet weten, zodat ik eraan kan werken om klaar te zijn voor de nieuwe shell wanneer Catalina wordt vrijgegeven?
Reacties
Antwoord
Allereerst enkele belangrijke dingen:
- Bash gaat niet weg . Als je al bash gebruikt, verandert er niets voor jou. Het enige dat verandert is dat zsh de standaard login-shell zal zijn voor nieuwe accounts, en zelfs dan kun je in plaats daarvan bash selecteren.
- Scripts worden niet beïnvloed . Wat verandert, is de shell voor interactief gebruik, d.w.z. de shell in terminals (en ook een paar andere dingen die de login-shell gebruiken, zoals crontabs). Als u een script in een bestand heeft met uitvoeringsrechten, beginnend met een shebang-regel zoals
#!/bin/bash
of#!/bin/sh
of#!/usr/bin/env bash
, het blijft precies zoals voorheen werken. - De syntaxis van Zsh is niet volledig compatibel met bash , maar het komt in de buurt. Veel code zal blijven werken, bijvoorbeeld typische aliassen en functies. De belangrijkste verschillen zitten in interactieve configuratiefuncties.
Nu, ervan uitgaande dat u overweegt naar zsh, wat al jaren een mogelijkheid is, hier zijn de belangrijkste verschillen die u zult tegenkomen. Dit is geen volledige lijst!
Belangrijkste verschillen voor interactief gebruik
Configuratiebestanden : bash leest (voornamelijk) .bashrc
in niet-login interactieve shells (maar macOS start standaard een login-shell in terminals), .profile
of in login-shells, en .inputrc
. Zsh leest (voornamelijk) .zshrc
(in alle interactieve shells) en .zprofile
(in login-shells). Dit betekent dat geen van uw bash-aanpassingen van toepassing zijn: u “zult ze moeten porten. U kunt de bestanden niet zomaar kopiëren omdat er veel dingen moeten worden aangepast.
Toetsbindingen gebruiken een compleet andere syntaxis. Bash gebruikt .inputrc
en de bind
ingebouwde om sleutels te binden aan readline-opdrachten . Zsh gebruikt de bindkey
ingebouwde om sleutels te binden aan zle-widgets . De meeste readline-opdrachten hebben een zsh-equivalent, maar het is niet altijd een perfecte gelijkwaardigheid.
Over sneltoetsen gesproken, als je Vi (m) gebruikt als je in-terminal editor maar niet als je command line mode in de shell, dan zal je merken dat zsh standaard naar vi editing mode gaat als EDITOR
of VISUAL
is ingesteld op vi
of vim
. bindkey -e
schakelt over naar emacs-modus.
Prompt : bash stelt de prompt in (voornamelijk) van PS1
die backslash-escapes . Zsh stelt de prompt voornamelijk in van PS1
die procent ontsnapt . De functionaliteit van bash” s PROMPT_COMMAND
is beschikbaar in zsh via de precmd
en preexec
hook-functies . Zsh heeft meer gemaksmechanismen om mooie prompts te bouwen, waaronder een promptthema-mechanisme .
Het basis opdrachtregelhistorie mechanismen (navigatie met omhoog / omlaag , zoek met Ctrl + R , geschiedenisuitbreiding met !!
en vrienden, laatste argument oproepen met Alt + . of $_
) werken op dezelfde manier, maar er zijn veel verschillen in de details, te veel om hier op te noemen. Je kunt je .bash_history
naar .zsh_history
kopiëren als je een shell-optie die het bestandsformaat verandert niet hebt gewijzigd.
Voltooiing : beide shells gaan standaard naar een basisaanvullingsmodus die meestal opdracht- en bestandsnamen voltooit, en schakelen naar een mooie modus door inclusief bash_completion
op bash of door compinit
in zsh uit te voeren. Je “zult een aantal commandos vinden die bash beter afhandelt en andere die zsh beter afhandelt. Zsh is meestal nauwkeuriger, maar geeft soms op waar bash iets doet dat niet correct is, maar wel verstandig. Om mogelijke aanvullingen voor een commando te specificeren, heeft zsh drie mechanismen:
- De “oud” voltooiingsmechanisme met
compctl
die u kunt vergeten. - Het ” nieuw “voltooiingsmechanisme met
compadd
en veel functies die beginnen met een onderstrepingsteken en een krachtig maar complex gebruikersconfiguratiemechanisme . - Een emulatie naar ondersteuning voor bash-voltooiingsfuncties die u kunt inschakelen door
bashcompinit
uit te voeren. De emulatie is niet 100% perfect, maar het werkt meestal.
Veel van bash “s shopt
instellingen hebben een overeenkomstige setopt
in zsh.
Zsh doe sn “t behandel #
standaard als een commentaarstart op de opdrachtregel, alleen in scripts (inclusief .zshrc
en dergelijke). Om interactieve opmerkingen in te schakelen, voert u setopt interactive_comments
uit.
Belangrijkste verschillen voor scripting
(en voor ervaren gebruikers natuurlijk op de opdrachtregel)
In bash neemt $foo
de waarde van foo
, splitst het in witruimte-tekens, en voor elk witruimte-gescheiden deel, als het jokertekens bevat en overeenkomt met een bestaand bestand, vervangt het het patroon door de lijst met overeenkomsten. Om alleen de waarde van foo
te krijgen, heb je "$foo"
nodig. Hetzelfde geldt voor opdrachtvervanging $(foo)
. In zsh is $foo
de waarde van foo
en is $(foo)
de uitvoer van foo
minus de laatste nieuwe regels, met twee uitzonderingen. Als een woord leeg raakt doordat lege variabelen zonder aanhalingstekens worden uitgebreid, wordt het “verwijderd (bijv. a=; b=; printf "%s\n" one "$a$b" three $a$b five
drukt one
af, een lege regel, three
, five
). Het resultaat van een niet-geciteerde opdrachtvervanging wordt gesplitst in witruimte, maar de stukken worden niet met jokertekens vergeleken.
Bash arrays worden geïndexeerd van 0 tot (lengte-1). Zsh-arrays worden geïndexeerd van 1 tot lengte. Met a=(one two three)
, in bash, is ${a[1]}
two
, maar in zsh is het “s one
. Als u in bash alleen naar een arrayvariabele verwijst zonder accolades, krijgt u het eerste element, bijv. $a
is one
en $a[1]
is one[1]
.In zsh breidt $a
zich uit naar de lijst met niet-lege elementen, en $a[1]
breidt uit naar het eerste element. Evenzo is in bash de lengte van een array ${#a}
; dit werkt ook in zsh, maar je kunt het eenvoudiger schrijven als $#a
. U kunt 0-indexering de standaard maken met setopt ksh_arrays
; dit schakelt ook de vereiste in om accolades te gebruiken om naar een array-element te verwijzen.
Bash heeft extra wildcardpatronen zoals @(foo|bar)
om overeen te komen met foo
of bar
, die alleen worden ingeschakeld met shopt -s extglob
. In zsh kun je deze patronen inschakelen met setopt ksh_glob
, maar er is ook een eenvoudiger te typen native syntaxis zoals (foo|bar)
, waarvan sommige setopt extended_glob
vereisen (doe zet dat in je .zshrc
, en het staat standaard aan in voltooiingsfuncties). **/
voor recursieve directory-traversal is altijd ingeschakeld in zsh.
In bash, als een jokertekenpatroon standaard niet overeenkomt met een bestand, blijft het links ongewijzigd. In zsh krijg je standaard een foutmelding, wat meestal de veiligste instelling is. Als je een jokertekenparameter aan een commando wilt geven, gebruik dan aanhalingstekens. Je kunt naar het bash-gedrag overschakelen met setopt no_nomatch
. U kunt niet-overeenkomende wildcard-patronen uitbreiden naar een lege lijst met setopt null_glob
.
In bash draait de rechterkant van een pijplijn in een subshell. In zsh draait het in de bovenliggende shell, dus je kunt dingen schrijven als somecommand | read output
.
Enkele leuke zsh features
Hier zijn een paar leuke zsh features die bash niet heeft ( althans niet zonder wat serieus elleboogvet). Nogmaals, dit is slechts een selectie van degene die ik het nuttigst vind.
Glob-kwalificaties staan overeenkomende bestanden toe op basis van metadata zoals hun tijdstempel, hun grootte, enz. Ze laten ook toe om de uitvoer te tweaken. De syntaxis is nogal cryptisch, maar het is buitengewoon handig. Hier zijn een paar voorbeelden:
-
foo*(.)
: alleen reguliere bestanden die overeenkomen metfoo*
en symbolische links naar reguliere bestanden, niet naar mappen en andere speciale bestanden. -
foo*(*.)
: alleen uitvoerbare reguliere bestanden die overeenkomen metfoo*
. -
foo*(-.)
: alleen reguliere bestanden die overeenkomen metfoo*
, geen symbolische links en andere speciale bestanden. -
foo*(-@)
: alleen bungelende symbolische links die overeenkomen metfoo*
. -
foo*(om)
: de bestanden die overeenkomen metfoo*
, gesorteerd op laatste wijzigingsdatum, meest recent eerst. Houd er rekening mee dat als u dit doorgeeft aanls
, het “zal zijn eigen sortering doen. Dit is vooral handig in … -
foo*(om[1,10])
: de 10 meest recente bestanden die overeenkomen metfoo*
, de meest recente eerst. -
foo*(Lm+1)
: bestanden die overeenkomen metfoo*
met een grootte van minstens 1 MB. -
foo*(N)
: hetzelfde alsfoo*
, maar als dit met geen enkel bestand overeenkomt, maak dan een lege lijst van de instelling van denull_glob
optie (zie hierboven). -
*(D)
: match alle bestanden inclusief puntbestanden ( behalve.
en..
). -
foo/bar/*(:t)
(met een history modifier ): de bestanden infoo/bar
, maar met alleen de basisnaam van het bestand. Bijvoorbeeld als er afoo/bar/qux.txt
, het “is uitgebreid alsqux.txt
. -
foo/bar/*(.:r)
: neem gewone bestanden onderfoo/bar
en verwijder de extensie. Bijv.foo/bar/qux.txt
wordt uitgebreid alsfoo/bar/qux
. -
foo*.odt(e\""REPLY=$REPLY:r.pdf"\")
: neem de lijst met bestanden die overeenkomen metfoo*.odt
, en vervang.odt
door.pdf
(ongeacht of de pdf bestand bestaat).
Hier zijn een paar nuttige zsh-specifieke wildcardpatronen .
-
foo*.txt~foobar*
: alle.txt
bestanden waarvan de naam begint metfoo
maar nietfoobar
. -
image<->.jpg(n)
: alle.jpg
bestanden waarvan de basisnaamimage
is gevolgd door een nummer, bijv.image3.jpg
enimage22.jpg
maar nietimage-backup.jpg
. De glob-kwalificatie(n)
zorgt ervoor dat de bestanden in numerieke volgorde worden weergegeven, dwzimage9.jpg
komt voorimage10.jpg
(je kunt dit de standaard maken, zelfs zonder-n
metsetopt numeric_glob_sort
).
Aan bestanden massaal hernoemen biedt zsh een erg handige tool: de zmv
functie . Aanbevolen voor uw .zshrc
:
autoload zmv alias zcp="zmv -C" zln="zmv -L"
Voorbeeld:
zmv "(*).jpeg" "$1.jpg" zmv "(*)-backup.(*)" "backups/$1.$2"
Bash heeft een paar manieren om transformaties toe te passen bij het nemen van de waarde van een variabele . Zsh heeft enkele van dezelfde en nog veel meer .
Zsh heeft een aantal kleine handige functies om mappen te wijzigen . Schakel setopt auto_cd
in om naar een directory te gaan wanneer u de naam typt zonder (bash heeft dit tegenwoordig ook). U kunt het formulier met twee argumenten gebruiken om cd
te wijzigen naar een directory waarvan de naam dicht bij de huidige directory ligt . Als u zich bijvoorbeeld “bevindt in /some/where/foo-old/deeply/nested/inside
en u wilt naar /some/where/foo-new/deeply/nested/inside
, typt u cd old new
.
Om een waarde aan een variabele toe te wijzen, schrijft u natuurlijk VARIABLE=VALUE
. Om bewerken de waarde van een variabele interactief, voer gewoon vared VARIABLE
uit.
Laatste advies
Zsh wordt geleverd met een configuratie-interface die enkele van de meest voorkomende instellingen ondersteunt, inclusief standaardrecepten voor zaken als niet-hoofdlettergevoelig aanvullen. Om deze interface (opnieuw) uit te voeren (de eerste regel is niet nodig als u “een configuratiebestand gebruikt dat is bewerkt door zsh-newuser-install
):
autoload -U zsh-newuser-install zsh-newuser-install
Uit de doos, zonder configuratiebestand, zijn veel van de handige functies van zsh uitgeschakeld voor achterwaartse compatibiliteit met versies uit de jaren 90. zsh-newuser-install
suggereert een aantal aanbevolen functies om in te schakelen.
Er zijn veel zsh-configuratieframeworks op internet (veel ervan zijn op Github ). Ze kunnen een handige manier zijn om aan de slag te gaan met enkele krachtige functies. De keerzijde van de medaille is dat ze je vaak opsluiten in het doen van dingen zoals de auteur dat doet, dus soms zullen ze je ervan weerhouden dingen te doen zoals je wilt. Gebruik ze op eigen risico.
De zsh handleiding bevat veel informatie, maar het is vaak geschreven op een manier die beknopt en moeilijk te volgen is, en heeft weinig voorbeelden. Aarzel niet om online naar uitleg en voorbeelden te zoeken: als je alleen het deel van zsh dat is gemakkelijk te begrijpen in de handleiding, je zult het missen. Twee goede bronnen zijn de zsh-users mailinglijst en Unix Stack Exchange . Een uitgebreide verzameling artikelen over het overschakelen naar zsh op de Mac is te vinden op scriptingosx.com en een nuttige Ruby-script om je commandogeschiedenis mee te nemen , is te vinden op Github.
Reacties
- Nu vraag ik ‘ me af of de geweldige ctrl-o werkt op zsh. Het werkt natuurlijk ook niet ‘ op Mac OS op bash, dus het ‘ is niet echt relevant voor dit antwoord of deze site. Ik kon ‘ geen informatie vinden over ctrl-o in zsh in een snelle online zoekopdracht, maar nogmaals, de informatie over ctrl-o in bash is ook universeel onnauwkeurig …
- @Jasper Ik wist niet ‘ niet dat bash dit had. Afgaande op de beschrijving, doet Co hetzelfde in zsh met de standaard sneltoetsen.
- Ik was blij te zien dat je de ” Enkele leuke zsh-functies hebt toegevoegd ” en lees het met spanning om te proberen te begrijpen waarom Apple de beslissing zou hebben genomen om over te schakelen van het extreem gewone Bash naar het veel minder populaire Zsh. Ik heb geen ‘ iets gevonden, zelfs niet op afstand, dwingend daar om de omschakeling te rechtvaardigen. Het ‘ is duidelijk een niet-uitputtende lijst, maar liet je de headline-features van Zsh weg omdat ze ‘ voor de hand zouden moeten liggen? Wat mis ik hier?
- @CodyGray Ik weet het niet ‘ en Apple heeft geen ‘ de gewoonte om zichzelf te rechtvaardigen. Het kan iets te maken hebben gehad met het feit dat als de situatie was omgekeerd, er geen ‘ sectie zou zijn “leuke bash-functies”.Of het kan zijn omdat de laatste niet-GPLv3 bash erg oud wordt terwijl zsh een meer liberale licentie heeft.
- Ik heb begrepen dat de vergunningverlening was in wezen de enige reden. Het ‘ is ook waarom bash op macOS nog steeds v3 is. Op straat wordt gezegd dat bash ‘ t niet wordt bijgewerkt en de verwachting is dat het zal worden verwijderd, tenzij de eindgebruiker het installeert via brew of etc. Mijn plan was om over te schakelen naar zsh eerder eerder dan later voor macOS, maar voorlopig vasthouden aan bash op Debian.
Antwoord
Wijziging nu je shell en test – je hoeft niet te wachten.
chsh -s /bin/zsh
- Alle scripts die afhankelijk zijn van
bash
syntaxis zal nog steeds bash vinden en aanroepen. - dezelfde bash van Mojave wordt verzonden op Catalina en gemigreerde gebruikers behouden hun oude shell.
- Veel blogs hebben geweldige artikelen over het verplaatsen van voorkeurenbestanden – hier is er een – https://scriptingosx.com/2019/06/moving-to-zsh-part-2-configuration-files/
- Armin maakte van zijn blogserie een echt boek met ongeveer 22.000 woorden.
Ik schat ook dat 95% van de macOS-gebruikers geen opdrachtregel gebruikt en van degenen die dat wel doen, nog eens 95% hoeft niets belangrijks te veranderen of alle. (Ik wed dat het meer is alsof 10% van de 1% die weten dat shells bestaan iets anders moeten doen dan een paar regels in hun .dot-bestanden overzetten)
Uw prompt zal veranderen en als u uw prompt op bash
hebt gewijzigd, is de manier waarop u deze op zsh
kunt wijzigen niet moeilijker en niet minder gedocumenteerd dan bash.
De nieuwere granaten zouden nooit van de grond komen als ze grote items braken of een pijnlijke aanpassingsperiode veroorzaakten. Als je een meer fundamentele verandering wilt en echt een schaal wilt waar je over na moet denken en training en intentie nodig hebt om te adopteren – probeer fish .
Opmerkingen
- Ik ben in conflict met
fish
. Ik gebruik het nu twee jaar, maar de incompatibiliteit van sommige gekopieerde / geplakte eenregelige regels is vermoeiend. Aan de andere kant is het een erg handige shell (de automatische suggesties zonder < kbd > Tab < / kbd > zijn uitstekend) - Ik wilde van vis houden, ik wil nog steeds van vis houden, maar ik heb te veel schelpconstructies van een decennium in
ksh
om die fold ooit echt te verlaten. Ik laat de bash graag achter me en omarm zsh nu volledig, persoonlijk. - Ik ben grondig onder de indruk van vis. Het is eigenlijk gewoon weer een Unix-achtige schaal met een beetje minder cruft. (Het zegt letterlijk ” Eindelijk een commandoregel-shell voor de jaren 90 ” op de startpagina.) De enige nieuwe shell die Ik heb de afgelopen jaren gezien dat het echt iets nieuws aan de (reguliere) tafel bracht, PowerShell was, dat helaas veel te lang tot Windows beperkt was, en nog steeds beperkt is tot .NET. Er is nog steeds niet dat veel nieuws in PowerShell dat nog niet ‘ t is gedaan, b.v. in DCL of JCL, maar het is op een (enigszins) smaakvolle manier gedaan.
- @bmike Waarom zou je dan niet gewoon ksh gebruiken? Apple levert de meest recente versie. Zelf heb ik bash lang geleden gepunt en zie ik geen reden om zsh nu te gaan gebruiken.
- Dit antwoord beschrijft echter niet ‘ de verschillen tussen bash en zsh helemaal niet … en ik ‘ m niet helemaal zeker waarom specificeren dat ” 95% van de macOS-gebruikers ‘ t gebruik de opdrachtregel ” is relevant voor een vraag van een gebruiker die duidelijk bash gebruikt.
Antwoord
Mijn shell-scripts zijn echt niet zo ingewikkeld
Hebben je shell-scripts shebang-regels (begin met #! /bin/bash
of iets dergelijks)? Als dat niet het geval is, heb je misschien onbedoeld een bash-functie gebruikt, waar het scripts uitvoert zonder een shebang met bash. Andere shells, zoals dash of zsh, laten het over aan het besturingssysteem, dat in plaats daarvan meestal /bin/sh
gebruikt. /bin/sh
op macOS is en blijft waarschijnlijk een kopie van /bin/bash
, maar voert bash uit met de naam sh
zorgt ervoor dat het zich anders gedraagt. De details zijn de Bash-handleiding, 6.11 Bash POSIX-modus . Enkele punten:
- Bash zorgt ervoor dat de
POSIXLY_CORRECT
variabele wordt ingesteld.
Deze omgevingsvariabele kan het gedrag van een aantal andere tools beïnvloeden, vooral als je GNU tools hebt geïnstalleerd.
- Procesvervanging is niet beschikbaar.
Procesvervanging is de <(...)
of >(...)
syntaxis.
- De
.
ensource
ingebouwde versies zoeken niet in de huidige directory naar de bestandsnaam argument als het niet wordt gevonden door te zoeken naar PATH.
Dus als uw script . foo
verwacht dat het een bestand met de naam foo
in de huidige directory plaatst, dat “niet zal werken. Je zou in plaats daarvan . ./foo
moeten doen.
Zoals je aan de hand van de cijfers kunt raden, zijn er veel kleine verschillen in het gedrag van bash in POSIX-modus. Gebruik het beste een shebang als je bash voor je scripts wilt gebruiken.
Opmerkingen
- Bij het controleren lijkt het erop dat de overgrote meerderheid van de shell-scripts die ik heb geschreven e of gebruik ofwel expliciet state / bin / bash in de shebang (heel weinig) of state / bin / sh (bijna allemaal), zodat dat in ieder geval geen probleem zou moeten zijn. Bedankt.
- Ik denk dat procesvervanging nu beschikbaar is. Ik heb het ook met succes op Catalina geprobeerd. Zie: zsh.sourceforge.net/Intro/intro_7.html
- @Siu heeft nog steeds ‘ t help-scripts die
/bin/sh
of/bin/bash
gebruiken in de shebang. Zsh is alleen de standaard interactieve shell, het heeft ‘ niet overal bash vervangen - @muru Ah, ik begrijp het. Ik heb het antwoord niet zorgvuldig gelezen en dacht dat de auteur het had over procesvervanging is niet beschikbaar in
zsh
😅
Antwoord
In de geest om dingen eenvoudig te houden …
Kan iemand een eenvoudig praktisch vergelijking, of specifieke struikelblokken die ik moet weten, zodat ik eraan kan werken om klaar te zijn voor de nieuwe shell wanneer Catalina wordt uitgebracht?
Als u overweegt om de nieuwe standaard shell te gebruiken, overweeg dan:
- Als u Zsh een draai wilt geven en gevoel enkele van de verschillen wilt geven zonder de instellingen van de shell op uw machine te veranderen, zou Powerline10k in een Docker-container kunnen proberen en kijken of het jouw ding is.
- Als je niet alle bellen en fluitjes en gebruik Bash voor slechts basisscripts, het is vrij eenvoudig om uw shell in te stellen zoals anderen hier hebben uitgelegd. En als u besluit dat u functies in Bash 5 wilt gebruiken, is het “een redelijk triviale upgrade voor macOS .
- Als je de overdraagbaarheid van je scripts wilt verbeteren, zodat ze “waarschijnlijker functioneren zoals verwacht in beide shells, test je ze op POSIX-compliance en verwijder eventuele “bashisms”. Ik “heb hiervoor ShellCheck gebruikt en het werkt best goed voor minder gecompliceerde scripts.
Hoewel geen bepaald pad duidelijk is deze drie benaderingen zouden u voldoende vertrouwen moeten geven om een weloverwogen beslissing te nemen zonder het probleem of de oplossingsruimte te overdrijven.
bash version 3.2.57(1)
: weet u of Applebash
voor iets ” belangrijk ” op het systeem?