¿Puede zsh acceder a la stdout del último programa ejecutado?

15

A menudo uso findo locatepara averiguar sobre caminos.

(~) locate foobar.mmpz
/home/progo/lmms/projects/foobar.mmpz

El siguiente paso es abrir o manipular los archivos. En un caso feliz como el anterior, puedo hacer esto:

(~) ls `!!`
ls `locate foobar.mmpz`
/home/progo/lmms/projects/foobar.mmpz

Pero nadie está muy contento cuando hay muchas líneas de salida, algunas de las cuales pueden no ser rutas u otra cosa de ese tipo. Además, volver a ejecutar comandos potencialmente derrochadores tampoco es tan elegante.

¿Habría una manera de conectar zsh para almacenar el stdout en una matriz para su posterior manipulación? Después de todo, el trabajo del shell es redirigir las transmisiones al usuario. Estoy pensando que podría almacenar la primera N y la última N líneas en una variable para su uso inmediato posterior, como $?y otros.

Ok, esto es genial: /unix//a/59704/5674 . Ahora estoy preguntando sobre el conocimiento de zsh (y portando el código a zsh) para manipular este tipo de captura después de cada línea de ejecución.

unperson325680
fuente
No es realmente el trabajo del shell redirigir la transmisión al usuario. Las aplicaciones escriben su salida en el dispositivo terminal, el shell no está involucrado en eso.
Stéphane Chazelas
@StephaneChazelas, pero el trabajo del shell es, por ejemplo, redirigir las transmisiones a los archivos por solicitud del usuario. También hay recetas para zsh que colorean stderr en rojo para separarlo de stdout. Creo que eso indica el papel del shell en los asuntos de transmisión.
unperson325680
Sí, puede hacerlo usando screeno scripty precmd y preexec hooks.
Stéphane Chazelas

Respuestas:

7

No hay una función para capturar la salida de la pantalla en la mayoría de los emuladores de terminal. Me parece recordar que el autor de xterm (el emulador de terminal de "referencia") declaró que sería difícil de implementar. Incluso si eso fuera posible, el proyectil tendría que hacer un seguimiento de dónde había estado el último aviso.

Por lo tanto, no escapará de tener que ejecutar el comando nuevamente, a menos que use un mecanismo manual específico de terminal, como copiar y pegar con el mouse en xterm o con el teclado en pantalla.

Sería muy poco práctico para el shell capturar automáticamente la salida de comandos, porque no puede distinguir entre comandos que tienen complejas terminales e interacciones del usuario de comandos que simplemente generan caracteres imprimibles.

Puede volver a ejecutar el comando y capturar su salida. Hay varias formas de hacer cada uno. Para volver a ejecutar el comando, puede usar:

  • !! sustitución de historial: más conveniente para escribir;
  • fc -e -, que se puede usar en una función.

Para capturar la salida, puede usar la sustitución de comandos o una función como la siguiente:

K () {
  lines=("${(f@)$(cat)}")
}
!! |K

Esto establece la linesmatriz en la salida del comando que se canaliza en ella.

Gilles 'SO- deja de ser malvado'
fuente
Que así sea. Ahora me doy cuenta, a la luz de las buenas explicaciones, de que un enfoque completamente automático es demasiado difícil como es, la segunda mejor cosa es algo como el Ksuyo.
unperson325680
3

Aquí hay un primer corte de algo para poner la última línea de salida en una variable llamada $lastline.

precmd () {                                                                                                                                                                                                        
    exec 2>&- >&-
    lastline=$(tail -1 ~/.command.out) 
    sleep 0.1   # TODO: synchronize better
    exec > /dev/tty 2>&1
}

preexec() {
    exec > >(tee ~/.command.out&)
}

Este usos de zsh preexecgancho para que se ejecute execcon teepara almacenar una copia de la salida estándar del comando, a continuación, utiliza precmdpara leer la salida almacenada y restaurar la salida estándar a ser sólo el terminal para mostrar el símbolo.

Pero aún necesita algo de trabajo. Por ejemplo, dado que stdout ya no es una terminal, los programas como vimy lessno funcionan correctamente.

Hay información útil relacionada en estas preguntas:

Mikel
fuente
Sí, quizás un enfoque 100% automático no va a funcionar. Hay muchas execllamadas en el código, ¿se está ejecutando el comando varias veces o estoy atrapado en una semántica especial?
unperson325680
execsin un nombre de programa solo establece redirecciones.
Mikel
2

Puede hacerlo simplemente canalizando sus resultados a tee, lo que simplemente guarda el resultado en un archivo y lo muestra al mismo tiempo.

Entonces, por ejemplo, podría hacer esto, que muestra su salida como normal, pero también la guarda en el archivo /tmp/it

locate foobar.mmpz | tee /tmp/it

luego tome ese archivo y agrúpelo para seleccionar cosas, por ejemplo

cat /tmp/it | grep progo | grep lms

entonces para usarlo, podrías hacer esto:

vi $(!!)

Brad Parks
fuente
2

Se me ocurrió esta solución a medio hornear:

alias -g ___='"$(eval "$(fc -ln -1)" | tail -n 1)"'

Esto le permite escribir ___en cualquier punto de la línea de comando. El comando anterior se volverá a ejecutar y ___se reemplazará con la última línea de su salida. Ejemplo de uso:

$ touch foo bar baz
$ ls -1
bar
baz
foo
$ vim ___

El último comando se expandirá a vim foo.

¡Esto tiene algunos bordes afilados!

  • Si incluye ___un comando pero el comando anterior también incluyó un ___, el shell se colgará en un estado extraño por un tiempo. Puede salir de este estado inmediatamente con Ctrl- C.

  • Tampoco puede presionar Tabpara expandir ___, como puede hacerlo con !$otras construcciones.

  • Algunos comandos mostrarán diferentes resultados cuando se ejecutan "normalmente" y cuando están conectados a una tubería. (Compare el resultado de lsy ls | cat.) Si el comando activado por ___es uno de estos, puede terminar ejecutando un comando diferente al esperado.

  • Y, por supuesto, si desea hacer algo con una línea de salida que no sea la última, esto no lo ayudará.

Elegí el nombre ___porque es algo que nunca he querido incluir como palabra en una línea de comando, ni siquiera como argumento. Puede elegir un nombre diferente, pero tenga cuidado de no elegir algo que pueda expandirse sin darse cuenta.

Bdesham
fuente