Vilka är de praktiska skillnaderna mellan Bash och Zsh?

Med nyheten att Catalina kommer att vara Zsh istället för Bash , jag ”m att hitta massor av resultat som berättar om växeln och att det kan orsaka problem med skalskript, men jag är inte tillräckligt bekant med Zsh för att veta vad dessa problem kan vara.

Mina skalskript är verkligen inte så komplicerat, men jag har bara någonsin använt Bash på macOS och Linux – ingen erfarenhet av Zsh. Kan någon ge en enkel praktisk jämförelse, eller specifika snubblar som jag kommer att behöva veta, så att jag kan börja arbeta för att vara redo för nytt skal när Catalina släpps?

Kommentarer

  • All denna hubbub som du har läst är mycket att göra åt ingenting. OS tilldelar en ” standard ” skal när du skapar nya användare, ingen anledning mer. Bash är inte ’ t går bort, och du kan använda det som ditt skal eller någon av de andra skalen c snarast erbjuds.
  • Att tala som någon som ’ använde båda och landade på bash – det enda som gjorde mig riktigt, djupt missnöjd med zsh var dess beslut för att bryta POSIX-överensstämmelse när standarden kanoniserar riktigt dåliga designbeslut på sätt som gjorde det enkelt att bli slarvig om korrekthet när man försöker skriva manus som behövde vara kompatibla med andra, strikt POSIX-superset-skal. Tyvärr kan den ” enda ” vara ganska stor. Ändå försvinner ksh93 inte ’ t, och alla som är seriösa om bash skulle inte ’ inte använda de antika 3.x-utgivningarna från Apple.
  • Som en uppföljning – installerade Catalina igår, bytte till zsh, importerade bash_history och kopierade över några av mina föredragna alias från bash_profile, ingenting verkar ha brutits. Uppskatta all information som tillhandahålls av alla här och förhoppningsvis hjälper den också andra.
  • @CharlesDuffy: Bra kommentar. WRT är ” seriös ” och använder bash version 3.2.57(1): Vet du om Apple använder bash för något ” viktigt ” på systemet?

Svar

Först några viktiga saker:

  1. Bash försvinner inte . Om du redan använder bash ändras ingenting för dig. Allt som ändras är att zsh är standardinloggningsskalet för nya konton, och även då kan du välja bash istället.
  2. Skript påverkas inte . Det som ändras är skalet för interaktiv användning, dvs skalet i terminaler (och även några andra saker som använder inloggningsskalet, till exempel crontabs). Om du har ett skript i en fil med exekveringsbehörigheter, börjar med en shebang-rad som #!/bin/bash eller #!/bin/sh eller #!/usr/bin/env bash, det kommer att fortsätta fungera precis som tidigare.
  3. Zshs syntax är inte helt kompatibel med bash , men det är nära. Mycket kod fortsätter att fungera, till exempel typiska alias och funktioner. De viktigaste skillnaderna är i interaktiva konfigurationsfunktioner.

Nu, förutsatt att du överväger att byta till zsh, vilket har varit en möjlighet i flera år, här är de viktigaste skillnaderna du kommer att stöta på. Detta är inte en uttömmande lista!

Huvudskillnader för interaktiv användning

Konfigurationsfiler : bash läser (huvudsakligen) .bashrc i interaktiva skal utan inloggning (men macOS startar ett inloggningsskal i terminaler som standard), .profile eller i inloggningsskal och .inputrc. Zsh läser (huvudsakligen) .zshrc (i alla interaktiva skal) och .zprofile (i inloggningsskal). Det betyder att ingen av dina bash-anpassningar kommer att gälla: du måste porta dem över. Du kan inte bara kopiera filerna eftersom många saker behöver justeras.

Nyckelbindningar använder helt annan syntax. Bash använder .inputrc och bind inbyggda för att binda nycklar till readline-kommandon . Zsh använder bindkey inbyggd för att binda nycklar till zle-widgets . De flesta kommandon för readline har en zsh-ekvivalent, men det är inte alltid en perfekt ekvivalens.

På tal om nyckelbindningar, om du använder Vi (m) som din terminalredigerare men inte som ditt kommandoradsläge i skalet, kommer du att märka att zsh är standard till vi-redigeringsläge om EDITOR eller VISUAL är inställd på vi eller vim . bindkey -e växlar till emacs-läge.

Fråga : bash ställer in prompten (huvudsakligen) från PS1 som innehåller bakåtvänd snedstreck flyr . Zsh ställer in prompten huvudsakligen från PS1 som innehåller procent flyr . Funktionen för bash” s PROMPT_COMMAND finns i zsh via precmd och preexec krokfunktioner . Zsh har fler bekvämlighetsmekanismer för att skapa snygga uppmaningar inklusive en snabb temamekanism .

Den grundläggande kommandoradshistorik mekanismer (navigering med Upp / Ner , sök med Ctrl + R , historiaxpansion med !! och vänner, sista argumentet återkallas med Alt + . eller $_) fungerar på samma sätt, men det finns många skillnader i detaljerna, för många att lista här. Du kan kopiera din .bash_history till .zsh_history om du inte har ändrat ett skalalternativ som ändrar filformatet.

Slutförande : båda skalen är standard till ett grundläggande kompletteringsläge som mest kompletterar kommandon och filnamn och byter till ett snyggt läge genom att inklusive bash_completion på bash eller genom att köra compinit i zsh. Du hittar några kommandon som bash hanterar bättre och andra som zsh hanterar bättre. Zsh är vanligtvis mer exakt, men ger ibland upp där bash gör något som inte är korrekt men som är förnuftigt. För att specificera möjliga kompletteringar för ett kommando har zsh tre mekanismer:

Många av bash ”s shopt inställningar har en motsvarande setopt i zsh.

Zsh doe sn ”t behandla # som en kommentar startar på kommandoraden som standard, endast i skript (inklusive .zshrc och liknande). För att aktivera interaktiva kommentarer, kör setopt interactive_comments .

Huvudskillnader för skript

(och för kraftanvändare på kommandoraden förstås)

I bash tar $foo värdet av foo, delar upp det med blankstegstecken och för varje del som skiljs åt mellanrum, om den innehåller jokertecken och matchar en befintlig fil, ersätter mönstret med listan med matchningar. För att bara få värdet av foo behöver du "$foo". Detsamma gäller kommandosubstitution $(foo). I zsh är $foo värdet av foo och $(foo) är utdata från foo minus dess slutliga nystreck, med två undantag. Om ett ord blir tomt på grund av expanderande tomma ociterade variabler tas det bort (t.ex. a=; b=; printf "%s\n" one "$a$b" three $a$b five skriver ut one, en tom rad, three, five). Resultatet av ett obekräftat kommandosubstitution delas i det vita utrymmet men bitarna genomgår inte matchning med jokertecken.

Bash matriser indexeras från 0 till (längd-1). Zsh-matriser indexeras från 1 till längd. Med a=(one two three), i bash är ${a[1]} two, men i zsh är det ”s one. I bash, om du bara refererar till en arrayvariabel utan parenteser, får du det första elementet, t.ex. $a är one och $a[1] är one[1].I zsh expanderar $a till listan över icke-tomma element och $a[1] utvidgas till det första elementet. På samma sätt, i bash är längden på en array ${#a}; detta fungerar också i zsh men du kan skriva det enklare som $#a. Du kan göra 0-indexering som standard med setopt ksh_arrays ; detta aktiverar också kravet att använda hängslen för att hänvisa till ett arrayelement.

Bash har extra jokerteckenmönster såsom @(foo|bar) för att matcha foo eller bar, som endast är aktiverade med shopt -s extglob. I zsh kan du aktivera dessa mönster med setopt ksh_glob, men det finns också en enklare typ att native syntax såsom (foo|bar), varav vissa kräver setopt extended_glob sätt det i din .zshrc, och det är som standard i färdigställningsfunktioner). **/ för rekursiv katalogtrafik är alltid aktiverat i zsh.

I bash, som standard, om ett jokerteckenmönster inte matchar någon fil, är det kvar oförändrad. I zsh kommer du som standard att få ett fel, vilket vanligtvis är den säkraste inställningen. Om du vill skicka en jokerteckenparameter till ett kommando, använd citat. Du kan växla till bash-beteendet med setopt no_nomatch . Du kan få icke-matchande jokerteckenmönster att expandera till en tom lista istället med setopt null_glob .

I bash körs den högra sidan av en pipeline i en subshell. I zsh körs den i det överordnade skalet, så du kan skriva saker som somecommand | read output.

Några fina zsh-funktioner

Här är några fina zsh-funktioner som bash inte har ( åtminstone inte utan något allvarligt armbågsfett). Återigen är detta bara ett urval av de som jag anser vara mest användbara.

Glob-kval tillåter matchande filer baserat på metadata som deras tidsstämpel, storlek osv. De tillåter också att justera utdata. Syntaxen är ganska kryptisk, men den är extremt bekväm. Här är några exempel:

  • foo*(.): endast vanliga filer som matchar foo* och symboliska länkar till vanliga filer, inte kataloger och andra specialfiler.
  • foo*(*.): endast körbara vanliga filer som matchar foo*.
  • foo*(-.): endast vanliga filer som matchar foo*, inte symboliska länkar och andra specialfiler.
  • foo*(-@): endast hängande symboliska länkar som matchar foo*.
  • foo*(om): filerna som matchar foo*, sorterade efter senaste modifieringsdatum, senast först. Observera att om du skickar detta till ls, det kommer att göra sin egen sortering. Detta är särskilt användbart i …
  • foo*(om[1,10]): de tio senaste filerna som matchar foo*, de senaste först.
  • foo*(Lm+1): filer som matchar foo* vars storlek är minst 1 MB.
  • foo*(N): samma som foo*, men om detta inte matchar någon fil, skapa en tom lista oavsett för inställningen av alternativet null_glob (se ovan).
  • *(D): matcha alla filer inklusive punktfiler ( utom . och ..).
  • foo/bar/*(:t) (med en historikmodifierare ): filerna i foo/bar, men med endast basnamnet på filen. Till exempel om det finns en foo/bar/qux.txt, den utvidgas som qux.txt.
  • foo/bar/*(.:r): ta vanliga filer under foo/bar och ta bort tillägget. T.ex. foo/bar/qux.txt utökas som foo/bar/qux.
  • foo*.odt(e\""REPLY=$REPLY:r.pdf"\"): ta lista över filer som matchar foo*.odt och ersätt .odt med .pdf (oavsett om PDF filen finns).

Här är några användbara zsh-specifika jokerteckenmönster .

  • foo*.txt~foobar*: alla .txt filer vars namn börjar med foo men inte foobar.
  • image<->.jpg(n): alla .jpg filer vars basnamn är image följt av ett nummer, t.ex.image3.jpg och image22.jpg men inte image-backup.jpg. Globkvalificatorn (n) gör att filerna listas i numerisk ordning, dvs image9.jpg kommer innan image10.jpg (du kan göra detta till standard även utan -n med setopt numeric_glob_sort ).

Till massbyta namn på filer ger zsh en mycket bekväm verktyg: zmv -funktionen . Föreslås för din .zshrc:

autoload zmv alias zcp="zmv -C" zln="zmv -L" 

Exempel:

zmv "(*).jpeg" "$1.jpg" zmv "(*)-backup.(*)" "backups/$1.$2" 

Bash har några sätt att tillämpa transformationer när värdet på en variabel tas . Zsh har något av samma och många fler .

Zsh har ett antal små praktiska funktioner för att ändra kataloger . Aktivera setopt auto_cd för att byta till en katalog när du skriver namnet utan att behöva skriva cd (bash har det också nuförtiden). Du kan använda tvåargumentformulär till cd för att byta till en katalog vars namn ligger nära den aktuella katalogen . Om du till exempel ”återkommer till /some/where/foo-old/deeply/nested/inside och vill gå till /some/where/foo-new/deeply/nested/inside, skriv bara cd old new.

För att tilldela ett värde till en variabel skriver du naturligtvis VARIABLE=VALUE. För att redigera värdet på en variabel interaktivt, kör bara vared VARIABLE .

Slutliga råd

Zsh levereras med ett konfigurationsgränssnitt som stöder några av de vanligaste inställningarna, inklusive konserverade recept för saker som skiftlägeskänslig slutförande. För att (åter) köra detta gränssnitt (den första raden behövs inte om du använder en konfigurationsfil som har redigerats av zsh-newuser-install):

autoload -U zsh-newuser-install zsh-newuser-install 

Utanför lådan, utan någon konfigurationsfil alls, är många av zshs användbara funktioner inaktiverade för bakåtkompatibilitet med 1990-talsversionerna. zsh-newuser-install föreslår några rekommenderade funktioner att aktivera.

Det finns många zsh-konfigurationsramar på webben (många av dem är på Github ). De kan vara ett bekvämt sätt att komma igång med några kraftfulla funktioner. Baksidan av myntet är att de ofta låser dig i att göra saker som författaren gör, så ibland hindrar de dig från att göra saker som du vill. Använd dem på egen risk.

zsh manual har mycket information, men den är ofta skriven på ett sätt som är kort och svår att följa, och har få exempel. Tveka inte att söka efter förklaringar och exempel online: om du bara använder delen av zsh som är lätt att förstå i manualen kommer du att missa. Två bra resurser är e-postlistan för zsh-användare och Unix Stack Exchange . En omfattande samling artiklar om hur man byter till zsh på mac finns på scriptingosx.com och är användbart Ruby-skript för att ta med dig din kommandohistorik finns på Github.

Kommentarer

  • Nu

undrar om den fantastiska ctrl-o fungerar på zsh. Naturligtvis fungerar det inte ’ på Mac OS på bash, så det ’ är inte riktigt relevant för det här svaret eller webbplatsen. Jag kunde ’ inte hitta någon information om ctrl-o i zsh i en snabb online-sökning, men då är informationen om ctrl-o i bash också universellt felaktig …

  • @Jasper Jag visste inte ’ att bash hade det här. Efter beskrivningen gör Co samma sak i zsh med standardnyckelbindningarna.
  • Jag var glad att se att du inkluderade ” Några fina zsh-funktioner ” avsnittet, och läs det oroligt för att försöka förstå varför Apple kanske har fattat beslutet att byta från den extremt vanliga Bash till den mycket mindre populära Zsh. Jag hittade inte ’ alls, till och med fjärrövertygande där för att motivera växlingen. Det ’ är uppenbarligen en icke uttömmande lista, men utelämnade du rubrikfunktionerna i Zsh eftersom de ’ skulle vara uppenbara? Vad saknar jag här?
  • @CodyGray Jag vet inte ’ och Apple är inte ’ t för vana att rättfärdiga sig själva. Det kan ha haft något att göra med det faktum att om situationen hade vänt, skulle det inte finnas ’ ett avsnitt ”fina bash-funktioner”.Eller det kan bero på att den senaste icke-GPLv3-basen blir riktigt gammal medan zsh har en mer liberal licens.
  • Min uppfattning var att licensieringen var i huvudsak den enda anledningen. Det ’ är också varför bash på macOS fortfarande är v3. Ordet på gatan är att bash vann ’ t uppdateras och förväntningen är att den kommer att tas bort, såvida inte slutanvändaren installerar den via brygga eller etc. Min plan var att byta till zsh förr snarare än senare för macOS, men håller fast vid Debian för tillfället.
  • Svar

    Ändra ditt skal nu och testa – du behöver inte vänta.

    chsh -s /bin/zsh 

    Jag skulle också uppskatta att 95% av macOS-användare inte använder en kommandorad och av dem som gör det, behöver 95% inte ändra någonting betydande eller vid Allt. (Jag skulle vilja satsa på det är mer som 10% av de 1% som vet att skal finns måste göra något annat än att porta ett par rader i sina .dot-filer)

    Din uppmaning kommer att ändras och om du ändrade din uppmaning på bash är sättet att ändra det på zsh inte svårare och inte mindre dokumenterat än bash.

    De nyare skalen skulle misslyckas med att komma av marken om de bröt större föremål eller orsakade en smärtsam anpassningsperiod. Om du vill ha en mer grundläggande förändring och verkligen vill ha ett skal måste du tänka på och kräver träning och avsikt att anta – prova fisk .

    Kommentarer

    • Jag är i konflikt med fish. Jag använder den i två år nu men inkompatibiliteten med vissa kopior / klistrade enrader är tröttsam. Å andra sidan är det ett mycket praktiskt skal (de automatiska förslagen utan < kbd > Flik < / kbd > är utmärkta)
    • Jag ville älska fisk, jag vill fortfarande älska fisk, men jag har för många skalkonstruktioner från ett decennium i ksh för att verkligen lämna det vecket. Jag ska gärna lämna bash bakom mig och helt omfamna zsh nu, personligen.
    • Jag är helt överväldigad av fisk. Det är egentligen ännu ett Unix-liknande skal med lite mindre grovhet. (Det står bokstavligen ” Slutligen ett kommandoradsskal för 90-talet ” trots allt.) Det enda nya skalet som Jag har sett de senaste åren som faktiskt tog något nytt till (mainstream) -tabellen, var PowerShell, som tyvärr var begränsad till Windows alldeles för länge och fortfarande är begränsad till .NET. Det finns fortfarande inte mycket nytt i PowerShell som inte har ’ inte redan gjorts t.ex. i DCL eller JCL, men det har gjorts på ett (något) smakfullt sätt.
    • @bmike Varför inte bara använda ksh då? Apple skickar den senaste versionen. Själv tappade jag bash för länge sedan och ser ingen anledning att börja använda zsh nu.
    • Detta svar beskriver inte ’ t skillnaderna mellan bash och zsh alls … och jag ’ är inte riktigt säker på varför jag anger att ” 95% av macOS-användare inte ’ Använd kommandoraden ” är relevant för en fråga av en användare som uppenbarligen använder bash.

    Svar

    Mina skalskript är verkligen inte så komplicerade

    Har dina skalskript shebanglinjer (börja med #! /bin/bash eller liknande)? Om inte, kanske du har använt en bash-funktion oavsiktligt, där den kör skript utan shebang med bash. Andra skal, som dash eller zsh, lämnar det upp till operativsystemet, som vanligtvis använder /bin/sh istället. /bin/sh på macOS är och kommer förmodligen att finnas kvar en kopia av /bin/bash, men kör bash med namnet sh gör att det har olika beteende. Specifikationerna är Bash-manualen, 6.11 Bash POSIX-läge . Några punkter:

    1. Bash ser till att variabeln POSIXLY_CORRECT är inställd.

    Denna miljövariabel kan påverka beteendet hos ett antal andra verktyg, särskilt om du har installerat GNU-verktyg.

    1. Processersättning är inte tillgänglig.

    Processersättning är <(...) eller >(...) syntax.

    1. Inbyggt . och source söker inte i den aktuella katalogen efter filnamnet argument om det inte hittas genom att söka PATH.

    Så om ditt skript gjorde . foo förväntar oss att den kommer till en fil med namnet foo i den aktuella katalogen som inte fungerar. Du bör göra . ./foo istället.

    Som du kan gissa från siffrorna finns det många mindre skillnader i beteende för bash i POSIX-läge. Använd bäst en shebang om du menar att använda bash för dina skript.

    Kommentarer

    • När du kontrollerar ser det ut som de allra flesta skalskript jag bryter e eller använd antingen uttryckligen state / bin / bash i shebang (väldigt få av dem) eller state / bin / sh (nästan alla), så det borde åtminstone inte vara ett problem. Tack.
    • Jag tror att processersättning är tillgänglig nu. Jag försökte det också på Catalina framgångsrikt. Se: zsh.sourceforge.net/Intro/intro_7.html
    • @Siu vann fortfarande ’ t hjälpskript som använder /bin/sh eller /bin/bash i shebang. Zsh är bara det interaktiva skalet som standard, det ersatte inte ’ bash överallt
    • @muru Ah, jag förstår. Jag läste inte svaret noggrant och trodde att författaren pratade om processersättning är inte tillgänglig i zsh 😅

    Svar

    I andan av att hålla sakerna enkla …

    Kan någon tillhandahålla en enkel praktisk jämförelse eller specifika snubblar måste jag veta, så att jag kan börja arbeta för att vara redo för det nya skalet när Catalina släpps?

    Om du tänker med det nya standardskalet, överväg:

    • Om du vill ge Zsh en virvel och känna några av skillnaderna utan att ändra skalinställningarna på din maskin, kan du prova Powerline10k i en Docker-behållare och se om det är din kopp te.
    • Om du inte behöver alla klockor och visslar och använd Bash för bara grundläggande manus är det ganska enkelt att ställa in skalet som andra här har förklarat. Och om du bestämmer dig för vill använda -funktioner i Bash 5 är det ganska trivial uppgradering för macOS .
    • Om du vill förbättra bärbarheten för dina skript så att de ”är mer benägna att fungera som förväntat i båda skalen, testa dem för POSIX-överensstämmelse och ta bort eventuella ”bashismer”. Jag har använt ShellCheck för detta och det fungerar ganska bra för mindre komplicerade skript.

    Medan ingen speciell sökväg är tydlig dessa tre tillvägagångssätt bör ge dig tillräckligt självförtroende för att fatta ett välgrundat beslut utan att överkonstruera problemet eller lösningsutrymmet.

    Lämna ett svar

    Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *