- Editar texto en la línea de comandos parece una función básica, pero cada programa maneja la entrada de forma distinta, así que la misma tecla puede comportarse de una manera completamente diferente
- La experiencia de entrada en la terminal suele dividirse en cuatro variantes: sin sistema de entrada, GNU readline, bibliotecas alternativas como
libedit, y sistemas de entrada propios
- En herramientas simples como
cat, nc y dash, las teclas de flecha pueden imprimirse como ^[[D, pero los controles que ofrece la terminal como Ctrl+W, Ctrl+U, Ctrl+C y Ctrl+Z siguen funcionando
- Las herramientas basadas en readline como
bash, psql, irb y python3 ofrecen historial, teclas de flecha, búsqueda con Ctrl+R y movimiento con Ctrl+A/Ctrl+E; con rlwrap se puede añadir una experiencia similar
- Si identificas qué sistema de entrada estás usando, es más fácil entender rápidamente por qué las flechas o
Ctrl+R se comportan distinto, y elegir si conviene revisar la documentación o usar una solución alternativa como rlwrap
La experiencia de entrada cambia según el programa
- La forma de escribir y editar texto en la terminal varía mucho según el programa
- Los tipos más representativos se pueden dividir así
cat, nc, git commit --interactive y otros no soportan las teclas de flecha, por lo que pueden aparecer cadenas como ^[[D^[[D^[[C^[[C^
irb, python3 en Linux y otros ofrecen funciones básicas de edición como historial y teclas de flecha mediante la biblioteca readline
- En algunos casos, como
/usr/bin/python3 en Mac, las teclas de flecha funcionan pero Ctrl+left o la búsqueda inversa con Ctrl+R no están soportadas
fish, ipython3, micro y vim usan su propio sistema de entrada
Modo 1: estado básico sin sistema de entrada
- Si un programa solo recibe texto con llamadas como
fgets() y no ofrece funciones de edición adicionales, las teclas de flecha pueden mostrarse como secuencias de escape en vez de actuar como comandos de edición
- Por ejemplo, en
dash, si presionas la flecha izquierda, la terminal puede mostrar algo así
$ ls l-^[[D^[[D^[[D
- Aun así, permanecen disponibles las funciones de control que la terminal ofrece por defecto
- ingreso de texto
- retroceso
Ctrl+W: borrar la palabra anterior
Ctrl+U: borrar toda la línea
Ctrl+C: interrumpir el proceso
Ctrl+Z: suspender el proceso
- Incluso en este entorno, usar
Ctrl+W permite borrar por palabras sin tener que presionar retroceso muchas veces
- Los códigos de control que soporta la terminal pueden revisarse con
stty -a
Modo 2: herramientas que usan readline
- readline es una biblioteca de GNU que hace más cómoda la entrada de texto y se usa en muchas herramientas interactivas
- Algunos atajos comunes de readline son estos
Ctrl+E o End: ir al final de la línea
Ctrl+A o Home: ir al inicio de la línea
Ctrl+left/right arrow: moverse una palabra hacia atrás o hacia adelante
- flecha hacia arriba: ir al comando anterior
Ctrl+R: buscar en el historial
- También se pueden usar
Ctrl+W y Ctrl+U del modo básico, pero en readline Ctrl+U no borra toda la línea, sino desde el cursor hasta el inicio de la línea
- La búsqueda de historial con
Ctrl+R en bash es una función que proporciona readline
psql, irb y python3 también son ejemplos de programas que usan readline
- La lista completa de comandos de edición de readline puede verse en el manual de readline
Añadir rlwrap a herramientas sin readline
- Incluso un programa que no soporta readline, como
nc, puede usarse como si lo soportara si lo ejecutas con rlwrap nc
- rlwrap puede mejorar mucho la usabilidad de herramientas que no tienen readline
- También permite configurar autocompletado personalizado, aunque eso no se ha probado aquí en la práctica
Por qué algunas herramientas no usan readline
- Hay varias razones por las que una herramienta podría no usar readline
- Programas muy simples como
cat o nc quizá no quieran añadir una dependencia relativamente grande
- readline no usa licencia LGPL sino GPL, por lo que puede no ser compatible con la licencia del programa
- Si solo una parte del programa es interactiva y la entrada se parece más a un único carácter como
y o n, dar soporte a readline puede no ser prioritario
git tiene funciones interactivas como git add -p, pero cuando hace falta una entrada larga y con sentido, normalmente lanza un editor de texto
idris2 no usa readline para mantener al mínimo las dependencias, y sugiere usar rlwrap si se necesita una mejor experiencia interactiva
Cómo verificar si la herramienta actual usa readline
- La forma más simple de comprobarlo es presionar
Ctrl+R
- Si aparece este prompt, es muy probable que esté usando readline
(reverse-i-search)`':
- Otras bibliotecas podrían usar la misma terminología, así que no es una prueba definitiva, pero no se conoce otro sistema que use la expresión
reverse-i-search para la búsqueda en el historial
Los atajos de readline y Emacs
- Los atajos
Ctrl+A para ir al inicio de la línea y Ctrl+E para ir al final pueden considerarse heredados de Emacs
- En Emacs,
Ctrl+A y Ctrl+E hacen lo mismo que en readline
- En cambio,
Ctrl+W y Ctrl+U no se comportan igual en Emacs y en la terminal
- La historia del proyecto readline puede leerse con más detalle en Historia del proyecto Readline
Modo 3: bibliotecas de entrada alternativas como libedit
/usr/bin/python3 en Mac está más cerca de un estado intermedio que solo soporta parte de las funciones de readline
- Por ejemplo, al presionar
Ctrl+left arrow, puede imprimirse ;5D en lugar de mover una palabra
$ python3
>>> importt subprocess;5D
- En la instalación predeterminada de Python en Mac OS, el módulo
readline de Python en realidad está basado en libedit
- Se presume que esto se debe a que readline usa licencia GPL
- Puedes comprobar si ese Python usa libedit con el siguiente comando
$ python3 -c "import readline; print(readline.__doc__)"
Importing this module enables command line editing using libedit readline.
- Python instalado en Linux o mediante Homebrew normalmente usa readline
- Python 3.13 planea eliminar la dependencia de readline y usar su propia biblioteca, así que en el futuro la afirmación “Python usa readline” podría dejar de ser siempre cierta
Modo 4: sistema de entrada propio
- Algunos programas usan su propio sistema de edición de texto y, a veces, ofrecen más funciones que readline
- Algunos ejemplos de sistemas de entrada propios son estos
- la mayoría de los editores de texto de terminal como
nano, micro, vim y emacs
- shells como
fish
- el editor de línea zle de
zsh
- REPL como
ipython
- herramientas como
atuin
- En estos sistemas puedes ver funciones como
- autocompletado más adaptado a la herramienta
- mejor gestión del historial, incluyendo resaltado de sintaxis
- más atajos de teclado
Sistemas propios inspirados en readline
- Atuin es una herramienta para buscar en el historial del shell; su implementación de entrada es propia, pero está inspirada en readline
- Atuin aprovecha que muchos usuarios ya están familiarizados con las combinaciones de teclas de readline, y hace que esas combinaciones funcionen aunque no use readline directamente
- prompt_toolkit, que usa IPython, también soporta varias opciones, incluidas combinaciones de teclas al estilo vi
- La configuración predeterminada de prompt_toolkit incluye soporte para combinaciones de teclas estilo readline
- Se parece a cómo muchos programas soportan las combinaciones básicas de vim, usando
j para bajar y k para subir
- Por ejemplo, Fastmail no tiene mucha relación con vim en la mayoría de sus atajos, pero sí soporta
j y k
- Los sistemas propios inspirados en readline pueden no ser totalmente compatibles con readline en detalles sutiles, pero para quienes usan solo algunos atajos básicos eso quizá no sea un gran problema
- Estos sistemas propios normalmente pueden ofrecer mejor autocompletado que usar solo readline
Shells con soporte de modo vi
bash, zsh y fish soportan modo vi para la entrada de texto
- En una encuesta no científica realizada en Mastodon, el 12% respondió que usa modo vi
- readline también tiene modo vi, y el soporte de modo vi en
bash funciona a través de eso
- Muchos otros programas que usan readline también pueden tener modo vi
Flujo para diagnosticar el estado de la entrada
- Saber qué sistema de entrada estás usando hace más fácil predecir el comportamiento de la línea de comandos
- En un prompt de entrada, se puede evaluar en este orden
- Si las teclas de flecha no funcionan, probablemente no haya sistema de entrada; aun así, puedes usar
Ctrl+W y Ctrl+U, y si hace falta agregar rlwrap
- Si al usar
Ctrl+R aparece reverse-i-search, probablemente se trate de readline, y podrás usar los atajos conocidos de readline y las funciones básicas de historial
- Si
Ctrl+R hace otra cosa, probablemente se trate de una biblioteca de entrada propia, que por lo general puede comportarse de forma parecida a readline; si hace falta, basta con revisar la documentación
- Aun así, quedan otras complejidades relacionadas con la entrada que aquí no se cubren
- problemas relacionados con
ssh y tmux
- la variable de entorno
TERM
- diferencias en soporte de copiar y pegar entre terminales como GNOME Terminal, iTerm y xterm
- Unicode
- y varios otros problemas de entrada
1 comentarios
Opiniones de Hacker News
Los artículos de Julia siempre son buenos. Como contenido que faltó: si se usa
sttydentro de un script de shell, se pueden cambiar varios comportamientos de la terminal, incluida la forma de procesar la entrada.Un experimento que hice hace tiempo con
shystty: https://gist.github.com/alganet/63f1dbc97b8fd35f7bb14ec30f79...En terminales compatibles con VT100 (mintty, xterm, Terminal.app, vscode, etc.) se pueden capturar e interpretar dentro del shell la mayoría de las combinaciones de teclas e incluso gestos del mouse (presionar/arrastrar/soltar).
La demo ejecutable es
bash -c "$(curl -L https://git.io/fjToH)"; basta con presionar teclas o mover el mouse. Para salir: Ctrl+W. También soporta zsh y ksh, pero no soporta dash y otros que no tienenread -rn1.Además, si se canaliza un programa interactivo a
cat -v, como envi | cat -v, se pueden ver los escapes VT100 que usa ese programa; aprendí mucho viendo cómo lo hace vi.stty sane.Abre 3 terminales y ejecuta 1)
man ascii, 2)nc -lvp 9001 | xxd -c1, 3)stty raw -echo; nc -nv 127.0.0.1 9001; luego, en la terminal 3, intenta reproducir en orden toda la columna “Hex” de la manpage.Se puede observar que algunos valores pueden generarse de varias maneras, y que algunas otras teclas envían un payload de varios bytes.
sttyy el modo raw: https://viewsourcecode.org/snaptoken/kilo/, https://viewsourcecode.org/snaptoken/kilo/02.enteringRawMode...Los parámetros y símbolos de
sttyque parecen magia vienen de secuencias de control definidas hace mucho tiempo. En estándares ambiguos de los años 70, como ECMA-48, hay mucha terminología oscura que hoy casi no se usa.Por ejemplo, Select Graphic Rendition(SGR) es más o menos como una versión setentera de las etiquetas HTML; para configurar el color de primer plano se usa algo como
ESC[38;2;R;G;BmTextESC[0m.ESC[es CSI,mes SGR,38es color de primer plano,2es el espacio de color RGB y0restablece el estilo.Como la estructura pone los parámetros antes del comando, se siente un poco al revés. Igual que los navegadores, las terminales han variado —y siguen variando— en qué implementan y cómo. Los colores están bastante bien soportados, pero otras funciones no tanto. Por ejemplo,
SGR<7>supuestamente es el modo “negative image”, pero el estándar no lo explica en detalle: algunas terminales lo muestran invertido y otras lo manejan de otra forma, lo que todavía causa problemas.Hace más de 20 años construí una máquina de estados sobre readline para poder usarlo no solo como una línea que se pliega, sino como un verdadero editor multilínea en el que se podía mover hacia arriba y hacia abajo.
Video: https://github.com/colmmacc/jot/raw/master/jot-demo.mp4
Repositorio CVS del cliente de IM para terminal Unix que incluía esta función: https://c-hey.redbrick.dcu.ie/src/c-hey_cvs_latest/
Al moverse arriba y abajo entre líneas, rastreaba y redibujaba el cursor; si cambiaba el tamaño de la terminal, observaba SIGWINCH y redibujaba. Me gustaría reescribirlo en Rust y publicarlo como una biblioteca pequeña, pero no he tenido tiempo. Siempre me sorprendió que nadie hiciera algo parecido.
Después de
autoload -Uz zed, se puede usarzedpara editar un archivo temporal y pasarlo agit commit -F, iniciando así un editor inline con todas las funciones de ZLE. Se guarda y confirma conC-x C-w, y se cancela conC-c.La ventaja de esta implementación básica es que
zedes un nuevo widget de ZLE, así que se puede controlar por completo el mapa de teclas. El modo emacs o el modo vi funcionan de inmediato, y también se puede personalizar más.Si se escribe un widget de ZLE propio, se puede invocar directamente dentro del editor de línea, sin necesidad de envolver el comando como arriba.
https://github.com/zsh-users/zsh/blob/master/Functions/Misc/...
git commit. El efecto de “máquina de escribir inline” también está bueno.Entre lo que faltó en el artículo están los caracteres anchos, el problema de que la misma pulsación de tecla se represente con distintas secuencias de escape ANSI por diferencias en los modos de teclado, diferencias de estado de la TTY como el eco local, y el hecho de que las llamadas al sistema que cambian el estado de la TTY varían sutilmente según el sistema operativo.
El soporte de emulación de terminal también es muy dispar. Hoy en día, en general, se imita a xterm, pero incluso dentro de eso nadie apunta a una compatibilidad del 100%.
También falta consenso sobre cómo comprobar las funciones que ofrece una terminal. Algunos envían secuencias de escape ANSI y esperan una respuesta; otros miran
$TERM; algunas terminales se exponen como xterm, pero ignoran las secuencias de funciones VT y configuran otras variables de entorno. Sinceramente, es más desastroso que las cadenas de agente de usuario.Todo esto bajo el supuesto de sistemas POSIX; si además metes Windows en la mezcla, se vuelve el doble de interesante.
Si en bash configuras
$EDITORcon tu editor favorito, puedes enviar la línea actual a$EDITORcon Ctrl-X Ctrl-E. Después de editar el comando, al guardar y salir se ejecuta.v.fchace lo mismo. Basta con escribirfc, sin secuencias con la tecla Ctrl.También puedes abrir el comando anterior con un editor específico en vez del
$EDITORpredeterminado. Por ejemplo, siEDITOR=viy quieres editar el comando anterior con ed, puedes usarfc -e ed.En modo vi también hay una forma rápida de crear un script de shell a partir del historial de comandos. Si el comando que quieres es el número 15 del historial, puedes hacer algo como
fc 15, luegow1.sh,%d,wq.EDITOR=nvimjunto con copilot.vim en Neovim como si fuera autocompletado de shell con IA. Funciona bastante bien.Lo vi aquí: https://twitter.com/arjie/status/1575201117595926530?s=46&t=...
Desde la perspectiva de un principiante en Linux, da curiosidad cómo, aunque un programa solo lea texto con algo como
fgets(), obtiene gratis funciones como Backspace, Ctrl+W y Ctrl+U.La documentación de
fgets()dice que lee hasta encontrar un salto de línea o EOF, así que surge la duda de si eso significa que, por defecto, se bloquea hasta que el usuario ingresa un salto de línea, y antes de eso la terminal permite editar el búfer de línea con Backspace o Ctrl+W/Ctrl+U.Pero no hay garantía de que el programa lea una línea completa; también podría leer carácter por carácter pasando
countcomo 2. Entonces, cuando ocurre un Backspace, no podría “devolver” caracteres que ya leyó; da curiosidad si hay alguna magia en el búfer interno defgets()o si en esos casos la edición de línea se rompe.https://en.cppreference.com/w/c/io/fgets
Que parezca haber buffering por líneas en realidad se debe a que lo gestiona el shell, y por eso los desarrolladores pueden agregar funciones de manipulación del búfer de texto, como autocompletar con Tab o anteponer sudo al inicio de la línea.
Antes de presionar Enter, los caracteres en pantalla solo están en la memoria de la terminal, y la llamada al sistema
readde la aplicación está bloqueada sobre el descriptor de archivo de la terminal. Hasta que se completa la línea, la terminal no escribe datos.En ese momento, la edición de la línea que se está ingresando normalmente también la proporciona la propia terminal. Que los caracteres se vean en pantalla también es una función de terminal llamada eco, y lo mismo ocurre con Backspace, Delete, Ctrl+C para SIGINT y Ctrl+D para EOF.
Los editores de línea y de texto desactivan estas funciones y dejan la terminal en modo raw. Todo lo que se ingresa se envía de inmediato a la aplicación para que esta lo procese directamente. Un ejemplo común es que un prompt de contraseña desactive solo el eco para ocultar los caracteres.
El tradicional fin de línea Unix
\ntambién es, en realidad, una función de terminal. Desde el punto de vista de la terminal,\nsolo mueve el cursor una línea hacia abajo, así que para volver al inicio de la línea también se necesita\r. El verdadero fin de línea es\r\n, y la terminal convierte invisiblemente\nen\r\n. Esto también se puede desactivar.En modo raw no funciona así.
helpde VMS a SunOS. El primer obstáculo fue cómo reaccionar a cada pulsación de tecla sin esperar Enter/Return.Mi jefe, a propósito, casi no me ayudó, y en 1986, sin web y casi sin libros de O’Reilly, me hizo descubrirlo prácticamente solo. Hasta hoy le estaré eternamente agradecido por esa decisión.
El shell dash también soporta modo de edición si se compila con libedit. Las variantes basadas en Debian probablemente no lo hacen por cuestiones de espacio, pero creo que conviene soportarlo para que quienes empezaron con bash tengan que aprender menos después.
El estándar POSIX también define
set -o vicomo una extensión opcional. Si un shell que afirma ser compatible con POSIX implementa este modo de edición, debe soportar obligatoriamenteset -o vi.“
[set -o] vi: permite la edición de la línea de comandos del shell usando un editor vi integrado. Al activar el modo vi, deben desactivarse otros modos de edición de línea de comandos que se ofrezcan como extensiones de la implementación.”https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V...
Siempre he pensado que la terminal es el único factor que mantiene a Linux atado para siempre a una cuota de mercado inferior al 5%
El problema no es solo la entrada de texto: toda la experiencia es compleja. El 95% de la población solo quiere volar un dron, y les ponen enfrente una cabina de Concorde sin etiquetas
https://qph.cf2.quoracdn.net/main-qimg-2566f4c91b894e4169d77..., https://media.thedroningcompany.com/images/tincy/WQZpC56vqMp...
Agradezco, más bien, que detrás de Ubuntu haya una cabina de Concorde y que pueda acceder a ella desde mi laptop Ubuntu de trabajo. Creo que no me gustaría no tener ese nivel de control
En cambio, la laptop de mi madre también usa Ubuntu y la de mi novia usa Manjaro, y ambas la usan sin problemas solo para “volar el dron”, sin pisar nunca la cabina del Concorde. Claro, yo uso Arch
La terminal es una herramienta para desarrolladores y administradores de sistemas. Solo parece así porque la mayoría de los usuarios de GNU/Linux de escritorio son desarrolladores y administradores de sistemas, y ellos son unos pocos puntos porcentuales de la población
Donde sí hay que saberse muchos atajos raros es más bien en la terminal de macOS, donde todo está hecho para ser lo menos intuitivo posible
Quizá sea una opinión impopular, pero en iTerm2 cambié al preset Natural text editing para alinearlo con los atajos de edición tradicionales de macOS
Como dentro de la app de terminal normalmente los remapea de nuevo a secuencias de control, tiene la ventaja de que obtienes la funcionalidad casi en todas partes sin tener que cambiarla en varios lugares ni preocuparte por el soporte de readline
No es perfecto, y a veces se remapeaban secuencias que tenía que escribir manualmente, pero como coincide con mi memoria muscular, terminé usando mucho más la edición de comandos
https://pliszko.com/blog/post/2021-10-31-natural-text-editin...
No quiero que la edición de texto en la terminal se comporte de manera especial
Las tres combinaciones de teclas de readline básicas que mejorarían la vida de la gente si las conociera son Ctrl+W, Ctrl+O y Ctrl+R
Ctrl+W borra la última palabra, como menciona Julia en el artículo
Ctrl+O ejecuta la línea recuperada del historial y luego vuelve a cargar la siguiente línea del historial. Si te mueves al primer comando del historial y presionas Ctrl+O cinco veces, puedes ejecutar cinco comandos en orden
Ctrl+R busca hacia atrás en el historial mientras escribes la cadena de búsqueda. Si presionas Ctrl+R de nuevo, vas al resultado anterior; Ctrl+S avanza en el tiempo. Se ejecuta con Enter o Ctrl+O
Para usar esa combinación de teclas para la búsqueda hacia adelante, tienes que saber por qué no funciona y estar dispuesto a desactivar XOFF
En Windows Terminal, Ctrl+C siempre funciona como corresponde. Si estás seleccionando texto, lo copia; si no, mata el proceso en ejecución.
Las apps de terminal en Linux no copian con Ctrl+C y exigen Ctrl+Shift+C. Para pegar, si presionas Ctrl+V se insertan caracteres raros, y como en todas las demás apps es Ctrl+V, la memoria muscular te juega en contra todo el tiempo.
Me pregunto si existe alguna app de terminal para Linux que se comporte como Windows Terminal y no te dé un golpe en los nudillos por “uso indebido del teletipo”.
Este método usa un búfer separado de Xorg, así que aunque copies algo con Ctrl+C y luego selecciones otro texto, Ctrl+V pega lo primero y el clic central pega lo segundo.
En Windows o en smartphones a veces es molesto porque dejas algo seleccionado y se te olvida presionar algo como Ctrl+C.
No creo que haya una terminal de Linux que haga lo mismo que Windows Terminal. Esto se debe a que un programa que se ejecuta dentro de la terminal podría querer manejar Ctrl+C directamente. Un editor de texto es un ejemplo, y la terminal ni siquiera sabe qué programa se está ejecutando en ese momento. El control de trabajos es responsabilidad del shell.
map ctrl+c copy_or_interrupt.https://sw.kovidgoyal.net/kitty/actions/#action-copy_or_inte...