Wat zijn de praktische verschillen tussen Bash en Zsh?

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

  • Al dit geroezemoes dat je hebt gelezen, heeft veel te maken met niets. Het besturingssysteem kent een ” standaard ” shell bij het aanmaken van nieuwe gebruikers, geen reden meer. Bash is niet ‘ t gaat weg, en je kunt dat gebruiken als je shell of een van de andere shells c momenteel aangeboden.
  • Spreken als iemand die ‘ s beide gebruikte en op bash belandde – het enige dat me echt, diep ongelukkig maakte met zsh, was zijn beslissing om POSIX-compliance te doorbreken wanneer de standaard toegegeven slechte ontwerpbeslissingen canonicaliseert op een manier die het gemakkelijk maakte om slordig te worden over correctheid bij het schrijven van scripts die compatibel moesten zijn met andere, strikt POSIX-superset-shells. Helaas kan dat ” enige ” een behoorlijk grote zijn. Toch gaat ksh93 niet ‘ t weg, en iedereen die serieus bezig is met bash zou ‘ hoe dan ook niet de oude 3.x-release van Apple gebruiken.
  • Als een vervolg – Catalina gisteren geïnstalleerd, overgeschakeld naar zsh, bash_history geïmporteerd en over een aantal van mijn favoriete aliassen uit bash_profile gekopieerd, lijkt er niets kapot te zijn. Waardeer alle informatie die door iedereen hier wordt verstrekt en hopelijk helpt het ook anderen.
  • @CharlesDuffy: goede opmerking. WRT is ” serieus “, en gebruikt bash version 3.2.57(1): weet u of Apple bash voor iets ” belangrijk ” op het systeem?

Antwoord

Allereerst enkele belangrijke dingen:

  1. 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.
  2. 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.
  3. 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:

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 met foo* en symbolische links naar reguliere bestanden, niet naar mappen en andere speciale bestanden.
  • foo*(*.): alleen uitvoerbare reguliere bestanden die overeenkomen met foo*.
  • foo*(-.): alleen reguliere bestanden die overeenkomen met foo*, geen symbolische links en andere speciale bestanden.
  • foo*(-@): alleen bungelende symbolische links die overeenkomen met foo*.
  • foo*(om): de bestanden die overeenkomen met foo*, gesorteerd op laatste wijzigingsdatum, meest recent eerst. Houd er rekening mee dat als u dit doorgeeft aan ls, het “zal zijn eigen sortering doen. Dit is vooral handig in …
  • foo*(om[1,10]): de 10 meest recente bestanden die overeenkomen met foo*, de meest recente eerst.
  • foo*(Lm+1): bestanden die overeenkomen met foo* met een grootte van minstens 1 MB.
  • foo*(N): hetzelfde als foo*, maar als dit met geen enkel bestand overeenkomt, maak dan een lege lijst van de instelling van de null_glob optie (zie hierboven).
  • *(D): match alle bestanden inclusief puntbestanden ( behalve . en ..).
  • foo/bar/*(:t) (met een history modifier ): de bestanden in foo/bar, maar met alleen de basisnaam van het bestand. Bijvoorbeeld als er a foo/bar/qux.txt, het “is uitgebreid als qux.txt.
  • foo/bar/*(.:r): neem gewone bestanden onder foo/bar en verwijder de extensie. Bijv. foo/bar/qux.txt wordt uitgebreid als foo/bar/qux.
  • foo*.odt(e\""REPLY=$REPLY:r.pdf"\"): neem de lijst met bestanden die overeenkomen met foo*.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 met foo maar niet foobar.
  • image<->.jpg(n): alle .jpg bestanden waarvan de basisnaam image is gevolgd door een nummer, bijv.image3.jpg en image22.jpg maar niet image-backup.jpg. De glob-kwalificatie (n) zorgt ervoor dat de bestanden in numerieke volgorde worden weergegeven, dwz image9.jpg komt voor image10.jpg (je kunt dit de standaard maken, zelfs zonder -n met setopt 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 

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:

  1. 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.

  1. Procesvervanging is niet beschikbaar.

Procesvervanging is de <(...) of >(...) syntaxis.

  1. De . en source 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.

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *