Может ли zsh получить доступ к выводу последней запущенной программы?

15

Я часто использую 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) для настройки захвата такого типа после каждой строки выполнения.

unperson325680
источник
На самом деле задача оболочки не перенаправить поток пользователю. Приложения записывают свой вывод на оконечное устройство, оболочка в этом не участвует.
Стефан Шазелас
@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не работают должным образом.

В этих вопросах есть полезная информация:

Mikel
источник
Да, возможно, 100% автоматический подход не сработает. В execкоде много вызовов, команда выполняется несколько раз, или я попал в особую семантику?
unperson325680
execбез имени программы просто устанавливает перенаправления.
Микель
2

Вы можете сделать это, просто отправив свои результаты в teeархив, который просто сохранит вывод в файл и отобразит его одновременно.

Так, например, вы можете сделать это, который показывает ваш вывод как обычно, но также сохраняет его в файл /tmp/it

locate foobar.mmpz | tee /tmp/it

затем кошка этого файла и grep его, чтобы выбрать вещи, например,

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.) Если команда, вызванная ___одним из них, может привести к тому, что вы запустите команду, отличную от ожидаемой.

  • И, конечно, если вы хотите что-то сделать с выходной строкой, отличной от последней, это вам не поможет.

Я выбрал имя, ___потому что это то, что я никогда не хотел включать в качестве слова в командной строке, даже в качестве аргумента. Вы можете выбрать другое имя, но позаботьтесь о том, чтобы не выбрать что-то, что может быть случайно расширено для вас.

bdesham
источник