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
Svar
Först några viktiga saker:
- 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.
- 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. - 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:
- ”gammal” kompletteringsmekanism med
compctl
som du kan glömma bort. - ” ny ”kompletteringsmekanism med
compadd
och många funktioner som börjar med understrykning och en kraftfull men komplex användarkonfigurationsmekanism . - En -emulering till stöd bash-kompletteringsfunktioner som du kan aktivera genom att köra
bashcompinit
. Emuleringen är inte 100% perfekt men den fungerar vanligtvis.
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 matcharfoo*
och symboliska länkar till vanliga filer, inte kataloger och andra specialfiler. -
foo*(*.)
: endast körbara vanliga filer som matcharfoo*
. -
foo*(-.)
: endast vanliga filer som matcharfoo*
, inte symboliska länkar och andra specialfiler. -
foo*(-@)
: endast hängande symboliska länkar som matcharfoo*
. -
foo*(om)
: filerna som matcharfoo*
, sorterade efter senaste modifieringsdatum, senast först. Observera att om du skickar detta tillls
, det kommer att göra sin egen sortering. Detta är särskilt användbart i … -
foo*(om[1,10])
: de tio senaste filerna som matcharfoo*
, de senaste först. -
foo*(Lm+1)
: filer som matcharfoo*
vars storlek är minst 1 MB. -
foo*(N)
: samma somfoo*
, men om detta inte matchar någon fil, skapa en tom lista oavsett för inställningen av alternativetnull_glob
(se ovan). -
*(D)
: matcha alla filer inklusive punktfiler ( utom.
och..
). -
foo/bar/*(:t)
(med en historikmodifierare ): filerna ifoo/bar
, men med endast basnamnet på filen. Till exempel om det finns enfoo/bar/qux.txt
, den utvidgas somqux.txt
. -
foo/bar/*(.:r)
: ta vanliga filer underfoo/bar
och ta bort tillägget. T.ex.foo/bar/qux.txt
utökas somfoo/bar/qux
. -
foo*.odt(e\""REPLY=$REPLY:r.pdf"\")
: ta lista över filer som matcharfoo*.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 medfoo
men intefoobar
. -
image<->.jpg(n)
: alla.jpg
filer vars basnamn ärimage
följt av ett nummer, t.ex.image3.jpg
ochimage22.jpg
men inteimage-backup.jpg
. Globkvalificatorn(n)
gör att filerna listas i numerisk ordning, dvsimage9.jpg
kommer innanimage10.jpg
(du kan göra detta till standard även utan-n
medsetopt 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 …
Svar
Ändra ditt skal nu och testa – du behöver inte vänta.
chsh -s /bin/zsh
- Alla skript som är beroende av
bash
syntax kommer fortfarande att hitta och ringa bash. - samma bash från Mojave levereras på Catalina och migrerade användare behåller sitt gamla skal.
- Många bloggar har bra skrivningar på flytta preferensfiler – här är en sådan – https://scriptingosx.com/2019/06/moving-to-zsh-part-2-configuration-files/
- Armin förvandlade sin bloggserie till en ordentlig bok med cirka 22 000 ord.
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 så 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:
- 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.
- Processersättning är inte tillgänglig.
Processersättning är <(...)
eller >(...)
syntax.
- Inbyggt
.
ochsource
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.
bash version 3.2.57(1)
: Vet du om Apple använderbash
för något ” viktigt ” på systemet?