Чи може zsh отримати доступ до stdout програми останнього запуску?


16

Я часто використовую findабо locateдізнаюся про шляхи.

(~) locate foobar.mmpz
/home/progo/lmms/projects/foobar.mmpz

Наступним кроком є ​​часто відкриття або іншим способом маніпулювання файлами. У такому щасливому випадку, як вище, я можу це зробити:

(~) ls `!!`
ls `locate foobar.mmpz`
/home/progo/lmms/projects/foobar.mmpz

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

Чи буде спосіб підключити zsh для зберігання stdout в масив для подальшої маніпуляції? Зрештою, завдання оболонки - перенаправляти потоки до користувача. Я думаю, що це може зберігати перші N і останні N рядків у змінній для негайного подальшого використання, як $?і інші.

Гаразд, це досить круто: /unix//a/59704/5674 . Я зараз запитую про zsh-ноу-хау (і перенесення коду на zsh) для встановлення такого типу захоплення після кожного рядка запуску.


Це не дійсно завдання оболонки перенаправляти потік до користувача. Програми записують свій вихід на термінальний пристрій, оболонка в цьому не бере участь.
Стефан Шазелас

@StephaneChazelas, але це завдання оболонки, скажімо, перенаправляти потоки у файли на запити користувача. Існують також рецепти zsh, які забарвлюють stderr в червоний колір, щоб відокремити його від stdout. Я думаю, що це вказує на роль оболонки в потокових питаннях.
unperson325680

Так, ви можете це зробити, використовуючи screenабо scriptі precmd і preexec гачки.
Стефан Шазелас

Відповіді:


7

На більшості термінальних емуляторів немає функції збору виводу з екрана. Здається, я згадую автора xterm («опорний» термінальний емулятор), заявивши, що це буде важко реалізувати. Навіть якби це було можливо, оболонці доведеться відслідковувати, де був останній рядок.

Таким чином, вам не вдасться знову запустити команду, якщо ви не скористаєтесь ручним механізмом, характерним для терміналу, таким як вставка копії мишею в xterm або за допомогою клавіатури на екрані.

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

Ви можете повторно запустити команду та захопити її вихід. Існують різні способи зробити кожен. Для повторної роботи команди можна використовувати:

  • !! підміна історії - найзручніша для набору;
  • fc -e -, яку можна використовувати у функції.

Щоб захопити вихід, ви можете використовувати підстановку команд або функцію на зразок наступного:

K () {
  lines=("${(f@)$(cat)}")
}
!! |K

Це встановлює linesмасив на висновок команди, яка в нього вкладена.


Нехай буде так. Зараз я розумію у світлі хороших пояснень, що повністю автоматичний підхід є надто важким, як це є, друге найкраще - це щось на зразок Kвашого.
unperson325680

3

Ось перший зріз чогось, щоб помістити останній рядок виводу у змінну, що називається $lastline.

precmd () {                                                                                                                                                                                                        
    exec 2>&- >&-
    lastline=$(tail -1 ~/.command.out) 
    sleep 0.1   # TODO: synchronize better
    exec > /dev/tty 2>&1
}

preexec() {
    exec > >(tee ~/.command.out&)
}

Це використовує ЗШ - х preexecГачок для запуску execз teeзберігати копію стандартного виводу командування, а потім використовує precmdдля читання збереженого виведення і відновити стандартний висновок буде тільки терміналом для показу підказки.

Але це ще потребує певної роботи. Наприклад, оскільки stdout більше не є терміналом, такі програми, як vimі lessне працюють належним чином.

У цих питаннях є корисна пов'язана інформація:


Так, можливо, 100% автоматичний підхід не спрацює. У execкоді багато викликів, чи команда виконується кілька разів чи мене спіймає спеціальна семантика?
unperson325680

execбез назви програми просто встановлюється перенаправлення.
Мікель

2

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

Так, наприклад, ви можете це зробити, що показує ваш результат як звичайний, але також зберігає його у файл /tmp/it

locate foobar.mmpz | tee /tmp/it

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

cat /tmp/it | grep progo | grep lms

тоді, щоб використовувати його, ви могли просто зробити це:

vi $(!!)


2

Я придумав це напівфабрикат:

alias -g ___='"$(eval "$(fc -ln -1)" | tail -n 1)"'

Це дозволяє писати ___в будь-якій точці командного рядка. Попередня команда буде повторно виконана, а ___заміна буде замінена останнім рядком її виводу. Приклад використання:

$ touch foo bar baz
$ ls -1
bar
baz
foo
$ vim ___

Остання команда буде розширена на vim foo.

У цього є кілька гострих країв!

  • Якщо ви включите ___до команди, але попередня команда також включила a ___, оболонка деякий час буде зависати в якомусь дивному стані. Ви можете вийти з цього стану негайно за допомогою Ctrl- C.

  • Ви також не можете натиснути, Tabщоб розширити ___, як ви можете, !$та інші конструкції.

  • Деякі команди відображатимуть різний вихід, коли вони працюють "нормально" та коли вони приєднані до труби. (Порівняйте вихід lsі ls | cat.) Якщо команда, запущена ___одним із них, ви можете виконати іншу команду, ніж очікували.

  • І звичайно, якщо ви хотіли зробити щось із вихідним рядком, окрім останнього, це вам не допоможе.

Я вибрав назву, ___тому що це те, чого я ніколи не хотів включати як слово в командний рядок, навіть не як аргумент. Ви можете вибрати інше ім’я, але будьте обережні, щоб не вибрати щось, що може бути розширено для вас ненавмисно.

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