Вопрос: Вызов vi через find | xargs ломает мой терминал. Зачем?


При вызове vim через find | xargs, как это:

find . -name "*.txt" | xargs vim

вы получаете предупреждение о

Input is not from a terminal

и терминал с довольно сильно сломанным поведением впоследствии. Почему это?


117
2017-09-15 16:26


Источник


Замечание: вы можете выполнить эту операцию полностью в пределах vim, не используя find или xargs вообще. Откройте vim без аргументов, затем запустите :args **/*.txt<CR>установить аргументы vim из редактора. - Trevor Powell
@TrevorPowell: Во все эти годы, vim никогда не переставал меня удивлять. - DevSolar
Связанный: grep -l .. | xargs vim генерирует предупреждение, почему? в UNIX SE - kenorb
Связанный: Терминал укусил после вызова Vim с xargs в Vim SE. - kenorb
Отчет об ошибке GitHub: vim не обрабатывает STDIN, установленный в / dev / null, - kenorb


Ответы:


Когда вы вызываете программу через xargs, stdin программы (стандартный ввод) указывает на /dev/null, (Так как xargs не знает оригинал stdin, он делает следующее лучшее.)

$ true | xargs Filan -s
    0 chrdev / dev / null
    1 tty / dev / pts / 1
    2 tty / dev / pts / 1

$ true | xargs ls -l / dev / fd /

Vim ожидает, что его stdin будет таким же, как и его управляющий терминал, и выполняет различные связанные с терминалом IOCTLпрямо на stdin. Когда это делается /dev/null (или любой дескриптор файла не-tty), эти ioctls бессмысленны и возвращают ENOTTY, который бесшумно игнорируется.

  • Я предполагаю более конкретную причину: при запуске Vim читает и запоминает старые настройки терминала и восстанавливает их при выходе. В нашей ситуации, когда «старые настройки» запрашиваются для не-tty fd (файловый дескриптор), Vim получает все значения пустым и все параметры отключены и небрежно устанавливает их на ваш терминал.

    Вы можете увидеть это, выполнив vim < /dev/null, выйдя из него, затем запустив stty, который выведет много <undef>s. В Linux работает stty sane снова сделает терминал пригодным для использования (хотя он будем потеряли такие возможности, как iutf8, возможно, вызвав незначительные досады позже).

Вы можете считать это ошибкой в ​​Vim, так как это Можно открытый /dev/tty для управления терминалом, но это не так. (В какой-то момент во время запуска Vim дублирует свой stderr на stdin, что позволяет ему читать ваши команды ввода - из fd, открытого для записи, - но даже это не сделано достаточно рано.)


80
2017-09-15 16:41



... с расцветом. Большое спасибо! - DevSolar
+1, а для TL, DR люди просто запускают stty sane - rahmanisback
@rahmanisback: Другие ответы, плюс комментарий Тревора, все это обеспечили способы избежать разрыва терминала в первую очередь. Я принял ответ грамотности, потому что мой вопрос был «почему», а не «как избежать» - это охвачено Другой вопрос что на самом деле порождал вот этот. - DevSolar
@DevSolar Понял, но подумайте о таких разочарованных людях, как я, которые просто google, как избавиться от этого поведения, а не к сожалению, - достаточно времени, чтобы изучить «почему», что очень интересно. - rahmanisback
когда мой терминал ломается, вот так я использую reset вместо stty sane и после этого он отлично работает. - Capi Etheriel


Следуя от ответа грамотности, xargs точки stdin в /dev/null


Из OSX / BSD man xargs

-o Повторно открыть stdin как / dev / tty в дочернем процессе
        перед выполнением команды. Это полезно
        если вы хотите, чтобы xargs запускали интерактивное приложение.

Таким образом, следующая строка кода должна работать на вас:

найти . -name "* .txt" | xargs -o vim

Для GNU man xargs флаг отсутствует, но мы можем явно передать / dev / tty для решения проблемы:

найти . -name "* .txt" | xargs bash -c '</ dev / tty vim "$ @" "ignoreme

ignoreme должен занять $ 0, так что $ @ - все аргументы из xargs


116
2018-05-23 12:15



Как бы вы создали псевдоним bash из этого? $@ кажется, не правильно переводят аргументы. - zanegray
@zanegray - вы не можете создать псевдоним, но вы можете сделать его функцией. Пытаться: function vimin () { xargs sh -c 'vim "$@" < /dev/tty' vim; } - Christopher


Самый простой способ:

vim $(find . -name "*foo*")

27
2018-03-08 02:13



Главный вопрос был «почему», а не «как его избежать», и на него ответили на удовлетворение два с половиной года назад. - DevSolar
Это, конечно, не работает должным образом, когда имена файлов содержат пробелы или другие специальные символы, а также представляют угрозу безопасности. - Dejay Clayton
Мой любимый ответ, потому что он работает для каждой команды, в которой перечислены файлы, а не только «найти» или «подстановочные знаки». Это требует немного доверия, как указывает Деджей. - Travis Wilson
Это не будет работать во многих случаях использования. Xargs предназначен для: например, когда количество путей очень велико (cc @TravisWilson) - Good Person


Он должен работать нормально, если вы используете опцию -exec для поиска, а не для подключения к xargs. Например

$ find . -type f -name filename.txt -exec vi {} +


18
2018-03-10 14:31



Да ... трюк есть + (вместо "обычного" \;), чтобы получить все найденные файлы в один Vim сессия - вариант I держать забывая о. Вы правы, конечно, и +1 для этого. я использую vim $(find ...) просто по привычке. Однако я действительно просил Зачем работа труб зажимает терминал, и грациозность прибила его своим объяснением. - DevSolar
Это лучший ответ, и он работает как на BSD / OSX / GNU / Linux. - kevinarpe
Кроме того, поиск - это не единственный способ получить список файлов, которые нужно редактировать одновременно vim. Я могу использовать grep, чтобы найти все файлы с шаблоном и попробовать их редактировать одновременно. - Chandranshu


Вместо этого используйте GNU Parallel:

find . -name "*.txt" | parallel -j1 --tty vim

Или если вы хотите открыть все файлы за один раз:

find . -name "*.txt" | parallel -Xj1 --tty vim

Он даже правильно обрабатывает имена файлов, такие как:

My brother's 12" records.txt

Смотрите видеоролик, чтобы узнать больше: http://www.youtube.com/watch?v=OpaiGYxkSuQ


8
2017-09-16 17:45



Не повсеместно доступно. Большую часть дня я работаю на серверах, где я не могу устанавливать дополнительные инструменты. Но спасибо за подсказку в любом случае. - DevSolar
Если у вас есть свобода делать файл «cat»; chmod + x file ", тогда вы можете установить GNU Parallel: это просто скрипт perl. Если вам нужны справочные страницы и такие, вы можете установить его под своим homedir: ./configure --prefix = $ HOME && make && make install - Ole Tange
Хорошо, попробовал, но параллельно не открыть все файлы, открыть их в последовательности, Это также довольно просто для простой операции. vim $(find . -name "*.txt") проще, и вы сразу открываете все файлы. - DevSolar
@DevSolar: несколько несвязанный, но оба find | xargs а также $(find) будут иметь большие проблемы с пробелами в именах файлов. - grawity
@grawity Правильно, но вокруг него нет простого способа (что я знаю). Вы должны начать возиться с $IFS, -print0 и т. д., а затем вы покинули сферу решения командной строки с одним выстрелом и достигли точки, где вы должны придумать скрипт ... есть причина, почему пробелы в именах файлов обескуражены. - DevSolar