Повторне використання виводу з останньої команди в Bash


180

Чи зберігається вихід команди Bash у будь-якому регістрі? Наприклад, щось подібне до $?отримання виводу замість стану виходу.

Я міг би призначити вихідну змінну за допомогою:

output=$(command)

але це більше вводити текст ...


Відповіді:


184

Ви можете використовувати $(!!) для перерахунку (не повторного використання) висновку останньої команди.

The !!самостійно виконує останню команду.

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

9
Ви можете вберегти себе від введення тексту $(your_cmd)за допомогою зворотних посилань: `your_cmd`Відповідно до посібника Gnu Bash, вони функціонально однакові. Звичайно, це також є предметом попередження, яке порушило @memecs

На практиці вихід команд є детермінованим, тому я б не агонізував над цим перерахунок. Це, безумовно, корисно, коли ви хочете зробити щось на зразок того, git diff $(!!)де попередня команда була findвикликом.
Шрідхар Сарнобат

Мабуть, є вагомі причини використовувати $()замість задніх планок. Але, мабуть, нема чого втрачати сон. github.com/koalaman/shellcheck/wiki/SC2006
Майкл Креншо

1
@ user2065875, хоча типи все ще працюють у Bash, вони застаріли на користь $().
kurczynski

87

Відповідь - ні. Bash не виділяє жодного виводу жодному параметру або блоку в своїй пам'яті. Крім того, вам дозволяється отримувати доступ до Bash лише дозволеними операціями інтерфейсу. Особисті дані Bash недоступні, якщо ви не зламаєте їх.


2
Чи можливо мати якусь власну функцію bash 'middleware', яка працює перед кожною командою, і все, що це робиться, це зберегти отриманий результат у щось, що виконує функції внутрішнього буфера обміну (скажімо, BASH_CLIPBOARD)?
Пат Нідем

@PatNeedham Не впевнений. Перевірте завантажувані модулі. Подивіться, чи можете ви написати.
konsolebox

11

Один із способів зробити це за допомогою trap DEBUG:

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

Тепер найдавніші виконані команди stdout та stderr будуть доступні в /tmp/out.log

Мінус лише в тому, що він виконає команду двічі: один раз для переадресації виводу та помилки /tmp/out.logта нормально один раз. Напевно, є якийсь спосіб запобігти і цій поведінці.


10
тож коли я набираю rm -rf * && cd .., не тільки поточний каталог, але й батьківський dir буде видалено ?? такий підхід здається мені дуже небезпечним
phil294

1
Тепер, як я скасувати цю дію?
Картик Чаухан

5
@KartikChauhan: Просто зробиtrap '' DEBUG
anubhava

7

Якщо ви перебуваєте на комп'ютері Mac і не проти зберігати свій результат у буфері обміну, а не писати до змінної, ви можете використовувати pbcopy та pbpaste як спосіб вирішення.

Наприклад, замість того, щоб зробити це, щоб знайти файл і розмежувати його вміст з іншим файлом:

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

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

Ви можете це зробити:

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

Рядок /var/bar/app/one.phpзнаходиться в буфері обміну при запуску першої команди.

До речі, pb in pbcopyі pbpasteпідставка для картону, синонім буфера обміну.


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

Що цікаво, я ніколи не думав використовувати pbpasteразом із заміною команд.
Шрідхар Сарнобат

Що робити, якщо я роблю перший і пізніше усвідомлюю, що це було неефективно, як я заощаджую обчислювальний час без повторного використання другого варіанту?
НельсонГон

4

Натхненний відповіддю anubhava, який я вважаю, що насправді не прийнятний, оскільки виконує кожну команду двічі.

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

exec 3>&1
trap save_output DEBUG

Таким чином, вихід останньої команди є в / tmp / last, і команда не викликається двічі.


1
Один недолік я знайшов в тому , що жоден з ваших команд не думаю , що вони працюють в TTY більше, а це значить , що вам потрібно особливу увагу , щоб отримати колірні коди працюють такі утиліти , як grepі git diff+1 в будь-якому випадку , хоча
Марті Ніл

2

Як казав konsolebox, вам доведеться вилазити на себе. Ось досить хороший приклад того, як можна цього досягти. Складений сховище (фактично призначене для фарбування stdout) дає вказівки щодо його побудови.

Я спробував: визначити якийсь новий дескриптор файлу всередині, .bashrcяк

exec 41>/tmp/my_console_log

(число довільне) і stderred.cвідповідно змініть, щоб вміст також записувався до fd 41. Він начебто працював, але містить навантаження байтів NUL, дивного форматування і, в основному, бінарних даних, не читабельних. Можливо, хтось із хорошими розуміннями C може спробувати це.

Якщо так, то все, що потрібно для отримання останнього надрукованого рядка tail -n 1 [logfile].


1

Так, навіщо вводити зайві рядки щоразу, коли домовлялися. Ви можете переспрямувати повернуті на вхід, але вихід на вхід (1> & 0) - ні. Також ви не хочете знову і знову писати функцію у кожному файлі для одного і того ж.

Якщо ви не експортуєте файли кудись і маєте намір використовувати їх лише локально, у вас може бути встановлений термінал декларації функції. Ви повинні додати функцію у ~/.bashrcфайл чи у ~/.profileфайл. У другому випадку вам потрібно включити Run command as login shellз Edit>Preferences>yourProfile>Command.

Складіть просту функцію, скажіть:

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

Основний сценарій:

#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 зберігає змінну навіть поза попереднім діапазоном:

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

1

Дуже просте рішення

Той, яким я користувався роками.

Сценарій (додати до свого .bashrcабо .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 }

Використання

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

$ ret
/path/to/filename

Я схильний додавати | capдо кінця всі мої команди. Таким чином, коли я знаходжу, що хочу зробити обробку тексту на виході повільно запущеної команди, я завжди можу отримати її res.


Що таке точка cat -в cap () { cat - | tee /tmp/capture.out}тут? Чи є cap () { tee /tmp/capture.out }якісь гикавки, яких я пропускаю?
Уотсон

1
@Watson хороший момент! Я не знаю, чому у мене це є, швидше за все, звичка. Чи варто додати cat - | cat - | cat -заздалегідь лише те, щоб зробити його цікавішим? 😉 Я виправлю це, як тільки я протестую, щоб переконатися, що це справді чудо
Коннор,

0

Не впевнений, для чого саме вам це потрібно, тому ця відповідь може бути нерелевантною. Ви завжди можете зберегти результат команди:, netstat >> output.txtале я не думаю, що це те, що ви шукаєте.

Є, звичайно, варіанти програмування; ви можете просто отримати програму для читання текстового файлу вище після виконання цієї команди і пов’язати її зі змінною, і в Ruby, моїй мові вибору, ви можете створити змінну з виводу команди за допомогою "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

Вставте це у файл .rb та запустіть його: ruby file.rbі він створить змінну з команди та дозволить вам маніпулювати нею.


9
"Не впевнений, що саме для цього вам потрібно" Ну, скажіть, ви (під тим, що я маю на увазі, що я ) просто запустили довготривалий сценарій, хочете шукати вихід, і тупо забули перенаправити висновок перед виконанням. Тепер я хочу спробувати виправити свою дурість, не повторюючи сценарій.
jscs

0

У мене є ідея, що я не встигаю негайно намагатися реалізувати.

Але що робити, якщо ви робите щось таке:

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

Можуть виникнути проблеми з буферизацією IO. Також файл може бути занадто величезним. Треба було б вирішити ці проблеми.


0

Я думаю, що використання команди script може допомогти. Щось на зразок,

  1. скрипт -c bash -qf fifo_pid

  2. Використання функцій bash для встановлення після розбору.


0

Якщо ви не хочете перерахувати попередню команду, ви можете створити макрос, який сканує поточний буфер терміналу, намагається відгадати вихід-останньої команди -копіювати, скопіює її в буфер обміну і, нарешті, набере в термінал.

Його можна використовувати для простих команд, які повертають один рядок виводу (тестується на Ubuntu 18.04 с gnome-terminal).

Встановіть наступні інструменти: xdootool, xclip,ruby

У gnome-terminalшляху до Preferences -> Shortcuts -> Select allі встановити його Ctrl+shift+a.

Створіть такий сценарій рубіну:

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

У налаштуваннях клавіатури додайте наступну комбінацію клавіш:

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 '

Вищенаведений ярлик:

  • копіює поточний буфер терміналу в буфер обміну
  • витягує висновок останньої команди (лише один рядок)
  • вводить його в поточний термінал
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.