Reutilizando a Saída do Último Comando no Bash

Respostas:

184

Você pode usar $(!!) para recalcular (não reutilizar) a saída do último comando.

Por !!si só, executa o último comando.

$ echo pierre
pierre
$ echo my name is $(!!)
echo my name is $(echo pierre)
my name is pierre
ling
fonte
9
Você pode evitar digitar $(your_cmd)usando backticks: `your_cmd`De acordo com o manual do Gnu Bash, eles são funcionalmente idênticos. Claro que isso também está sujeito à advertência levantada por @memecs
Na prática, a saída dos comandos é determinística, então eu não agonizaria com a recalculação. Certamente é útil quando você deseja fazer algo como git diff $(!!)onde o comando anterior foi uma findinvocação.
Sridhar Sarnobat 04/04
Aparentemente, existem boas razões para usar em $()vez de reticulares. Mas provavelmente nada para perder o sono. github.com/koalaman/shellcheck/wiki/SC2006
Michael Crenshaw
1
@ user2065875 embora os backticks ainda funcionem no Bash, eles foram preteridos em favor de $().
kurczynski 28/02
87

A resposta é não. O Bash não aloca nenhuma saída para nenhum parâmetro ou bloco em sua memória. Além disso, você só pode acessar o Bash por suas operações de interface permitidas. Os dados privados do Bash não estão acessíveis, a menos que você os hackie.

konsolebox
fonte
2
É possível ter alguma função customizada do 'middleware' do bash que é executada antes de cada comando, onde tudo o que faz é salvar a saída resultante em algo que atue como uma área de transferência interna (por exemplo, BASH_CLIPBOARD)?
Pat Needham
@PatNeedham Não tenho certeza. Verifique os módulos carregáveis. Veja se você pode escrever um.
konsolebox
11

Uma maneira de fazer isso é usando trap DEBUG:

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

Agora, o comando stdout e stderr do comando executado mais recentemente estará disponível em /tmp/out.log

A única desvantagem é que ele executará um comando duas vezes: uma vez para redirecionar a saída e o erro /tmp/out.loge uma vez normalmente. Provavelmente, há alguma maneira de evitar esse comportamento também.

anubhava
fonte
10
Então, quando eu digitar rm -rf * && cd .., não apenas o diretório atual, mas também o diretório pai será apagado? esta abordagem parece altamente perigoso para mim
phil294
1
Agora, como desfazer esta ação?
Kartik Chauhan
5
@KartikChauhan: Just dotrap '' DEBUG
anubhava
7

Se você estiver no Mac e não se importa de armazenar sua saída na área de transferência, em vez de gravar em uma variável, use pbcopy e pbpaste como uma solução alternativa.

Por exemplo, em vez de fazer isso para encontrar um arquivo e diferenciar seu conteúdo com outro arquivo:

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

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

Você poderia fazer isso:

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

A cadeia /var/bar/app/one.phpestá na área de transferência quando você executa o primeiro comando.

By the way, pb in pbcopye pbpastestand para área de transferência, um sinônimo de área de transferência.

Raghu Dodda
fonte
1
em linux $ find app -name 'one.php' | xclip $ diff $(xclip -o) /var/bar/two.php
si-mikey 13/03
interessante, nunca pensei em usar pbpastejunto com a substituição de comandos.
Sridhar Sarnobat 04/04
E se eu fizer a primeira e mais tarde perceber que era ineficiente, como economizo tempo computacional sem executar novamente com a segunda opção?
NelsonGon
4

Inspirado pela resposta de anubhava, que eu acho que não é realmente aceitável, pois executa cada comando duas vezes.

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

exec 3>&1
trap save_output DEBUG

Dessa forma, a saída do último comando está em / tmp / last e o comando não é chamado duas vezes.

olivecoder
fonte
1
Uma desvantagem que eu encontrei para isso é que nenhum de seus comandos acho que eles estão rodando em um tty mais, o que significa que precisam de atenção especial para obter os códigos de cores a trabalhar para utilitários como grepou git diffum qualquer maneira embora
Marty Neal
2

Como o konsolebox disse, você teria que invadir o próprio bash. Aqui está um bom exemplo de como alguém pode conseguir isso. O repositório stderred (realmente destinado a colorir stdout) fornece instruções sobre como construí-lo.

Eu tentei: Definindo um novo descritor de arquivo dentro .bashrccomo

exec 41>/tmp/my_console_log

(número é arbitrário) e modificar stderred.cde acordo para que o conteúdo também é gravado fd 41. tipo de funcionou, mas contém um monte de NUL bytes, formattings estranhos e é, basicamente, dados binários, não legível. Talvez alguém com um bom entendimento de C possa tentar isso.

Nesse caso, tudo o que é necessário para obter a última linha impressa é tail -n 1 [logfile].

phil294
fonte
1

Sim, por que digitar linhas extras sempre que acordado. Você pode redirecionar o retorno para a entrada, mas a saída para a entrada (1> & 0) não é necessária. Além disso, você não deseja escrever uma função repetidamente em cada arquivo para o mesmo.

Se você não estiver exportando os arquivos para qualquer lugar e pretender usá-lo apenas localmente, poderá fazer com que o Terminal configure a declaração de função. Você precisa adicionar a função no ~/.bashrcarquivo ou no ~/.profilearquivo. No segundo caso, você precisa ativar a Run command as login shellpartir de Edit>Preferences>yourProfile>Command.

Faça uma função simples, 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<&-
}

No script 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'"

O Bash salva a variável mesmo fora do escopo anterior:

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

Solução Muito Simples

Um que eu uso há anos.

Script (adicione ao seu .bashrcou .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

Costumo adicionar | capao final de todos os meus comandos. Dessa forma, quando eu descobrir que quero fazer o processamento de texto na saída de um comando de execução lenta, sempre posso recuperá-lo res.

Connor
fonte
Qual é o ponto de cat -nos cap () { cat - | tee /tmp/capture.out}aqui? Existe cap () { tee /tmp/capture.out }algum soluço que eu sinto falta?
Watson
1
@Watson bom ponto! Não sei por que tenho isso lá, provavelmente hábito. Devo adicionar cat - | cat - | cat -antes apenas para torná-lo mais interessante? 😉 Vou consertar uma vez que testei para garantir que seja realmente um spandrel
Connor
0

Não sei exatamente para que você está precisando disso, portanto, essa resposta pode não ser relevante. Você sempre pode salvar a saída de um comando:, netstat >> output.txtmas acho que não é isso que você está procurando.

É claro que existem opções de programação; você pode simplesmente obter um programa para ler o arquivo de texto acima após a execução desse comando e associá-lo a uma variável. No Ruby, minha linguagem preferida, você pode criar uma variável sem saída de 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

Coloque isso em um arquivo .rb e execute-o: ruby file.rbe ele criará uma variável fora do comando e permitirá que você a manipule.

user4493605
fonte
9
"Não tenho certeza exatamente do que você está precisando disso" Bem, digamos que (com isso quero dizer que eu ) apenas executei um script de longa duração, deseje pesquisar a saída e estupidamente esqueci de redirecionar a saída antes de executar. Agora, quero tentar remediar minha estupidez sem executar novamente o script.
JSCs
0

Eu tenho uma ideia que não tenho tempo para tentar implementar imediatamente.

Mas e se você fizer algo como o seguinte:

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

Pode haver problemas com o buffer de E / S. Além disso, o arquivo pode ficar muito grande. Seria necessário encontrar uma solução para esses problemas.

William Navarre
fonte
0

Eu acho que usar o comando script pode ajudar. Algo como,

  1. script -c bash -qf fifo_pid

  2. Usando os recursos do bash para definir após a análise.

o sudhakar
fonte
0

Se você não quiser recalcular o comando anterior, poderá criar uma macro que varre o buffer do terminal atual, tente adivinhar a saída do último comando - suposta -, copie-a para a área de transferência e, finalmente, digite-a no terminal.

Pode ser usado para comandos simples que retornam uma única linha de saída (testada no Ubuntu 18.04 com gnome-terminal).

Instalar as seguintes ferramentas: xdootool, xclip,ruby

Em gnome-terminalir para Preferences -> Shortcuts -> Select alle configurá-lo para Ctrl+shift+a.

Crie o seguinte 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

Nas configurações do teclado, adicione o seguinte atalho 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 '

O atalho acima:

  • copia o buffer do terminal atual para a área de transferência
  • extrai a saída do último comando (apenas uma linha)
  • digita-o no terminal atual
Giuliano
fonte