Jak mogę wykonać ostatni wiersz wyjścia w bash?

12

Wiem, że mogę uruchomić ostatnie polecenie w bash !!, ale jak mogę uruchomić ostatni wiersz wyniku?

Mam na myśli przypadek użycia tego wyjścia:

The program 'git' is currently not installed. You can install it by typing:
sudo apt-get install git

Ale nie wiem, jak mogę to uruchomić. Myślę o czymś takim !!, może @@lub podobnym?


Super Użytkownik też ma to pytanie.

Tim
źródło
1
Dlaczego po prostu go nie skopiujesz i nie wkleisz?
Pilot6
1
@Tim Chcesz mieć sposoby na uruchomienie ostatniego wiersza wyniku programu, ale w tym konkretnym przypadku użycia istnieje również podejście do zmiany command_not_found_handlefunkcji powłoki lub uruchamianego skryptu (zwykle po prostu /usr/lib/command-not-found). Myślę, że możesz to zrobić, aby wyświetlać się interaktywnie z opcją automatycznego instalowania pakietu lub (prawdopodobnie lepiej), aby nazwa pakietu była gdzieś przechowywana i można ją zainstalować, uruchamiając krótkie polecenie. To wystarczająco różni się od tego, o co tutaj pytałeś, możesz rozważyć opublikowanie osobnego pytania na ten temat.
Eliah Kagan
2
Może być również istotne: github.com/nvbn/thefuck (Ignoruj nazwy) to narzędzie auto koryguje git / apt / etc polecenia :)
bhathiya-perera

Odpowiedzi:

16

Polecenie $(!! |& tail -1)powinno wykonać:

$ git something
The program 'git' is currently not installed. You can install it by typing:
sudo apt-get install git

$ $(!! |& tail -1)
$(git something |& tail -1)
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following extra packages will be installed:
git-man liberror-perl

Jak widać sudo apt-get install gitpolecenie jest uruchomione.

EDYCJA: Rozkładanie$(!! |& tail -1)

  • $()jest bashwzorcem zastępowania poleceń

  • bashrozwinie !!się do ostatniego wykonanego polecenia

  • |&część jest trudna. Normalnie potok |pobiera STDOUT polecenia po lewej stronie i przekazuje go jako STDIN do polecenia po prawej stronie |, w twoim przypadku poprzednie polecenie wypisuje swoje wyjście na STDERR jako komunikat o błędzie. Tak więc robienie |nie pomogłoby, musimy przekazać zarówno STDOUT, jak i STDERR (lub tylko STDERR) do polecenia po prawej stronie. |&przekaże STDOUT i STDERR zarówno jako STDIN do polecenia po prawej stronie. Alternatywnie, lepiej możesz przekazać tylko STDERR:

    $(!! 2>&1 >/dev/null | tail -1)
  • tail -1jak zwykle wydrukuje ostatni wiersz z jego wejścia. Tutaj zamiast wypisywania ostatniego wiersza możesz być bardziej precyzyjny, jak wypisywanie wiersza zawierającego apt-get installpolecenie:

    $(!! 2>&1 >/dev/null | grep 'apt-get install')
heemayl
źródło
1
Twoje podejście zakłada, że ​​wynik będzie identyczny za drugim razem. Niektóre polecenia mogą wygenerować inny wynik następnym razem, w takim przypadku bardzo trudno jest przewidzieć, co faktycznie zrobi wykonanie ostatniego wiersza jego wyniku.
kasperd
1
@kasperd Masz rację .. jeśli chodzi o ten konkretny przykład, wynik powinien pozostać taki sam ..
heemayl
7

TL; DR: alias @@='$($(fc -ln -1) |& tail -1)'

Funkcje interakcji historii Basha nie oferują żadnego mechanizmu do sprawdzania wyników poleceń. Powłoka tego nie przechowuje , a ekspansja historii dotyczy specjalnie poleceń, które sam wykonałeś, lub części tych poleceń.

Pozostawia to podejście polegające na ponownym uruchomieniu ostatniego polecenia i przekazaniu zarówno stdout, jak i stderr ( |&) do podstawienia polecenia. Odpowiedź heemayla osiąga to, ale nie można jej użyć w aliasie, ponieważ powłoka wykonuje ekspansję historii przed rozwinięciem aliasów, a nie później.

Nie mogę też sprawić, by rozszerzenie historii działało w funkcji powłoki, nawet poprzez włączenie go w funkcji za pomocą set -H. Podejrzewam, !!że funkcja nigdy nie zostanie rozwinięta i nie jestem pewien, do czego by ją rozwinął, ale w tej chwili nie jestem pewien , dlaczego tak nie jest.

Dlatego jeśli chcesz skonfigurować rzeczy tak, abyś mógł to zrobić przy bardzo małym pisaniu, powinieneś użyć fcwbudowanej powłoki zamiast rozszerzenia historii, aby wyodrębnić ostatnie polecenie z historii. Ma to tę dodatkową zaletę, że działa nawet po wyłączeniu rozszerzania historii.

Jak pokazano w Gordon Davisson jest odpowiedzią na stworzenie aliasu zawierające interpretację historii bash (na Super User ), $(fc -ln -1)symuluje !!. To z podłączeniem do !!w dowodzenia heemayl za $(!! |& tail -1) plony:

$($(fc -ln -1) |& tail -1)

Działa to podobnie, $(!! |& tail -1)ale można przejść do definicji aliasu:

alias @@='$($(fc -ln -1) |& tail -1)'

Po uruchomieniu tej definicji, włożeniu jej .bash_aliaseslub .bashrcuruchomieniu nowej powłoki, możesz po prostu wpisać @@(lub jakkolwiek nazwałeś alias), aby spróbować wykonać ostatni wiersz danych wyjściowych z ostatniego polecenia.

ek@Io:~$ alias @@='$($(fc -ln -1) |& tail -1)'
ek@Io:~$ evolution
The program 'evolution' is currently not installed. You can install it by typing:
sudo apt-get install evolution
ek@Io:~$ @@
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following extra packages will be installed:
  evolution-common evolution-data-server evolution-data-server-online-accounts
....
Eliah Kagan
źródło
Czy jest jakaś opcja przeniesienia tego do osobnego skryptu? Próbowałem tego i ostatecznie nie powiodło się, ponieważ fc nic nie wyświetla ... Ponieważ fc śledzi historię bieżącej sesji powłoki, więc przeniesienie jej do osobnego skryptu otwiera inną sesję z pustą historią, oczywiście ... Dodałem kilka polecenia powyżej wiersza fc w skrypcie i jedno z nich zostało wykonane. Zastanawiam się więc, czy istnieje jakaś opcja poinformowania skryptu, że „kontekstem” jest sesja bash, która go wykonała. Na przykład kilka argumentów za #! / Bin / bash czy coś ...
Exterminator13
@ Exterminator13 Jeśli masz na myśli osobny skrypt, który prowadzony --like ./script, nie to, że źródło z .lub sourcewbudowane - jestem pewien. Bash dołącza do pliku przy wyjściu z powłoki lub po uruchomieniu historyz -alub -w(patrz help history). Gdyby tak było, polecenia w wielu interaktywnych powłokach byłyby przeplatane (pojawiające się w kolejności ich uruchamiania). Możesz sprawić, że będzie się od razu dołączał. . Ale myślę, że jest jeszcze wiele do zrobienia, aby fcpracować w skrypcie. Sugeruję opublikowanie nowego pytania na ten temat. Jeśli tak, prosimy o komentarz tutaj z linkiem do niego.
Eliah Kagan
2

Jeśli używasz emulatora terminala pod X, jak gnome-terminal, konsolelub xterm, można użyć zaznaczenia X na coś skopiować i wkleić:

Użyj wyboru podstawowego

Wybierz całą linię, aby uruchomić trzykrotnie lewym przyciskiem myszy .

Następnie wklej go do wiersza poleceń za pomocą środkowego kliknięcia przycisku .

Powinno to działać w większości emulatorów terminali. Wykorzystuje podstawowy wybór, bez dotykania schowka - są one oddzielne.
Technicznie wybiera, a następnie łączy kopiowanie i wklejanie w jednej operacji.

Volker Siegel
źródło
1
@Tim To prawda - wyjaśnię to.
Volker Siegel,
1

Jak wspomniał Eliah Kagan , powłoka nie przechowuje danych wyjściowych polecenia. Istnieje jednak sposób na uzyskanie ostatniego wiersza wyjścia ostatniego polecenia bez ponownego uruchamiania polecenia , jeśli uruchomisz screen .

Umieść w tobie następujące linie ~/.screenrc:

register t "^a[k0y$ ^a]^a^L"
bind t process t

Następnie możesz nacisnąć Ctrl+, ataby wstawić poprzednią linię do bieżącego monitu. Byłoby możliwe, aby to również automatycznie naciskało klawisz Enter, ale prawdopodobnie lepiej jest to sprawdzić przed uruchomieniem i po prostu nacisnąć klawisz Enter ręcznie.

Jak to działa:

  • register trejestruje ciąg znaków w buforze o nazwie t. Ciąg następujący po nim jest sekwencją klucza.
  • bind twiąże się z kluczem t(tzn. wykonuje za pomocą Ctrl+ at), process toznacza ocenę zawartości wymienionego bufora t.
  • Sama sekwencja oznacza:
    • ^a[(= Ctrla[): wejdź w tryb kopiowania
    • kprzejdź o jedną linię w górę, 0przejdź na początek linii, y$kopiuj do końca linii, (spacja) wykonaj polecenie
    • ^a](= Ctrla]): wklej skopiowaną treść
    • ^a^L(= CtrlaCtrlL) odśwież ekran (w przeciwnym razie wklejona zawartość nie pojawi się, ponieważ nie wpisałeś jej samodzielnie)
atextor
źródło