Как я могу выполнить последнюю строку вывода в Bash?

12

Я знаю, что могу запустить последнюю команду в bash !!, но как я могу запустить последнюю строку вывода?

Я думаю о случае использования этого вывода:

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

Но я не знаю, как я могу это запустить. Я думаю о чем-то похожем !!, может быть @@или похожем?


У Супер пользователя тоже есть этот вопрос.

Тим
источник
1
Почему бы тебе просто не скопировать и не вставить?
Pilot6
1
@Tim Вам нужны способы запуска последней строки вывода программы, но для этого конкретного случая использования есть также подход к изменению command_not_found_handleфункции оболочки или сценария, который она запускает (обычно просто /usr/lib/command-not-found). Я думаю, вы могли бы сделать это так, чтобы вам предлагали интерактивную подсказку с опцией автоматической установки пакета или (возможно, лучше), чтобы имя пакета где-то сохранялось и могло быть установлено с помощью короткой команды. Это достаточно отличается от того, что вы спросили здесь, вы можете рассмотреть возможность размещения отдельного вопроса об этом.
Элия ​​Каган
2
@EliahKagan askubuntu.com/questions/621912/…
Тим
2
Также может иметь значение: github.com/nvbn/thefuck (игнорировать имя), этот инструмент автоматически исправляет команды git / apt / etc :)
bhathiya-perera

Ответы:

16

Команда $(!! |& tail -1)должна сделать:

$ 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

Как видите, sudo apt-get install gitкоманда запущена.

РЕДАКТИРОВАТЬ: ломать$(!! |& tail -1)

  • $()это bashшаблон подстановки команд

  • bashрасширится !!до последней выполненной команды

  • |&часть хитрая. Обычно pipe |принимает STDOUT левой команды и передает ее как STDIN команде справа |, в вашем случае предыдущая команда выводит свой вывод на STDERR в виде сообщения об ошибке. Таким образом, выполнение |не поможет, нам нужно передать как STDOUT, так и STDERR (или только STDERR) команде с правой стороны. |&передаст STDOUT и STDERR как STDIN для правой команды. С другой стороны, лучше вы можете только передать STDERR:

    $(!! 2>&1 >/dev/null | tail -1)
  • tail -1как обычно, напечатает последнюю строку из своего ввода. Здесь вместо того, чтобы печатать последнюю строку, вы можете быть более точным, как печатать строку, содержащую apt-get installкоманду:

    $(!! 2>&1 >/dev/null | grep 'apt-get install')
heemayl
источник
1
Ваш подход предполагает, что результат будет идентичным во второй раз. Некоторые команды могут выдавать другой вывод в следующий раз, и в этом случае очень трудно предсказать, что на самом деле будет делать выполнение последней строки своего вывода.
Касперд
1
@ kasperd Вы правы ... поскольку этот конкретный пример важен, результат должен остаться прежним ..
heemayl
7

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

Средства взаимодействия с историей Bash не предлагают никакого механизма для проверки вывода команд. Оболочка не хранит это , и расширение истории специально для команд, которые вы сами запускаете, или частей этих команд.

Это оставляет подход повторного запуска последней команды и передачи как stdout, так и stderr ( |&) в подстановку команд. Ответ Heemayl достигает этого, но не может использоваться в псевдониме, потому что оболочка выполняет расширение истории до расширения псевдонимов, а не после.

Я не могу заставить расширение истории работать в функции оболочки, даже включив его в функцию с помощью set -H. Я подозреваю, !!что функция никогда не будет расширена, и я не уверен, что она будет расширена, если бы она была, но сейчас я точно не знаю, почему это не так.

Поэтому, если вы хотите настроить все так, чтобы вы могли делать это с минимальным набором текста, вы должны использовать fcвстроенную оболочку вместо расширения истории, чтобы извлечь последнюю команду из истории. Это имеет дополнительное преимущество, заключающееся в том, что оно работает, даже если расширение истории отключено

Как показано в Гордоном Дэвиссоне «s ответа на создание псевдонима , содержащее расширение истории Баша (на Super User ), $(fc -ln -1)симулирует !!. Подключив это в течение !!в командных heemayl по $(!! |& tail -1) урожайности:

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

Это работает как, $(!! |& tail -1)но может идти в определении псевдонима:

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

После того, как вы запустите это определение или вставите его в .bash_aliasesили .bashrcи запустите новую оболочку, вы можете просто напечатать @@(или как бы вы ни назвали псевдоним), чтобы попытаться выполнить последнюю строку вывода из последней команды.

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
....
Элия ​​Каган
источник
Есть ли возможность перенести это в отдельный скрипт? Я попробовал это и, наконец, потерпел неудачу, потому что fc ничего не выводит ... Поскольку fc следует истории текущего сеанса оболочки, поэтому перемещение его в отдельный скрипт открывает там другой сеанс с пустой историей, конечно ... Я добавил несколько Команды над строкой fc в скрипте, и одна из них была выполнена. Итак, мне интересно, есть ли возможность сообщить сценарию, что его «контекст» - это сеанс bash, который его выполнил. Например, некоторые аргументы в пользу #! / Bin / bash или чего-то еще ...
Exterminator13
@ Exterminator13 Если вы имеете в виду отдельный скрипт, который вы запускаете - например ./script, не то, что вы используете исходный код .или sourceвстроенный - я не уверен. Bash добавляет к файлу при выходе из оболочки или при запуске historyс помощью -aили -w(см. help history). В противном случае команды в нескольких интерактивных оболочках будут чередоваться (отображаются в порядке их запуска). Вы можете добавить его немедленно. , Но я думаю, что есть еще что-то, чтобы сделать fcработу в сценарии. Я предлагаю опубликовать новый вопрос по этому поводу. Если вы это сделаете, пожалуйста, не стесняйтесь комментировать здесь со ссылкой на него.
Элия ​​Каган
2

Если вы используете эмулятор терминала под X, например gnome-terminal, konsoleили xterm, вы можете использовать выбор X для чего-то вроде копирования и вставки:

Используйте основной выбор

Выберите всю строку для запуска с тройным щелчком левой кнопки .

Затем вставьте его в командную строку , используя среднюю кнопку .

Это должно работать в большинстве терминальных эмуляторов. Он использует первичный выбор, не касаясь буфера обмена - они раздельные.
Он выбирает, а затем объединяет копирование и вставку в одной операции, технически.

Volker Siegel
источник
1
@Tim Это правда - я проясню это.
Фолькер Сигел
1

Как отметил Элия ​​Каган , оболочка не хранит вывод команды. Тем не менее, есть способ получить последнюю строку вывода последней команды, без повторного запуска команды , если вы запускаете screen .

Вставьте в вас следующие строки ~/.screenrc:

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

Затем вы можете нажать Ctrl+, atчтобы вставить предыдущую строку в текущее приглашение. Можно было бы сделать это также автоматически, нажав клавишу ввода, но, вероятно, лучше проверить это перед запуском и просто нажать клавишу ввода вручную.

Как это устроено:

  • register tрегистрирует строку в буфер с именем t. Последующая строка является ключевой последовательностью.
  • bind tпривязывается к ключу t(т. е. выполняется с использованием Ctrl+ at), process tозначает оценку содержимого буфера с именем t.
  • Сама последовательность означает:
    • ^a[(= Ctrla[): войти в режим копирования
    • kперейти на одну строку вверх, 0перейти к началу строки, y$скопировать до конца строки (пробел) завершить команду
    • ^a](= Ctrla]): вставить скопированный контент
    • ^a^L(= CtrlaCtrlL) обновить экран (иначе вставленный контент не будет отображаться, потому что вы его не печатали сами
atextor
источник