Як я можу виконати останній рядок виводу в bash?


12

Я знаю, що можу запустити останню команду в bash, з !!, але як я можу запустити останній рядок виводу?

Я думаю про випадок використання цього виходу:

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

Але я не знаю, як мені це зробити. Я думаю про щось на кшталт того !!, може, @@чи подібного?


У Супер Користувача є і це питання.


1
Чому б просто не скопіювати його та вставити?
Пілот6

1
@Tim Ви хочете способи запустити останній рядок виводу програми, але для цього конкретного випадку використання також є підхід зміни функції command_not_found_handleоболонки або сценарію, який він працює (зазвичай просто /usr/lib/command-not-found). Я думаю, ви могли б зробити це, щоб вам було запропоновано інтерактивно з можливістю автоматично встановити пакет, або (можливо, краще), щоб ім'я пакета було збережено десь і його можна було встановити, виконавши коротку команду. Це досить відрізняється від того, що ви тут задали, ви можете розглянути питання про це окремим запитанням.
Елія Каган


2
Можливо, це також буде доречно: github.com/nvbn/thefuck (Ігноруйте ім'я) цей інструмент автоматично виправляє команди git / apt / тощо :)
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буде розгорнуто !!до останньої виконаної команди

  • |&частина хитра. Зазвичай труба |приймає 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')

1
Ваш підхід припускає, що вихід буде ідентичним вдруге. Деякі команди можуть створити інший висновок наступного разу, і в цьому випадку дуже важко передбачити, що насправді буде виконувати останній рядок його виводу.
kasperd

1
@kasperd Ви маєте рацію. Здоровий, що стосується цього конкретного прикладу, результат повинен залишатися таким же ..
heemayl

7

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

Засоби взаємодії історії Баша не пропонують жодного механізму для вивчення результатів команд. Оболонка цього не зберігає , а розширення історії призначене спеціально для команд, які ви виконуєте, або частин цих команд.

Це залишає підхід до повторної повторної роботи останньої команди та підключення як 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 Якщо ви маєте на увазі окремий сценарій, який ви виконуєте - like ./script, а не те, що ви використовуєте джерело з .або sourceвбудованим - я не впевнений. Bash додає файл до виходу з оболонки або при запуску historyз -aабо -w(див. help history). Якби це було, команди в декількох інтерактивних оболонках будуть переплетені (відображатись у порядку, яким ви їх виконували). Ви можете зробити його додаванням негайно. . Але я думаю, що потрібно зробити більше, щоб зробити fcроботу над сценарієм. Пропоную опублікувати нове запитання з цього приводу. Якщо ви це робите, будь ласка, не соромтесь коментувати тут посилання на нього.
Елія Каган

2

Якщо ви використовуєте термінальний емулятор під X, як gnome-terminal, konsoleабо xterm, ви можете використовувати виділення X для чогось типу копіювання та вставки:

Використовуйте основний вибір

Виберіть весь рядок для запуску потрійним клацанням лівою кнопкою миші .

Потім вставте його в командний рядок за допомогою клацання середньої кнопки .

Це має працювати в більшості термінальних емуляторів. Він використовує основний вибір, не торкаючись буфера обміну - вони окремі.
Це технічний вибір, а потім об'єднано копіювання та вставлення за одну операцію.


1
@Tim Це правда - я це зрозумію.
Волкер Зігель

1

Як згадував Еліа Каган , оболонка не зберігає командний вихід. Однак є спосіб отримати останній рядок виводу останньої команди, не запускаючи команду знову , якщо ви запускаєте екран .

Покладіть у вас наступні рядки ~/.screenrc:

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

Потім ви можете натиснути Ctrl+, atщоб вставити попередній рядок у поточний рядок. Це можна зробити також автоматично натисканням клавіші enter, але, мабуть, краща ідея перевірити це перед запуском і просто натиснути клавішу enter вручну.

Як це працює:

  • register tреєструє рядок у буфер з назвою t. Рядок, що слідує за цією ключовою послідовністю.
  • bind tприв'язує до ключа t(тобто виконує за допомогою Ctrl+ at), process tозначає оцінювати вміст буфера t.
  • Сама послідовність означає:
    • ^a[(= Ctrla[): увійти в режим копіювання
    • kперейти на один рядок вгору, 0перейти до початку рядка, y$скопіювати до кінця рядка, (пробіл) виконати команду
    • ^a](= Ctrla]): вставити скопійований вміст
    • ^a^L(= CtrlaCtrlL) оновіть екран (інакше вставлений вміст не з’явиться, тому що ви його самі не ввели
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.