Ponowne użycie danych wyjściowych z ostatniego polecenia w Bash

Odpowiedzi:

184

Możesz użyć $(!!) do przeliczenia (nie ponownego użycia) wyniku ostatniego polecenia.

!!Na własnych wykonuje ostatnie polecenie.

$ echo pierre
pierre
$ echo my name is $(!!)
echo my name is $(echo pierre)
my name is pierre
molwa
źródło
9
Możesz uchronić się przed wpisywaniem $(your_cmd)za pomocą grawitacji: `your_cmd`Zgodnie z instrukcją Gnu Bash są one funkcjonalnie identyczne. Oczywiście jest to również przedmiotem ostrzeżenia podniesionego przez @memecs
W praktyce wynik poleceń jest deterministyczny, więc nie męczyłbym się z ponownym obliczeniem. Jest to z pewnością przydatne, gdy chcesz zrobić coś takiego, git diff $(!!)gdzie poprzednie polecenie było findwywołaniem.
Sridhar Sarnobat
Najwyraźniej istnieją dobre powody, aby używać $()zamiast graweru. Ale prawdopodobnie nie ma nad czym stracić snu. github.com/koalaman/shellcheck/wiki/SC2006
Michael Crenshaw
1
@ user2065875, mimo że znaki odwrotne nadal działają w Bash, są one przestarzałe na rzecz $().
kurczyński
87

Odpowiedź brzmi nie. Bash nie przydziela żadnego wyjścia do żadnego parametru ani żadnego bloku w swojej pamięci. Ponadto możesz uzyskać dostęp do Bash tylko poprzez dozwolone operacje interfejsu. Prywatne dane Bash nie są dostępne, chyba że je zhakujesz.

konsolebox
źródło
2
Czy można mieć jakąś niestandardową funkcję „oprogramowania pośredniego” basha, która uruchamia się przed każdym poleceniem, a wszystko co robi, to zapisuje wynikowe dane wyjściowe do czegoś, co działa jak wewnętrzny schowek (powiedzmy BASH_CLIPBOARD)?
Pat Needham
@PatNeedham Not sure. Sprawdź ładowalne moduły. Sprawdź, czy potrafisz napisać.
konsolebox
11

Jednym ze sposobów jest użycie trap DEBUG:

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

Teraz stdout i stderr ostatnio wykonywanego polecenia będą dostępne w /tmp/out.log

Jedynym minusem jest to, że wykona polecenie dwukrotnie: raz, aby przekierować wyjście i błąd do /tmp/out.logi raz normalnie. Prawdopodobnie istnieje również sposób, aby temu zapobiec.

anubhava
źródło
10
więc kiedy piszę rm -rf * && cd .., nie tylko bieżący katalog, ale także katalog nadrzędny zostanie usunięty? takie podejście wydaje mi się bardzo niebezpieczne
phil294
1
Jak teraz mogę cofnąć tę czynność?
Kartik Chauhan
5
@KartikChauhan: Po prostu zróbtrap '' DEBUG
anubhava
7

Jeśli korzystasz z komputera Mac i nie masz nic przeciwko przechowywaniu wyników w schowku zamiast zapisywania w zmiennej, możesz użyć pbcopy i pbpaste jako obejścia.

Na przykład, zamiast robić to, aby znaleźć plik i porównać jego zawartość z innym plikiem:

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

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

Możesz to zrobić:

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

Ciąg /var/bar/app/one.phpznajduje się w schowku po uruchomieniu pierwszego polecenia.

Nawiasem mówiąc, pb in pbcopyi pbpasteoznacza pasteboard, synonim schowka.

Raghu Dodda
źródło
1
w systemie Linux $ find app -name 'one.php' | xclip $ diff $(xclip -o) /var/bar/two.php
si-mikey
ciekawe, nigdy nie myślałem o użyciu pbpasterazem z zastępowaniem poleceń.
Sridhar Sarnobat
Co jeśli zrobię pierwszą, a później zdam sobie sprawę, że była nieefektywna, jak zaoszczędzić czas obliczeniowy bez ponownego uruchamiania z drugą opcją?
NelsonGon
4

Zainspirowany odpowiedzią anubhavy, która moim zdaniem jest nie do zaakceptowania, ponieważ uruchamia każde polecenie dwukrotnie.

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

exec 3>&1
trap save_output DEBUG

W ten sposób wynik ostatniego polecenia znajduje się w / tmp / last, a polecenie nie jest wywoływane dwukrotnie.

olivecoder
źródło
1
Jedną wadą, którą odkryłem, jest to, że żadne z twoich poleceń nie myśli, że działają już na tty, co oznacza, że ​​potrzebujesz specjalnej uwagi, aby kody kolorów działały dla narzędzi takich jak greplub git diff+1
Marty Neal
2

Tak jak powiedział konsolebox, musiałbyś włamać się do samego basha. Oto całkiem dobry przykład, jak można to osiągnąć. Repozytorium stderred (w rzeczywistości przeznaczone do kolorowania standardowego wyjścia) zawiera instrukcje, jak je zbudować.

I spróbowaliśmy: definiowanie jakiegoś nowego deskryptora pliku wewnątrz .bashrcjak

exec 41>/tmp/my_console_log

(liczba jest dowolna) i stderred.codpowiednio zmodyfikuj , aby treść również została zapisana na fd 41. W pewnym sensie działała, ale zawiera mnóstwo bajtów NUL, dziwne formatowanie i jest w zasadzie danymi binarnymi, nieczytelnymi. Może ktoś, kto dobrze rozumie C, mógłby to wypróbować.

Jeśli tak, wszystko, co jest potrzebne do uzyskania ostatniej wydrukowanej linii, to tail -n 1 [logfile].

phil294
źródło
1

Tak, po co za każdym razem pisać dodatkowe wiersze. Możesz przekierować z powrotem na wejście, ale wyjście na wejście (1> & 0) nie działa. Nie będziesz też chciał wielokrotnie pisać funkcji w każdym pliku dla tego samego.

Jeśli nigdzie nie eksportujesz plików i zamierzasz używać ich tylko lokalnie, możesz ustawić w Terminalu deklarację funkcji. Musisz dodać funkcję w ~/.bashrcpliku lub w ~/.profilepliku. W drugim przypadku musisz włączyć Run command as login shellz Edit>Preferences>yourProfile>Command.

Utwórz prostą funkcję, powiedz:

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

W głównym skrypcie:

#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 zapisuje zmienną nawet poza poprzednim zakresem:

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

Bardzo proste rozwiązanie

Takiego, którego używam od lat.

Skrypt (dodaj do swojego .bashrclub .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 }

Stosowanie

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

$ ret
/path/to/filename

Zwykle dodaję | capna końcu wszystkich moich poleceń. W ten sposób, gdy stwierdzę, że chcę przetworzyć tekst na wyjściu wolno działającego polecenia, zawsze mogę go pobrać res.

Connor
źródło
Jaki jest sens cat -w cap () { cat - | tee /tmp/capture.out}tutaj? Czy cap () { tee /tmp/capture.out }pojawiają się jakieś problemy, których mi brakuje?
Watson
1
@Watson dobra uwaga! Nie wiem, dlaczego mam taki nawyk. Czy powinienem dodać cat - | cat - | cat -wcześniej, żeby było ciekawiej? 😉 Naprawię to po przetestowaniu, aby upewnić się, że to naprawdę spandrel
Connor,
0

Nie jestem pewien, do czego to potrzebujesz, więc ta odpowiedź może nie być odpowiednia. Zawsze możesz zapisać wynik polecenia:, netstat >> output.txtale nie sądzę, że tego właśnie szukasz.

Istnieją oczywiście opcje programowania; możesz po prostu uzyskać program, który przeczyta powyższy plik tekstowy po uruchomieniu tego polecenia i skojarzy go ze zmienną, aw Ruby, moim wybranym języku, możesz utworzyć zmienną z wyjścia polecenia za pomocą `` lewych apostrofów '':

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

Umieść to w pliku .rb i uruchom: ruby file.rba utworzy zmienną z polecenia i pozwoli na manipulowanie nią.

user4493605
źródło
9
„Nie wiem dokładnie, czego potrzebują to dla” Well, można powiedzieć, (przez co rozumiem ja ) po prostu zabrakło skrypt długim czasie trwania, chcesz szukać wyjścia, i głupio zapomniał przekierować wyjście przed wykonaniem. Teraz chcę spróbować naprawić swoją głupotę bez ponownego uruchamiania skryptu.
jscs
0

Mam pomysł, którego nie mam czasu na natychmiastową realizację.

Ale co, jeśli zrobisz coś takiego:

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

Mogą wystąpić problemy z buforowaniem we / wy. Plik może również stać się zbyt duży. Trzeba byłoby znaleźć rozwiązanie tych problemów.

William Navarre
źródło
0

Myślę, że użycie polecenia skryptu może pomóc. Coś jak,

  1. skrypt -c bash -qf fifo_pid

  2. Używanie funkcji basha do ustawiania po przeanalizowaniu.

sudhakar
źródło
0

Jeśli nie chcesz ponownie obliczyć poprzedniego polecenia, możesz utworzyć makro, które skanuje bieżący bufor terminala, próbuje odgadnąć -supposed- wynik ostatniego polecenia, kopiuje je do schowka i na koniec wpisuje do terminala.

Może być używany do prostych poleceń, które zwracają pojedynczą linię wyjścia (testowane na Ubuntu 18.04 z gnome-terminal).

Zainstalować następujące narzędzia: xdootool, xclip,ruby

W gnome-terminalpodróży do Preferences -> Shortcuts -> Select alli ustawić go Ctrl+shift+a.

Utwórz następujący skrypt 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

W ustawieniach klawiatury dodaj następujący skrót klawiaturowy:

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 '

Powyższy skrót:

  • kopiuje bieżący bufor terminala do schowka
  • wyodrębnia wynik ostatniego polecenia (tylko jedna linia)
  • wpisuje go w bieżącym terminalu
Giuliano
źródło