Riutilizzare l'output dall'ultimo comando in Bash


Risposte:


184

È possibile utilizzare $(!!) per ricalcolare (non riutilizzare) l'output dell'ultimo comando.

Da !!solo esegue l'ultimo comando.

$ echo pierre
pierre
$ echo my name is $(!!)
echo my name is $(echo pierre)
my name is pierre

9
Puoi salvarti dalla digitazione $(your_cmd)usando i backtick: `your_cmd`secondo il manuale di Gnu Bash sono funzionalmente identici. Naturalmente questo è anche soggetto all'avvertimento lanciato da @memecs

In pratica, l'output dei comandi è deterministico, quindi non mi dispiacerebbe per la sua ricompilazione. È sicuramente utile quando vuoi fare qualcosa di simile al punto in git diff $(!!)cui il comando precedente era una findchiamata.
Sridhar Sarnobat,

Apparentemente ci sono buoni motivi per usare al $()posto dei bastoncini. Ma probabilmente niente da perdere. github.com/koalaman/shellcheck/wiki/SC2006
Michael Crenshaw

1
@ user2065875 anche se i backtick funzionano ancora in Bash, sono deprecati a favore di $().
kurczynski,

87

La risposta è no. Bash non alloca alcun output a nessun parametro o blocco nella sua memoria. Inoltre, puoi accedere a Bash solo tramite le sue operazioni di interfaccia consentite. I dati privati ​​di Bash non sono accessibili a meno che non vengano hackerati.


2
È possibile avere una funzione 'middleware' bash personalizzata che viene eseguita prima di ogni comando, dove tutto ciò che fa è salvare l'output risultante in qualcosa che funge da appunti interno (diciamo, BASH_CLIPBOARD)?
Pat Needham,

@PatNeedham Non sono sicuro. Controllare i moduli caricabili. Vedi se riesci a scriverne uno.
konsolebox

11

Un modo per farlo è usando trap DEBUG:

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

Ora il comando stdout e stderr eseguiti più di recente saranno disponibili in /tmp/out.log

L'unico inconveniente è che eseguirà un comando due volte: una volta per reindirizzare l'output e l'errore verso /tmp/out.loge una volta normalmente. Probabilmente esiste un modo per prevenire anche questo comportamento.


10
così quando scrivo rm -rf * && cd .., non solo la directory corrente, ma anche la directory principale verrà cancellata ?? questo approccio mi sembra molto pericoloso
phil294

1
Ora come posso annullare questa azione?
Kartik Chauhan,

5
@KartikChauhan: Just dotrap '' DEBUG
anubhava,

7

Se sei su Mac e non ti dispiace archiviare il tuo output negli appunti invece di scrivere su una variabile, puoi usare pbcopy e pbpaste come soluzione alternativa.

Ad esempio, invece di farlo per trovare un file e diffrarne il contenuto con un altro file:

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

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

Potresti farlo:

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

La stringa /var/bar/app/one.phpè negli Appunti quando si esegue il primo comando.

A proposito, pb in pbcopye pbpastesta per pasteboard, un sinonimo di appunti.


1
su linux $ find app -name 'one.php' | xclip $ diff $(xclip -o) /var/bar/two.php
si-mikey,

interessante, non avrei mai pensato di usare pbpasteinsieme alla sostituzione dei comandi.
Sridhar Sarnobat,

Che cosa succede se eseguo il primo e successivamente mi rendo conto che era inefficiente, come posso risparmiare tempo di calcolo senza rieseguire con la seconda opzione?
Nelson Gon,

4

Ispirato dalla risposta di Anubhava, che a mio avviso non è accettabile in quanto esegue ogni comando due volte.

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

exec 3>&1
trap save_output DEBUG

In questo modo l'output dell'ultimo comando è in / tmp / last e il comando non viene chiamato due volte.


1
Un aspetto negativo che ho scoperto è che nessuno dei tuoi comandi crede di essere in esecuzione in un tty, il che significa che hai bisogno di un'attenzione speciale per far funzionare i codici colore per utility come grepo git diff+1 comunque
Marty Neal,

2

Come ha detto Konsolebox, dovresti hackerare Bash stesso. Ecco un buon esempio di come si potrebbe raggiungere questo obiettivo. Il repository stderred (in realtà pensato per colorare lo stdout) fornisce istruzioni su come costruirlo.

Ci ho provato: definire alcuni nuovi descrittori di file all'interno di .bashrclike

exec 41>/tmp/my_console_log

(numero è arbitrario) e modificare stderred.cdi conseguenza in modo che il contenuto viene scritto anche per fd 41. tipo di lavorato, ma contiene un sacco di NUL byte, formattazioni strani ed è fondamentalmente dati binari, non leggibile. Forse qualcuno con una buona comprensione di C potrebbe provarlo.

In tal caso, tutto ciò che è necessario per ottenere l'ultima riga stampata è tail -n 1 [logfile].


1

Sì, perché digitare righe extra ogni volta concordato. È possibile reindirizzare restituito all'input, ma l'output all'input (1> & 0) è nope. Inoltre non vorrai scrivere una funzione ancora e ancora in ogni file per lo stesso.

Se non si esportano i file da nessuna parte e si intende utilizzarli solo localmente, è possibile impostare Terminale nella configurazione della dichiarazione delle funzioni. Devi aggiungere la funzione nel ~/.bashrcfile o nel ~/.profilefile. Nel secondo caso, è necessario abilitare Run command as login shellda Edit>Preferences>yourProfile>Command.

Crea una funzione semplice, ad esempio:

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<&-
}

Nella sceneggiatura principale:

#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 salva la variabile anche al di fuori dell'ambito precedente:

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

1

Soluzione molto semplice

Uno che ho usato per anni.

Script (aggiungi al tuo .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

Tendo ad aggiungere | capalla fine di tutti i miei comandi. In questo modo, quando trovo che voglio eseguire l'elaborazione del testo sull'output di un comando a esecuzione lenta, posso sempre recuperarlo con res.


Qual è il punto di cat -a cap () { cat - | tee /tmp/capture.out}qui? Fa cap () { tee /tmp/capture.out }incorrere alcun inconveniente mi manca?
Watson,

1
@Watson buon punto! Non so perché l'ho preso lì, probabilmente abitudine. Devo aggiungere cat - | cat - | cat -prima solo per renderlo più interessante? 😉 Risolverò una volta che ho provato per assicurarsi che sia davvero un spandrel
Connor

0

Non sono sicuro di cosa ti serva, quindi questa risposta potrebbe non essere pertinente. Puoi sempre salvare l'output di un comando:, netstat >> output.txtma non credo sia quello che stai cercando.

Esistono ovviamente opzioni di programmazione; potresti semplicemente ottenere un programma per leggere il file di testo sopra dopo che quel comando è stato eseguito e associarlo a una variabile, e in Ruby, la mia lingua preferita, puoi creare una variabile fuori dal comando usando 'backtick':

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

Inseriscilo in un file .rb ed eseguilo: ruby file.rbe creerà una variabile fuori dal comando e ti permetterà di manipolarlo.


9
"Non sono esattamente sicuro di cosa ti serva per questo" Bene, dire che (con questo intendo ) ho appena eseguito uno script di lunga durata, vuoi cercare l'output e stupidamente dimenticato di reindirizzare l'output prima di eseguire. Ora voglio provare a porre rimedio alla mia stupidità senza rieseguire la sceneggiatura.
jscs,

0

Ho un'idea che non ho tempo per provare a implementare immediatamente.

Ma cosa succede se fai qualcosa del genere:

$ 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!

Potrebbero esserci problemi con il buffering IO. Inoltre, il file potrebbe diventare troppo grande. Si dovrebbe trovare una soluzione a questi problemi.


0

Penso che usare il comando script potrebbe essere d'aiuto. Qualcosa di simile a,

  1. script -c bash -qf fifo_pid

  2. Utilizzo delle funzionalità di bash da impostare dopo l'analisi.


0

Se non si desidera ricalcolare il comando precedente, è possibile creare una macro che esegue la scansione del buffer del terminale corrente, provare a indovinare l'output fornito dall'ultimo comando, copiarlo negli Appunti e infine digitarlo sul terminale.

Può essere utilizzato per semplici comandi che restituiscono una singola riga di output (testato su Ubuntu 18.04 con gnome-terminal).

Installare i seguenti strumenti: xdootool, xclip,ruby

In gnome-terminalvai a Preferences -> Shortcuts -> Select alle impostalo su Ctrl+shift+a.

Crea il seguente script 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

Nelle impostazioni della tastiera aggiungi la seguente scorciatoia da tastiera:

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 '

La scorciatoia sopra:

  • copia il buffer dei terminali corrente negli appunti
  • estrae l'output dell'ultimo comando (solo una riga)
  • lo digita nel terminale corrente
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.