Con la noticia de que Catalina usará de forma predeterminada Zsh en lugar de Bash , estoy encontrando muchos resultados que me informan sobre el cambio y que puede causar problemas con los scripts de shell, pero no estoy lo suficientemente familiarizado con Zsh para saber cuáles son esos problemas. puede ser.
Mis scripts de shell realmente no son tan complicados, pero solo he usado Bash en macOS y Linux: cero experiencia con Zsh. ¿Alguien puede proporcionar una comparación práctica simple o obstáculos específicos que necesitaré saber para poder comenzar a trabajar para estar listo para el nuevo caparazón cuando se lance Catalina?
Primero, algunas cosas importantes:
#!/bin/bash
o #!/bin/sh
o #!/usr/bin/env bash
, seguirá funcionando exactamente como antes.Ahora, suponiendo que esté considerando cambiar a zsh, lo cual ha sido una posibilidad durante años, estas son las principales diferencias que encontrará. ¡Esta no es una lista exhaustiva!
Archivos de configuración : bash lee (principalmente) .bashrc
en shells interactivos sin inicio de sesión (pero macOS inicia un shell de inicio de sesión en terminales de forma predeterminada), .profile
o .bash_profile
en shells de inicio de sesión, y .inputrc
. Zsh lee (principalmente) .zshrc
(en todos los shells interactivos) y .zprofile
(en los shells de inicio de sesión). Esto significa que no se aplicará ninguna de sus personalizaciones de bash: deberá transferirlas. No puede simplemente copiar los archivos porque muchas cosas necesitarán ajustes.
Las combinaciones de teclas usan una sintaxis completamente diferente. Bash usa .inputrc
y el bind
incorporado para vincular claves a comandos de línea de lectura . Zsh usa el bindkey
incorporado para vincular claves a widgets zle . La mayoría de los comandos readline tienen un equivalente zsh, pero no siempre es una equivalencia perfecta.
Hablando de combinaciones de teclas, si usa Vi(m) como su editor en la terminal pero no como su modo de línea de comando en el shell, notará que zsh se establece de manera predeterminada en el modo de edición vi (es decir, con modos de comando e inserción) si EDITOR
o VISUAL
es establecido en vi
o vim
. bindkey -e
cambia al modo emacs (es decir, donde siempre puede escribir directamente).
Aviso : bash establece el aviso (principalmente) desde PS1
el cual contiene escapes de barra invertida . Zsh establece el indicador principalmente desde PS1
el que contiene escapes porcentuales . Aunque los conceptos son similares, los códigos de escape son completamente diferentes. La funcionalidad de bash PROMPT_COMMAND
está disponible en zsh a través de las funciones precmd
y gancho . Zsh tiene más mecanismos convenientes para crear avisos sofisticados, incluido un mecanismo de tema de aviso .preexec
Los mecanismos básicos del historial de la línea de comandosUp (navegación con / Down, búsqueda con Ctrl+ R, expansión del historial con !!
y amigos, recuperación del último argumento con Alt+ .o $_
) funcionan de la misma manera, pero hay muchas diferencias en los detalles, demasiadas para enumerarlas aquí. . Puede copiar su .bash_history
a .zsh_history
si no ha cambiado una opción de shell que cambia el formato del archivo.
Finalización : ambos shells tienen por defecto un modo de finalización básico que en su mayoría completa comandos y nombres de archivos, y cambia a un modo sofisticado al incluir bash_completion
en bash o ejecutar compinit
en zsh. Encontrará algunos comandos que bash maneja mejor y otros que zsh maneja mejor. Zsh suele ser más preciso, pero a veces se da por vencido cuando bash hace algo que no es correcto pero que es sensato. Para especificar posibles finalizaciones para un comando, zsh tiene tres mecanismos:
compctl
el que puedes olvidarte.compadd
muchas funciones que comienzan con un guión bajo y un mecanismo de configuración de usuario potente pero complejo .bashcompinit
. La emulación no es 100% perfecta pero suele funcionar.Muchas de shopt
las configuraciones de bash tienen su correspondiente setopt
en zsh.
Zsh no se trata #
como un comienzo de comentario en la línea de comandos de forma predeterminada, solo en scripts (incluidos .zshrc
y similares). Para habilitar los comentarios interactivos, ejecute setopt interactive_comments
.
(y para usuarios avanzados en la línea de comandos, por supuesto)
En bash, $foo
toma el valor de foo
, lo divide en espacios en blanco y, para cada parte separada por espacios en blanco, si contiene caracteres comodín y coincide con un archivo existente, reemplaza el patrón por la lista de coincidencias. Para obtener el valor de foo
, necesita "$foo"
. Lo mismo se aplica a la sustitución de comandos $(foo)
. En zsh, $foo
es el valor de foo
y $(foo)
es la salida de foo
menos sus saltos de línea finales, con dos excepciones. Si una palabra queda vacía debido a la expansión de variables vacías sin comillas, se elimina (por ejemplo, a=; b=; printf "%s\n" one "$a$b" three $a$b five
imprime one
, una línea vacía, three
, five
). El resultado de una sustitución de comando sin comillas se divide en espacios en blanco, pero las piezas no se someten a la coincidencia de comodines.
Las matrices Bash están indexadas de 0 a (longitud-1). Las matrices Zsh están indexadas de 1 a longitud. Puede hacer que la indexación 0 sea la predeterminada con setopt ksh_arrays
. Zsh requiere menos llaves (a menos que ksh_arrays
esté habilitado). Por ejemplo, supongamos a=(first second third "" last)
.
Funcionalidad | sintaxis bash | Sintaxis idiomática de zsh | Expansión |
---|---|---|---|
primer elemento | ${a[0]} |
$a[1] |
first |
segundo elemento | ${a[1]} |
$a[2] |
second |
último elemento | ${a[${#a[@]}-1]} |
$a[-1] |
last |
Longitud | ${#a[@]} |
$#a |
5 |
todos los elementos | "${a[@]}" |
"${a[@]}" o"${(@)a}" |
first second third (palabra vacía)last |
Todos los elementos no vacíos | $a |
first second third last |
Bash tiene patrones de comodines adicionales , como @(foo|bar)
to match foo
o bar
, que solo están habilitados con shopt -s extglob
. En zsh, puede habilitar estos patrones con setopt ksh_glob
, pero también hay una sintaxis nativa más simple de escribir, como (foo|bar)
, algunas de las cuales requieren setopt extended_glob
(coloque eso en su .zshrc
, y está activado de manera predeterminada en las funciones de finalización). Zsh tiene **/
para el recorrido recursivo de directorios (al igual que el bash moderno pero no el bash 3.2 que se envía con macOS).
En bash, por defecto, si un patrón comodín no coincide con ningún archivo, se deja sin cambios. En zsh, de forma predeterminada, obtendrá un error, que suele ser la configuración más segura. Si desea pasar un parámetro comodín a un comando, use comillas. Puede cambiar al comportamiento bash con setopt no_nomatch
. Puede hacer que los patrones comodín que no coincidan se expandan a una lista vacía en su lugar con setopt null_glob
.
En bash, el lado derecho de una canalización se ejecuta en una subcapa. En zsh, se ejecuta en el shell principal, por lo que puede escribir cosas como somecommand | read output
.
Aquí hay algunas características agradables de zsh que bash no tiene (al menos no sin un poco de esfuerzo). Una vez más, esta es solo una selección de las que considero más útiles.
Los calificadores globales permiten hacer coincidir archivos en función de los metadatos, como su marca de tiempo, su tamaño, etc. También permiten ajustar la salida. La sintaxis es bastante críptica, pero es extremadamente conveniente. Aquí están algunos ejemplos:
foo*(.)
: solo coincidencia de archivos regulares foo*
y enlaces simbólicos a archivos regulares, no directorios y otros archivos especiales.foo*(*.)
: solo archivos ejecutables regulares coincidentes foo*
.foo*(-.)
: solo coincidencia de archivos regulares foo*
, no enlaces simbólicos y otros archivos especiales.foo*(-@)
: solo coincidencia de enlaces simbólicos colgantes foo*
.foo*(om)
: los archivos coincidentes foo*
, ordenados por última fecha de modificación, los más recientes primero. Tenga en cuenta que si pasa esto a ls
, hará su propia clasificación. Esto es especialmente útil en…foo*(om[1,10])
: los 10 archivos más recientes coincidentes foo*
, el más reciente primero.foo*(Lm+1)
: archivos coincidentes foo*
cuyo tamaño es de al menos 1 MB.foo*(N)
: igual que foo*
, pero si esto no coincide con ningún archivo, genera una lista vacía independientemente de la configuración de la null_glob
opción (ver arriba).*(D)
: coincide con todos los archivos, incluidos los archivos de puntos (excepto .
y ..
).foo/bar/*(:t)
(usando un modificador de historial ): los archivos en foo/bar
, pero solo con el nombre base del archivo. Por ejemplo, si hay un foo/bar/qux.txt
, se expande como qux.txt
.foo/bar/*(.:r)
: tome los archivos normales foo/bar
y elimine la extensión. Por ejemplo, foo/bar/qux.txt
se expande como foo/bar/qux
.foo*.odt(e\''REPLY=$REPLY:r.pdf'\')
: toma la lista de archivos que coinciden foo*.odt
y reemplaza .odt
por .pdf
(independientemente de si el archivo PDF existe).Aquí hay algunos patrones de comodines específicos de zsh útiles .
foo*.txt~foobar*
: todos .txt
los archivos cuyo nombre comienza con foo
pero no foobar
.image<->.jpg(n)
: todos .jpg
los archivos cuyo nombre base va image
seguido de un número, por ejemplo, image3.jpg
y image22.jpg
pero no image-backup.jpg
. El calificador glob (n)
hace que los archivos se enumeren en orden numérico, es decir, image9.jpg
viene antes image10.jpg
(puede hacer que esto sea el valor predeterminado incluso sin -n
) setopt numeric_glob_sort
.Para renombrar archivos en masa , zsh proporciona una herramienta muy conveniente: la zmv
función . Sugerido para su .zshrc
:
autoload zmv
alias zcp='zmv -C' zln='zmv -L'
Ejemplo:
zmv '(*).jpeg' '$1.jpg'
zmv '(*)-backup.(*)' 'backups/$1.$2'
Bash tiene algunas formas de aplicar transformaciones al tomar el valor de una variable . Zsh tiene algo de lo mismo y muchos más .
Zsh tiene una serie de pequeñas características convenientes para cambiar de directorio . Actívelo setopt auto_cd
para cambiar a un directorio cuando escriba su nombre sin tener que escribir cd
(bash también tiene esto hoy en día). Puede usar el formulario de dos argumentos paracd
cambiar a un directorio cuyo nombre esté cerca del directorio actual. Por ejemplo, si estás dentro /some/where/foo-old/deeply/nested/inside
y quieres ir a /some/where/foo-new/deeply/nested/inside
, simplemente escribe cd old new
.
Para asignar un valor a una variable, por supuesto escribe VARIABLE=VALUE
. Para editar el valor de una variable de forma interactiva, simplemente ejecute vared VARIABLE
.
Zsh viene con una interfaz de configuración que admite algunas de las configuraciones más comunes, incluidas recetas enlatadas para cosas como la finalización que no distingue entre mayúsculas y minúsculas. Para (volver a) ejecutar esta interfaz (la primera línea no es necesaria si está utilizando un archivo de configuración que fue editado por zsh-newuser-install
):
autoload -U zsh-newuser-install
zsh-newuser-install
Listo para usar, sin ningún archivo de configuración, muchas de las características útiles de zsh están deshabilitadas para compatibilidad con versiones anteriores de 1990. zsh-newuser-install
sugiere algunas funciones recomendadas para activar.
Hay muchos marcos de configuración de zsh en la web (muchos de ellos están en Github ). Pueden ser una forma conveniente de comenzar con algunas características poderosas. La otra cara de la moneda es que a menudo te obligan a hacer las cosas como lo hace el autor, por lo que a veces te impiden hacer las cosas como quieres. Usélos bajo su propio riesgo.
El manual de zsh tiene mucha información, pero a menudo está escrito de una manera concisa y difícil de seguir, y tiene pocos ejemplos. No dude en buscar explicaciones y ejemplos en línea: si solo usa la parte de zsh que es fácil de entender en el manual, se lo perderá. Dos buenos recursos son la lista de correo zsh-users y Unix Stack Exchange . Se puede encontrar una amplia colección de artículos sobre cómo cambiar a zsh en Mac en scriptingosx.com y se puede encontrar un script de Ruby útil para llevar su historial de comandos con usted en Github.
#!
y no se ven afectados por el shell de inicio de sesión predeterminado)bash
, pero obtendré una versión actual MacPorts
para ejecutar junto con la versión 3.2 bash
que proporciona Apple. Para mí, siento que zsh
tiene muchas características excelentes que probablemente nunca usaré ... ¡como la costosa cámara que compré recientemente!one[1]
? No hay one
una matriz definida.a
. En bash, $a
siempre se expande al primer elemento de la matriz, incluso cuando va seguido de algo que parece un subíndice de matriz.a=(one two); echo $a[1]
se imprimirá one[1]
(Eso es lo que escribió allí en la respuesta), en cambio, en zsh se imprimirá solo one
. Realmente me tomó mucho releer para entender ese significado. El cambio de shell cada pocas palabras hace que la declaración sea difícil de leer en mi humilde opinión. ¿Podría expresarse de una manera más simple y menos enrevesada?Cambie su caparazón ahora y pruebe, no es necesario esperar.
chsh -s /bin/zsh
bash
la sintaxis seguirán buscando y llamando a bash.Además, estimaría que el 95 % de los usuarios de macOS no utilizan una línea de comandos y, de los que sí lo hacen, otro 95 % no tendrá que cambiar nada significativo o en absoluto. (Apuesto a que es más como el 10% del 1% que sabe que los shells existen necesitan hacer algo más que portar un par de líneas en sus archivos .dot)
Su indicador cambiará y si cambió su indicador en bash
, la forma de cambiarlo zsh
no es más difícil ni menos documentada que bash.
Los proyectiles más nuevos nunca podrían despegar si rompieran elementos importantes o causaran un período de adaptación doloroso. Si quiere un cambio más fundamental y realmente quiere un caparazón, necesita pensar y requiere entrenamiento e intención de adoptar: pruebe con pescado .
fish
. Lo uso desde hace dos años, pero la incompatibilidad de algunos one-lines de copiar/pegar es agotador. Por otro lado es un shell muy práctico (las sugerencias automáticas sin <kbd>Tab</kbd> son excelentes)ksh
para dejar realmente ese redil. Con mucho gusto dejaré bash atrás y aceptaré zsh por completo ahora, personalmente.fish
no es nada. Si realmente quieres una experiencia de shell única, usa tclsh
.Mis scripts de shell realmente no son tan complicados
¿Sus scripts de shell tienen líneas shebang (comienza con #! /bin/bash
o similar)? De lo contrario, es posible que sin querer haya estado usando una función de bash, donde ejecuta scripts sin un shebang usando bash. Otros shells, como dash o zsh, lo dejan en manos del sistema operativo, que normalmente lo usaría /bin/sh
en su lugar. /bin/sh
en macOS es, y probablemente seguirá siendo, una copia de /bin/bash
, pero ejecutar bash con el nombre sh
hace que tenga un comportamiento diferente. Los detalles son el manual de Bash, 6.11 Modo Bash POSIX . Algunos puntos:
- Bash se asegura de que la
POSIXLY_CORRECT
variable esté configurada.
Esta variable de entorno puede afectar el comportamiento de otras herramientas, especialmente si tiene herramientas GNU instaladas.
- La sustitución de procesos no está disponible.
La sustitución de procesos es la sintaxis <(...)
o >(...)
.
- Los componentes
.
ysource
no buscan en el directorio actual el argumento de nombre de archivo si no se encuentra buscando en PATH.
Entonces, si su secuencia de comandos . foo
esperaba que generara un archivo con el nombre foo
en el directorio actual, eso no funcionará. Deberías hacer . ./foo
, en su lugar.
Como puede adivinar por los números, hay muchas diferencias menores en el comportamiento de bash en el modo POSIX. Mejor use un shebang si quiere usar bash para sus scripts.
/bin/sh
o /bin/bash
en el shebang. Zsh es solo el shell interactivo predeterminado, no reemplazó a bash en todas parteszsh
😅En el espíritu de mantener las cosas simples...
¿Alguien puede proporcionar una comparación práctica simple o obstáculos específicos que necesitaré saber para poder comenzar a trabajar para estar listo para el nuevo caparazón cuando se lance Catalina?
Si está pensando en usar el nuevo shell predeterminado, considere:
Si bien no hay un camino en particular claro, estos tres enfoques deberían brindarle la confianza suficiente para tomar una decisión informada sin sobrediseñar el problema o el espacio de solución.
fd0
Carlos Duffy
Dr. Nixon
Seamus
bash version 3.2.57(1)
: ¿Sabes si Apple lo usabash
para algo "importante" en el sistema?Dave Everitt