360 votes

Quelles sont les différences pratiques entre Bash et Zsh ?

Avec la nouvelle que Catalina utilisera par défaut Zsh au lieu de Bash. Je trouve beaucoup de résultats qui me parlent de ce commutateur, et qui me disent qu'il peut causer des problèmes avec les scripts shell scripts, mais je ne suis pas assez familier avec Zsh pour savoir quels sont ces problèmes.

Mes scripts ne sont vraiment pas si compliqués, mais je n'ai jamais utilisé que Bash sur MacOS et Linux - aucune expérience avec Zsh. Quelqu'un peut-il me fournir une comparaison simple et pratique, ou des points d'achoppement spécifiques que je devrai connaître, afin que je puisse commencer à travailler pour être prêt pour le nouveau shell lorsque Catalina sortira ?

425voto

staffan Points 3299

Tout d'abord, quelques éléments importants :

  1. Bash ne va pas disparaître . Si vous utilisez déjà bash, rien ne changera pour vous. Tout ce qui change, c'est que zsh sera l'interpréteur de commandes de connexion par défaut pour les utilisateurs de nouveau et même dans ce cas, vous pouvez choisir bash à la place.
  2. Les scripts ne sont pas concernés. . Ce qui change, c'est le shell pour une utilisation interactive, c'est-à-dire le shell dans les terminaux (et aussi quelques autres choses qui utilisent le shell de connexion, comme les crontabs). Si vous avez un script dans un fichier avec des permissions d'exécution, commençant par un ligne shebang comme #!/bin/bash o #!/bin/sh o #!/usr/bin/env bash il continuera à fonctionner exactement comme avant.
  3. La syntaxe de Zsh n'est pas complètement compatible avec celle de bash, mais elle en est proche. Beaucoup de code continuera à fonctionner, par exemple les alias et fonctions typiques. Les principales différences résident dans les fonctionnalités de configuration interactive.

Maintenant, en supposant que vous envisagiez de passer à zsh, ce qui est une possibilité depuis des années, voici les principales différences que vous rencontrerez. Il ne s'agit pas d'une liste exhaustive !

Principales différences pour une utilisation interactive

Fichiers de configuration : bash lit (principalement) .bashrc dans les shells interactifs sans login (mais MacOS lance un shell de login dans les terminaux par défaut), .profile o .bash_profile dans les shells de connexion, et .inputrc . Zsh lit (principalement) .zshrc (dans tous les shells interactifs) et .zprofile (dans les coquilles de connexion). Cela signifie qu'aucune de vos personnalisations bash ne s'appliquera : vous devrez les porter. Vous ne pouvez pas vous contenter de copier les fichiers car de nombreuses choses devront être modifiées.

Liaisons de touches utilisent une syntaxe complètement différente. Bash utilise .inputrc et le bind intégré pour lier les clés à commandes de lecture en ligne . Zsh utilise le site bindkey intégré pour lier les clés à zle widgets . La plupart des commandes readline ont un équivalent zsh, mais ce n'est pas toujours une équivalence parfaite.

En parlant de liaisons de touches, si vous utilisez Vi(m) comme éditeur dans le terminal mais pas comme mode de ligne de commande dans le shell, vous remarquerez que zsh passe par défaut en mode d'édition vi (c'est-à-dire avec les modes commande et insertion) si EDITOR o VISUAL est réglé sur vi o vim . bindkey -e passe en mode emacs (c'est-à-dire où vous pouvez toujours taper directement).

Prompt : bash définit l'invite (principalement) de PS1 qui contient échappements de barres obliques inversées . Zsh définit l'invite principalement à partir de PS1 qui contient pourcentages d'évasion . Bien que les concepts soient similaires, les codes d'échappement sont complètement différents. La fonctionnalité de la fonction PROMPT_COMMAND est disponible dans zsh via l'option precmd y preexec fonctions de crochet . Zsh dispose de plus de mécanismes pratiques pour construire des invites fantaisistes, y compris l'option mécanisme de thème rapide .

L'essentiel historique de la ligne de commande mécanismes (navigation avec Up / Down , chercher avec Ctrl + R l'expansion de l'histoire avec !! et amis, dernier rappel des arguments avec Alt + . o $_ ) fonctionnent de la même manière, mais il existe de nombreuses différences dans les détails, trop nombreuses pour être énumérées ici. Vous pouvez copier votre .bash_history à .zsh_history si vous n'avez pas modifié une option du shell qui change le format du fichier.

Achèvement Les deux shells utilisent par défaut un mode de complétion basique qui complète principalement les noms de commandes et de fichiers, et passent à un mode plus sophistiqué en incluant la commande bash_completion sur bash ou en exécutant compinit dans zsh. Vous trouverez des commandes que bash gère mieux et d'autres que zsh gère mieux. Zsh est généralement plus précis, mais abandonne parfois là où bash fait quelque chose qui n'est pas correct mais qui est raisonnable. Pour spécifier les complétions possibles d'une commande, zsh dispose de trois mécanismes :

De nombreuses fonctions de bash shopt paramètres ont un correspondant setopt dans zsh.

Zsh ne traite pas # comme début de commentaire sur la ligne de commande par défaut, seulement dans les scripts (y compris les .zshrc et autres). Pour activer les commentaires interactifs, exécutez setopt interactive_comments .

Principales différences pour les scripts

(et pour les utilisateurs expérimentés en ligne de commande bien sûr)

Dans bash, $foo prend la valeur de foo le divise au niveau des espaces, et pour chaque partie séparée par des espaces, s'il contient des caractères génériques et correspond à un fichier existant, remplace le motif par la liste des correspondances. Pour obtenir simplement la valeur de foo vous avez besoin "$foo" . Il en va de même pour la substitution de commandes $(foo) . Dans zsh, $foo est la valeur de foo y $(foo) est la sortie de foo moins ses derniers traits de soulignement, à deux exceptions près. Si un mot devient vide à cause de l'expansion de variables vides non citées, il est supprimé (par ex. a=; b=; printf "%s\n" one "$a$b" three $a$b five imprime one une ligne vide, three , five ). Le résultat d'une substitution de commande non citée est divisé au niveau des espaces, mais les morceaux ne sont pas soumis à une correspondance avec les caractères génériques.

Bash tableaux sont indexés de 0 à (longueur-1). Les tableaux de Zsh sont indexés de 1 à la longueur. Vous pouvez faire de l'indexation 0 la valeur par défaut avec setopt ksh_arrays . Zsh requiert moins d'accolades (sauf si ksh_arrays est activé). Par exemple, supposons que a=(first second third "" last) .

Fonctionnalité

Syntaxe Bash

Syntaxe idiomatique de zsh

Expansion

Premier élément

${a[0]}

$a[1]

first

Deuxième élément

${a[1]}

$a[2]

second

Dernier élément

${a[${#a[@]}-1]}

$a[-1]

last

Longueur

${#a[@]}

$#a

5

Tous les éléments

"${a[@]}"

"${a[@]}" o "${(@)a}"

first second third (mot vide) last

Tous les éléments non vides

$a

first second third last

Bash a un supplément modèles de caractères génériques comme @(foo|bar) pour correspondre foo o bar qui ne sont activés qu'avec shopt -s extglob . Dans zsh, vous pouvez activer ces motifs avec setopt ksh_glob mais il existe aussi un modèle plus simple à utiliser. syntaxe native comme (foo|bar) dont certains exigent setopt extended_glob (mettez-le dans votre .zshrc et il est activé par défaut dans les fonctions d'achèvement). Zsh a **/ pour la traversée récursive des répertoires (comme le fait bash moderne mais pas bash 3.2 qui est livré avec MacOS).

Dans bash, par défaut, si un le motif de remplacement ne correspond pas n'importe quel fichier, il est laissé inchangé. Dans zsh, par défaut, vous obtiendrez une erreur, ce qui est généralement le paramètre le plus sûr. Si vous voulez passer un paramètre joker à une commande, utilisez des guillemets. Vous pouvez passer au comportement de bash avec setopt no_nomatch . Vous pouvez faire en sorte que les motifs joker non correspondants s'étendent à une liste vide à la place avec setopt null_glob .

Dans bash, l'option côté droit d'un pipeline s'exécute dans un sous-shell. Dans zsh, il s'exécute dans le shell parent, vous pouvez donc écrire des choses comme somecommand | read output .

Quelques fonctionnalités intéressantes de zsh

Voici quelques fonctionnalités de zsh que bash n'a pas (du moins pas sans une bonne dose d'huile de coude). Encore une fois, il s'agit juste d'une sélection de celles que je considère comme les plus utiles.

Qualifications mondiales permettent de faire correspondre des fichiers sur la base de métadonnées telles que leur horodatage, leur taille, etc. Ils permettent également de modifier la sortie. La syntaxe est plutôt cryptique, mais elle est extrêmement pratique. Voici quelques exemples :

  • foo*(.) : uniquement les fichiers réguliers correspondant foo* et des liens symboliques vers des fichiers ordinaires, et non vers des répertoires et autres fichiers spéciaux.
  • foo*(*.) : uniquement les fichiers réguliers exécutables correspondant foo* .
  • foo*(-.) : uniquement les fichiers réguliers correspondant foo* mais pas les liens symboliques et autres fichiers spéciaux.
  • foo*(-@) : seulement les liens symboliques pendants qui correspondent foo* .
  • foo*(om) : les fichiers correspondant foo* triés par date de dernière modification, les plus récents en premier. Notez que si vous passez ceci à ls il fera son propre tri. Ceci est particulièrement utile dans
  • foo*(om[1,10]) : les 10 fichiers les plus récents correspondant foo* le plus récent en premier.
  • foo*(Lm+1) : fichiers correspondants foo* dont la taille est d'au moins 1MB.
  • foo*(N) : même chose que foo* mais s'il ne correspond à aucun fichier, il produira une liste vide, quel que soit le paramétrage de la directive null_glob (voir ci-dessus).
  • *(D) : correspond à tous les fichiers, y compris les fichiers point (sauf . y .. ).
  • foo/bar/*(:t) (en utilisant un modificateur d'historique ) : les fichiers dans foo/bar mais avec seulement le nom de base du fichier. Par exemple, s'il existe un fichier foo/bar/qux.txt il est développé comme qux.txt .
  • foo/bar/*(.:r) : prendre des fichiers réguliers sous foo/bar et supprimer l'extension. Par exemple foo/bar/qux.txt est développée comme suit foo/bar/qux .
  • foo*.odt(e\''REPLY=$REPLY:r.pdf'\') : prendre la liste des fichiers correspondant foo*.odt et remplacer .odt par .pdf (que le fichier PDF existe ou non).

Voici quelques informations utiles spécifiques à zsh modèles de caractères génériques .

  • foo*.txt~foobar* : tous .txt les fichiers dont le nom commence par foo mais pas foobar .
  • image<->.jpg(n) : tous .jpg dont le nom de base est image suivi d'un numéro, par exemple image3.jpg y image22.jpg mais pas image-backup.jpg . Le qualificatif glob (n) fait en sorte que les fichiers soient listés dans l'ordre numérique, c.-à-d. image9.jpg vient avant image10.jpg (vous pouvez en faire la valeur par défaut même sans -n avec setopt numeric_glob_sort ).

A renommage en masse des fichiers zsh fournit un outil très pratique : l'utilitaire zmv fonction . Suggéré pour votre .zshrc :

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

Exemple :

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

Bash a quelques les façons d'appliquer des transformations lors de la prise de valeur d'une variable . Zsh a certains d'entre eux et bien d'autres encore .

Zsh dispose d'un certain nombre de petites fonctionnalités pratiques pour changer les répertoires . Allumez setopt auto_cd pour passer à un répertoire lorsque vous tapez son nom sans avoir à taper cd (bash a aussi cela de nos jours). Vous pouvez utiliser le forme à deux arguments pour cd pour passer à un répertoire dont le nom est proche du répertoire actuel. Par exemple, si vous êtes dans /some/where/foo-old/deeply/nested/inside et vous voulez aller à /some/where/foo-new/deeply/nested/inside il suffit de taper cd old new .

Pour attribuer une valeur à une variable, on écrit bien sûr VARIABLE=VALUE . Pour modifier la valeur d'une variable de manière interactive, il suffit d'exécuter vared VARIABLE .

Conseil final

Zsh est livré avec une interface de configuration qui prend en charge quelques-uns des paramètres les plus courants, y compris des recettes en boîte pour des choses comme la complétion insensible à la casse. Pour (re)lancer cette interface (la première ligne n'est pas nécessaire si vous utilisez un fichier de configuration qui a été édité par zsh-newuser-install ) :

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

En l'état, sans aucun fichier de configuration, de nombreuses fonctions utiles de zsh sont désactivées pour assurer la compatibilité avec les versions des années 1990. zsh-newuser-install suggère quelques fonctionnalités recommandées à activer.

Il existe de nombreux cadres de configuration de zsh sur le web (beaucoup d'entre eux sont sur Github ). Ils peuvent constituer un moyen pratique de démarrer avec des fonctionnalités puissantes. Le revers de la médaille est qu'ils vous enferment souvent dans la façon de faire de l'auteur, ce qui vous empêche parfois de faire les choses comme vous le souhaitez. Utilisez-les à vos risques et périls.

Le manuel de zsh contient beaucoup d'informations, mais il est souvent écrit d'une manière laconique et difficile à suivre, et comporte peu d'exemples. N'hésitez pas à chercher des explications et des exemples en ligne : si vous n'utilisez que la partie de zsh facile à comprendre dans le manuel, vous passerez à côté. Deux bonnes ressources sont la liste de diffusion zsh-users y Unix Stack Exchange . Un site vaste collection d'articles sur le passage à zsh sur le mac peuvent être trouvés sur scriptingosx.com et un script Ruby utile pour emporter votre historique de commandes avec vous peut être trouvé sur Github.

26voto

Oskar Points 1242

Changez votre coquille maintenant et testez-la - pas besoin d'attendre.

chsh -s /bin/zsh

De plus, j'estime que 95 % des utilisateurs de MacOS n'utilisent pas de ligne de commande et que parmi ceux qui le font, 95 % n'auront pas à modifier quoi que ce soit de significatif, voire rien du tout. (Je parierais qu'il s'agit plutôt de 10% des 1% qui savent que les shells existent et qui ont besoin de faire autre chose que de porter quelques lignes dans leurs fichiers .dot).

Votre invite va changer et si vous avez changé d'invite sur bash , la façon de le changer sur zsh n'est pas plus difficile et pas moins documenté que bash.

Les coquilles les plus récentes n'arriveraient jamais à décoller si elles cassaient des éléments majeurs ou entraînaient une période d'adaptation douloureuse. Si vous souhaitez un changement plus fondamental et si vous voulez vraiment une coquille à laquelle vous devez réfléchir et qui nécessite une formation et une intention d'adopter - essayez poisson .

0 votes

@Mavrik Jetez un coup d'œil au blog stupéfiant d'Armin Briegel et à son livre encore meilleur sur les différences. S

0 votes

Excellente réponse ; c'est pratique et accommodant - vraiment. :) Mais pour moi, il y a 2 choses : 1) Apple capitalisation boursière est terminée 1 billion de dollars US . 2) Je sens "arraché" quand une entreprise de 1T$ envoie un logiciel vieux de 15 ans avec un nouvel ordinateur de 4K$ qu'elle vend. J'aime mon Mac, je ne reviendrai pas en arrière, mais... ce #AntiqueSoftware n'est pas une bonne chose.

0 votes

@Seamus Vous pourriez être choqué par la taille de l'équipe qui travaille sur ce sujet. Apple a essayé de créer des équipes plus grandes et cela nous a donné de pires résultats. Ne regardez pas le pourcentage de revenus que Mac rapporte, je soupçonne que nous avons plus d'ingénieurs Mac que les chiffres ne le permettent, d'après l'histoire.

8voto

muru Points 725

Mes scripts shell scripts ne sont vraiment pas très compliqués.

Est-ce que vos scripts d'interpréteur de commandes ont des lignes shebang (commençant par #! /bin/bash ou similaire) ? Si ce n'est pas le cas, vous avez peut-être utilisé involontairement une fonctionnalité de bash, où il exécute scripts sans shebang en utilisant bash. D'autres shells, comme dash ou zsh, laissent le choix au système d'exploitation, qui utilise généralement /bin/sh à la place. /bin/sh sur MacOS est, et restera probablement, une copie de /bin/bash mais en exécutant bash avec le nom sh lui donne un comportement différent. Les détails sont dans le manuel de Bash, 6.11 Mode POSIX de Bash . Quelques points :

  1. Bash garantit que le POSIXLY_CORRECT est définie.

Cette variable d'environnement peut affecter le comportement d'un certain nombre d'autres outils, surtout si vous avez installé des outils GNU.

  1. La substitution de processus n'est pas disponible.

La substitution de processus est le <(...) o >(...) la syntaxe.

  1. El . y source Les builtins ne cherchent pas le répertoire courant pour l'argument nom de fichier s'il n'est pas trouvé en cherchant dans PATH.

Donc si votre script a fait . foo en s'attendant à ce qu'il génère un fichier nommé foo dans le répertoire courant, cela ne fonctionnera pas. Vous devez faire . ./foo à la place.

Comme vous pouvez le deviner à partir des chiffres, il y a beaucoup de différences mineures dans le comportement de bash en mode POSIX. Il vaut mieux utiliser un shebang si vous voulez utiliser bash pour vos scripts.

1 votes

En vérifiant, il semble que la grande majorité des scripts que j'ai écrits ou que j'utilise indiquent explicitement /bin/bash dans le shebang (très peu d'entre eux) ou indiquent /bin/sh (presque tous), donc cela au moins ne devrait pas être un problème. Merci.

0 votes

Je pense que la substitution de processus est disponible maintenant. Je l'ai également essayé avec succès sur Catalina. Voir : zsh.sourceforge.net/Intro/intro_7.html

0 votes

@Siu n'aidera toujours pas scripts qui utilisent /bin/sh ou /bin/bash dans le shebang. Zsh est seulement le shell interactif par défaut, il n'a pas remplacé bash partout.

4voto

vhs Points 253

Dans l'esprit de garder les choses simples...

Quelqu'un peut-il me fournir une comparaison simple et pratique, ou des points d'achoppement spécifiques que je devrai connaître, afin que je puisse commencer à travailler pour être prêt pour la nouvelle coquille lorsque Catalina sortira ?

Si vous envisagez d'utiliser le nouveau shell par défaut, considérez :

  • Si vous voulez donner un tour à Zsh et se sentir certaines des différences sans modifier les paramètres de l'enveloppe sur votre machine, vous pouvez essayer Powerline10k dans un conteneur Docker et voir si c'est votre tasse de thé.
  • Si vous n'avez pas besoin de toutes les cloches et si vous utilisez Bash pour des scripts de base, il est assez facile de définir votre shell comme d'autres ici l'ont expliqué. Et si vous décidez d'utiliser Fonctions de Bash 5 c'est un peu mise à niveau triviale pour MacOS .
  • Si vous voulez améliorer la portabilité de vos scripts afin qu'ils aient plus de chances de fonctionner comme prévu dans les deux shells, les tester pour la conformité POSIX et supprimer tout "bashisme". J'ai utilisé ShellCheck pour cela et cela fonctionne assez bien pour les scripts moins compliqués.

Bien qu'aucune voie particulière ne soit claire, ces trois approches devraient vous donner suffisamment de confiance pour prendre une décision en connaissance de cause, sans surdéterminer l'espace du problème ou de la solution.

LesApples.com

LesApples est une communauté de Apple où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres utilisateurs d'appareils Apple, poser vos propres questions ou résoudre celles des autres.

Powered by:

X