Réutilisation de la sortie de la dernière commande dans Bash


Réponses:


185

Vous pouvez utiliser $(!!) pour recalculer (pas réutiliser) la sortie de la dernière commande.

Le !!exécute seul la dernière commande.

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

9
Vous pouvez vous éviter de taper $(your_cmd)en utilisant des backticks: `your_cmd`selon le manuel de Gnu Bash, ils sont fonctionnellement identiques. Bien sûr, cela est également soumis à l'avertissement émis par @memecs

En pratique, la sortie des commandes est déterministe, donc je ne m'inquiéterais pas de recalculer. C'est certainement utile lorsque vous voulez faire quelque chose comme git diff $(!!)où la commande précédente était une findinvocation.
Sridhar Sarnobat

Apparemment, il y a de bonnes raisons d'utiliser à la $()place des backticks. Mais probablement rien pour perdre le sommeil. github.com/koalaman/shellcheck/wiki/SC2006
Michael Crenshaw

1
@ user2065875 bien que les backticks fonctionnent toujours dans Bash, ils sont déconseillés au profit de $().
kurczynski

87

La réponse est non. Bash n'alloue aucune sortie à aucun paramètre ni à aucun bloc de sa mémoire. De plus, vous n'êtes autorisé à accéder à Bash que par ses opérations d'interface autorisées. Les données privées de Bash ne sont accessibles que si vous les piratez.


2
Est-il possible d'avoir une fonction «middleware» bash personnalisée qui s'exécute avant chaque commande, où tout ce qu'elle fait est de sauvegarder la sortie résultante dans quelque chose qui agit comme un presse-papiers interne (par exemple, BASH_CLIPBOARD)?
Pat Needham

@PatNeedham Pas sûr. Vérifiez les modules chargeables. Voyez si vous pouvez en écrire un.
konsolebox

12

Une façon de faire est d'utiliser trap DEBUG:

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

Les stdout et stderr de la commande la plus récemment exécutée seront désormais disponibles dans /tmp/out.log

Le seul inconvénient est qu'il exécutera une commande deux fois: une fois pour rediriger la sortie et l'erreur vers /tmp/out.loget une fois normalement. Il existe probablement également un moyen d'empêcher ce comportement.


10
donc quand je tape rm -rf * && cd .., non seulement le répertoire courant, mais aussi le répertoire parent sera effacé ?? cette approche me semble très dangereuse
phil294

1
Maintenant, comment annuler cette action?
Kartik Chauhan

5
@KartikChauhan: Just dotrap '' DEBUG
anubhava

7

Si vous êtes sur mac et que cela ne vous dérange pas de stocker votre sortie dans le presse-papiers au lieu d'écrire dans une variable, vous pouvez utiliser pbcopy et pbpaste comme solution de contournement.

Par exemple, au lieu de faire cela pour rechercher un fichier et comparer son contenu avec un autre fichier:

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

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

Vous pouvez faire ceci:

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

La chaîne se /var/bar/app/one.phptrouve dans le presse-papiers lorsque vous exécutez la première commande.

À propos, pb in pbcopyet pbpastereprésente le presse-papiers, synonyme de presse-papiers.


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

intéressant, je n'avais jamais pensé à utiliser pbpasteavec la substitution de commandes.
Sridhar Sarnobat

Que faire si je fais le premier et que je me rends compte plus tard qu'il est inefficace, comment puis-je gagner du temps de calcul sans réexécuter avec la deuxième option?
NelsonGon

4

Inspiré par la réponse d'anubhava, qui je pense n'est pas réellement acceptable car il exécute chaque commande deux fois.

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 cette façon, la sortie de la dernière commande est dans / tmp / last et la commande n'est pas appelée deux fois.


1
Un inconvénient que j'ai trouvé à cela est qu'aucune de vos commandes ne pense plus qu'elle s'exécute dans un tty, ce qui signifie que vous avez besoin d'une attention particulière pour que les codes de couleur fonctionnent pour des utilitaires comme grepou git diff+1 de toute façon
Marty Neal

2

Comme l'a dit konsolebox, vous devrez pirater lui-même. Voici un très bon exemple de la manière dont on pourrait y parvenir. Le référentiel stderred (en fait destiné à la coloration de stdout) donne des instructions sur la façon de le construire.

Je l'ai essayé: définition d'un nouveau descripteur de fichier à l'intérieur .bashrccomme

exec 41>/tmp/my_console_log

(nombre est arbitraire) et modifier en stderred.cconséquence afin que le contenu est aussi écrit à fd 41. Ce genre de travaillé, mais contient des charges de NUL octets, formatages étranges et sont essentiellement des données binaires, pas lisibles. Peut-être que quelqu'un avec une bonne compréhension de C pourrait essayer cela.

Si tel est le cas, tout ce dont vous avez besoin pour obtenir la dernière ligne imprimée est tail -n 1 [logfile].


1

Ouais, pourquoi taper des lignes supplémentaires à chaque fois d'accord. Vous pouvez rediriger le retour vers l'entrée, mais la sortie vers l'entrée (1> & 0) est non. Vous ne voudrez pas non plus écrire une fonction encore et encore dans chaque fichier pour la même chose.

Si vous n'exportez les fichiers nulle part et que vous avez l'intention de les utiliser uniquement localement, vous pouvez demander à Terminal de configurer la déclaration de fonction. Vous devez ajouter la fonction dans un ~/.bashrcfichier ou dans un ~/.profilefichier. Dans le second cas, vous devez activer à Run command as login shellpartir de Edit>Preferences>yourProfile>Command.

Créez une fonction simple, dites:

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

Dans le 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'"

Bash enregistre la variable même en dehors de la portée précédente:

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

1

Solution très simple

Celui que j'utilise depuis des années.

Script (ajouter à votre .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 }

Usage

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

$ ret
/path/to/filename

J'ai tendance à ajouter | capà la fin de toutes mes commandes. De cette façon, lorsque je trouve que je veux faire du traitement de texte sur la sortie d'une commande à exécution lente, je peux toujours la récupérer avec res.


Quel est le point de cat -dans cap () { cat - | tee /tmp/capture.out}ici? cap () { tee /tmp/capture.out }Y a- t-il un hoquet qui me manque?
Watson le

1
@Watson bon point! Je ne sais pas pourquoi j'ai ça là-bas, probablement une habitude. Dois-je ajouter à l' cat - | cat - | cat -avance juste pour le rendre plus intéressant? 😉 Je vais le réparer une fois que j'ai testé pour m'assurer que c'est vraiment un tympan
Connor le

0

Vous ne savez pas exactement pourquoi vous en avez besoin, cette réponse peut donc ne pas être pertinente. Vous pouvez toujours enregistrer la sortie d'une commande:, netstat >> output.txtmais je ne pense pas que ce soit ce que vous recherchez.

Il existe bien sûr des options de programmation cependant; vous pouvez simplement obtenir un programme pour lire le fichier texte ci-dessus après l'exécution de cette commande et l'associer à une variable, et dans Ruby, mon langage de choix, vous pouvez créer une variable à partir de la sortie de la commande en utilisant des `` 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

Collez ceci dans un fichier .rb et exécutez-le: ruby file.rbet cela créera une variable hors de la commande et vous permettra de la manipuler.


9
"Je ne sais pas exactement pour quoi vous avez besoin de cela" Eh bien, disons que vous (par ce que je veux dire je ) viens d'exécuter un script de longue durée, que vous voulez rechercher la sortie et que vous avez bêtement oublié de rediriger la sortie avant de l'exécuter. Maintenant, je veux essayer de remédier à ma stupidité sans relancer le script.
jscs

0

J'ai une idée que je n'ai pas le temps d'essayer de mettre en œuvre immédiatement.

Mais que faire si vous faites quelque chose comme ce qui suit:

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

Il peut y avoir des problèmes avec la mise en mémoire tampon d'E / S. Le fichier peut également devenir trop volumineux. Il faudrait trouver une solution à ces problèmes.


0

Je pense que l'utilisation de la commande de script pourrait aider. Quelque chose comme,

  1. script -c bash -qf fifo_pid

  2. Utilisation des fonctionnalités bash à définir après l'analyse.


0

Si vous ne voulez pas recalculer la commande précédente, vous pouvez créer une macro qui scanne le tampon actuel du terminal, essaie de deviner la sortie -supposée- de la dernière commande, la copie dans le presse-papiers et la tape finalement dans le terminal.

Il peut être utilisé pour des commandes simples qui retournent une seule ligne de sortie (testé sur Ubuntu 18.04 avec gnome-terminal).

Installer les outils suivants: xdootool, xclip,ruby

Dans gnome-terminalallez à Preferences -> Shortcuts -> Select allet réglez-le sur Ctrl+shift+a.

Créez le script ruby ​​suivant:

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

Dans les paramètres du clavier, ajoutez le raccourci clavier suivant:

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 '

Le raccourci ci-dessus:

  • copie le tampon actuel du terminal dans le presse-papiers
  • extrait la sortie de la dernière commande (une seule ligne)
  • le tape dans le terminal actuel
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.