Reutilizando la salida del último comando en Bash

Respuestas:

185

Puede usar $(!!) para volver a calcular (no reutilizar) la salida del último comando.

El !!solo ejecuta el último comando.

$ echo pierre
pierre
$ echo my name is $(!!)
echo my name is $(echo pierre)
my name is pierre
abadejo
fuente
9
Puede evitar escribir $(your_cmd)a través de las teclas de retroceso: de `your_cmd`acuerdo con el manual de Gnu Bash, son funcionalmente idénticas. Por supuesto, esto también está sujeto a la advertencia planteada por @memecs
En la práctica, la salida de los comandos es determinista, por lo que no me preocuparía que vuelva a calcular. Ciertamente es útil cuando quieres hacer algo como git diff $(!!)donde el comando anterior era una findinvocación.
Sridhar Sarnobat
Aparentemente hay buenas razones para usar en $()lugar de backticks. Pero probablemente nada para perder el sueño. github.com/koalaman/shellcheck/wiki/SC2006
Michael Crenshaw
1
@ user2065875 aunque los backticks todavía funcionan en Bash, están en desuso a favor $().
kurczynski
87

La respuesta es no. Bash no asigna ninguna salida a ningún parámetro o bloque en su memoria. Además, solo se le permite acceder a Bash por sus operaciones de interfaz permitidas. No se puede acceder a los datos privados de Bash a menos que los piratee.

konsolebox
fuente
2
¿Es posible tener alguna función personalizada de bash 'middleware' que se ejecute antes de cada comando, donde todo lo que hace es guardar la salida resultante en algo que actúa como un portapapeles interno (por ejemplo, BASH_CLIPBOARD)?
Pat Needham
@PatNeedham No estoy seguro. Verifique los módulos cargables. Vea si puede escribir uno.
konsolebox el
11

Una forma de hacerlo es mediante el uso de trap DEBUG:

f() { bash -c "$BASH_COMMAND" >& /tmp/out.log; }
trap 'f' DEBUG

Ahora el comando stdout y stderr ejecutados más recientemente estarán disponibles en /tmp/out.log

El único inconveniente es que ejecutará un comando dos veces: una para redirigir la salida y el error a /tmp/out.logotra y normalmente. Probablemente hay alguna forma de prevenir este comportamiento también.

anubhava
fuente
10
Entonces rm -rf * && cd .., cuando escribo , no solo se borrará el directorio actual, sino también el directorio principal. este enfoque me parece muy peligroso
phil294
1
Ahora, ¿cómo deshago esta acción?
Kartik Chauhan
55
@KartikChauhan: Just dotrap '' DEBUG
anubhava
7

Si está en Mac y no le importa almacenar su salida en el portapapeles en lugar de escribir en una variable, puede usar pbcopy y pbpaste como solución alternativa.

Por ejemplo, en lugar de hacer esto para buscar un archivo y diferenciar su contenido con otro archivo:

$ find app -name 'one.php' 
/var/bar/app/one.php

$ diff /var/bar/app/one.php /var/bar/two.php

Podrías hacer esto:

$ find app -name 'one.php' | pbcopy
$ diff $(pbpaste) /var/bar/two.php

La cadena /var/bar/app/one.phpestá en el portapapeles cuando ejecuta el primer comando.

Por cierto, pb en pbcopyy pbpastesignifica cartón, sinónimo de portapapeles.

Raghu Dodda
fuente
1
en linux $ find app -name 'one.php' | xclip $ diff $(xclip -o) /var/bar/two.php
si-mikey
interesante, nunca pensé en usarlo pbpastejunto con la sustitución de comandos.
Sridhar Sarnobat
¿Qué sucede si hago lo primero y luego me doy cuenta de que era ineficiente? ¿Cómo puedo ahorrar tiempo de cálculo sin volver a ejecutar con la segunda opción?
NelsonGon
4

Inspirado por la respuesta de anubhava, que creo que en realidad no es aceptable, ya que ejecuta cada comando dos veces.

save_output() { 
   exec 1>&3 
   { [ -f /tmp/current ] && mv /tmp/current /tmp/last; }
   exec > >(tee /tmp/current)
}

exec 3>&1
trap save_output DEBUG

De esta manera, la salida del último comando está en / tmp / last y el comando no se llama dos veces.

Olivecoder
fuente
1
Una desventaja que he encontrado en esto es que ninguno de sus comandos cree que ya se está ejecutando en un tty, lo que significa que necesita atención especial para que los códigos de color funcionen para utilidades como grepo git diff+1 de todos modos
Marty Neal
2

Como dijo konsolebox, tendrías que hackear el propio bash. Aquí hay un buen ejemplo de cómo se puede lograr esto. El repositorio stderred (en realidad destinado a colorear stdout) da instrucciones sobre cómo construirlo.

Lo intenté: definiendo un nuevo descriptor de archivo dentro .bashrccomo

exec 41>/tmp/my_console_log

(el número es arbitrario) y modifique en stderred.cconsecuencia para que el contenido también se escriba en fd 41. Funcionó un poco , pero contiene muchos bytes NUL, formatos extraños y es básicamente información binaria, no legible. Tal vez alguien con buenos conocimientos de C podría probar eso.

Si es así, todo lo necesario para obtener la última línea impresa es tail -n 1 [logfile].

phil294
fuente
1

Sí, ¿por qué escribir líneas adicionales cada vez que se acuerde? Puede redirigir devuelto a la entrada, pero la salida a la entrada (1> & 0) es no. Además, no querrá escribir una función una y otra vez en cada archivo para la misma.

Si no está exportando los archivos a ninguna parte y tiene la intención de usarlos solo localmente, puede hacer que Terminal configure la declaración de la función. Tiene que agregar la función en el ~/.bashrcarchivo o en el ~/.profilearchivo. En el segundo caso, debe habilitar Run command as login shelldesde Edit>Preferences>yourProfile>Command.

Haga una función simple, diga:

get_prev()
{
    # option 1: create an executable with the command(s) and run it
    echo $* > /tmp/exe
    bash /tmp/exe > /tmp/out

    # option 2: if your command is single command (no-pipe, no semi-colons), still it may not run correct in some exceptions
    #echo `$*` > /tmp/out

    # return the command(s) outputs line by line
    IFS=$(echo -en "\n\b")
    arr=()
    exec 3</tmp/out
    while read -u 3 -r line
    do
        arr+=($line)
        echo $line
    done
    exec 3<&-
}

En guión principal:

#run your command:
cmd="echo hey ya; echo hey hi; printf `expr 10 + 10`'\n' ; printf $((10 + 20))'\n'"
get_prev $cmd
#or simply
get_prev "echo hey ya; echo hey hi; printf `expr 10 + 10`'\n' ; printf $((10 + 20))'\n'"

Bash guarda la variable incluso fuera del alcance anterior:

#get previous command outputs in arr
for((i=0; i<${#arr[@]}; i++)); do echo ${arr[i]}; done
Himanshu Tanwar
fuente
1

Solución muy simple

Uno que he usado por años.

Script (agregar a su .bashrco .bash_profile)

# capture the output of a command so it can be retrieved with ret
cap () { tee /tmp/capture.out}

# return the output of the most recent command that was captured by cap
ret () { cat /tmp/capture.out }

Uso

$ find . -name 'filename' | cap
/path/to/filename

$ ret
/path/to/filename

Tiendo a agregar | capal final de todos mis comandos. De esta forma, cuando descubro que quiero procesar texto en la salida de un comando de ejecución lenta, siempre puedo recuperarlo res.

Connor
fuente
¿Cuál es el punto de cat -en cap () { cat - | tee /tmp/capture.out}esta lista? ¿Tiene cap () { tee /tmp/capture.out }algún hipo que me falta?
Watson
1
@Watson buen punto! No sé por qué tengo eso allí, probablemente hábito. ¿Debo agregar de cat - | cat - | cat -antemano solo para hacerlo más interesante? 😉 Lo arreglaré una vez que lo haya probado para asegurarme de que sea realmente un spandrel
Connor
0

No estoy seguro exactamente para qué necesita esto, por lo que esta respuesta puede no ser relevante. Siempre puede guardar la salida de un comando: netstat >> output.txtpero no creo que sea lo que está buscando.

Sin embargo, existen opciones de programación; simplemente puede obtener un programa para leer el archivo de texto anterior después de ejecutar ese comando y asociarlo con una variable, y en Ruby, mi idioma de elección, puede crear una variable fuera de la salida del comando usando 'backticks':

output = `ls`                       #(this is a comment) create variable out of command

if output.include? "Downloads"      #if statement to see if command includes 'Downloads' folder
print "there appears to be a folder named downloads in this directory."
else
print "there is no directory called downloads in this file."
end

Pegue esto en un archivo .rb y ejecútelo: ruby file.rbcreará una variable fuera del comando y le permitirá manipularlo.

usuario4493605
fuente
9
"No estoy seguro exactamente para qué necesita esto" Bueno, digamos que (con lo que quiero decir que yo ) simplemente ejecuté un script de larga duración, quería buscar la salida y olvidé estúpidamente redirigir la salida antes de ejecutarla. Ahora quiero intentar remediar mi estupidez sin volver a ejecutar el script.
jscs
0

Tengo una idea que no tengo tiempo para intentar implementar de inmediato.

Pero qué pasa si haces algo como lo siguiente:

$ MY_HISTORY_FILE = `get_temp_filename`
$ MY_HISTORY_FILE=$MY_HISTORY_FILE bash -i 2>&1 | tee $MY_HISTORY_FILE
$ some_command
$ cat $MY_HISTORY_FILE
$ # ^You'll want to filter that down in practice!

Puede haber problemas con el almacenamiento intermedio de E / S. Además, el archivo puede ser demasiado grande. Uno tendría que encontrar una solución a estos problemas.

William Navarre
fuente
0

Creo que usar el comando de script podría ayudar. Algo como,

  1. script -c bash -qf fifo_pid

  2. Uso de las funciones de bash para configurar después del análisis.

el sudhakar
fuente
0

Si no desea volver a calcular el comando anterior, puede crear una macro que escanee el búfer de terminal actual, intente adivinar la salida -supuesta- del último comando, la copie en el portapapeles y finalmente la escriba en la terminal.

Se puede usar para comandos simples que devuelven una sola línea de salida (probado en Ubuntu 18.04 con gnome-terminal).

Instalar las siguientes herramientas: xdootool, xclip,ruby

En gnome-terminalir a Preferences -> Shortcuts -> Select ally configurarlo en Ctrl+shift+a.

Cree el siguiente script de ruby:

cat >${HOME}/parse.rb <<EOF
#!/usr/bin/ruby
stdin = STDIN.read
d = stdin.split(/\n/)
e = d.reverse
f = e.drop_while { |item| item == "" }
g = f.drop_while { |item| item.start_with? "${USER}@" }
h = g[0] 
print h
EOF

En la configuración del teclado, agregue el siguiente método abreviado de teclado:

bash -c '/bin/sleep 0.3 ; xdotool key ctrl+shift+a ; xdotool key ctrl+shift+c ; ( (xclip -out | ${HOME}/parse.rb ) > /tmp/clipboard ) ; (cat /tmp/clipboard | xclip -sel clip ) ; xdotool key ctrl+shift+v '

El atajo anterior:

  • copia el búfer de terminal actual en el portapapeles
  • extrae la salida del último comando (solo una línea)
  • lo escribe en el terminal actual
Giuliano
fuente