Вызов vi через find | xargs ломает мой терминал. Почему?

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

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

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

Input is not from a terminal

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


Этот вопрос явно касался Зачем, а не о как избежать. Об этом спросили и ответили в другом месте.

6 ответов
6

Когда вы вызываете программу через xargs, стандартный ввод программы указывает на /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 ожидает, что его стандартный ввод будет таким же, как и его управляющий терминал, и выполняет различные связанные с терминалом ioctlпрямо на stdin. Когда закончите /dev/null (или любой другой файловый дескриптор, отличный от tty), эти ioctl не имеют смысла и возвращают ENOTTY, который незаметно игнорируется.

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

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

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

  • 21 год

    +1, а за TL; DR люди просто бегают stty sane

    — doc_id
    11 фев ’15 в 10:33

  • @rahmanisback: Все остальные ответы, плюс комментарий Тревора, в первую очередь, предлагали способы избежать поломки терминала. Я принял ответ Grawity, потому что мой вопрос был «почему», а не «как избежать» — это покрывается другим вопросом, который на самом деле порожденный Вот этот.

    — DevSolar
    11 фев ’15 в 10:41

  • @DevSolar Понятно, но подумайте о разочарованных людях вроде меня, которые просто гуглили, как избавиться от такого поведения, хотя, к сожалению, не имели достаточно времени прямо сейчас, чтобы изучить «почему», что, тем не менее, очень интересно.

    — doc_id
    11 фев ’15 в 18:34

  • 5

    когда мой терминал ломается, я использую reset вместо stty sane и после этого он работает нормально.

    — Капи Этериэль
    21 апр.

(Следуя объяснению Гравити, xargs точки stdin к /dev/null.)

В решение для этой проблемы нужно добавить -o параметр для xargs. Из man xargs:

-o

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

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

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

GNU xargs поддерживает это расширение с момента выпуска в 2017 г. (с длинным именем параметра --open-tty).

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

find . -name "*.txt" | xargs bash -c '</dev/tty vim "$@"' ignoreme

ignoreme должен принимать $ 0, так что $ @ — это все аргументы из xargs.)

  • 2

    Как бы вы создали из этого псевдоним bash? $@ похоже, неправильно переводит аргументы.

    — занегрей
    31 авг.

  • 2

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

    — Кристофер
    12 фев ’17 в 18:02

  • Для подробного объяснения того, как работает решение GNU xargs, и зачем вам нужен фиктивный ignoreme строка, см. vi.stackexchange.com/a/17813

    — Висбаки
    2 ноя ’18 в 20:42

  • @zanegray, можно под псевдонимом сделать. Цитаты хитрые. Смотрите решение на vi.stackexchange.com/a/17813

    — Висбаки
    2 ноя ’18 в 20:46

  • The -J, -o, -P and -R options are non-standard FreeBSD extensions which may not be available on other operating systems. (Для меня он был недоступен в macOS, потому что я установил xargs из homebrew (GNU))

    — localhostdotdev
    7 мая ’19 в 16:56

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

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

  • 5

    Главный вопрос был «почему», а не «как этого избежать», и два с половиной года назад на него ответили удовлетворительно.

    — DevSolar
    8 марта ’14 в 12: 352014-03-08 12:35

  • 5

    Это, конечно, не работает должным образом, когда имена файлов содержат пробелы или другие специальные символы, а также представляет собой угрозу безопасности.

    — Деджей Клейтон
    17 июня ’15 в 16: 142015-06-17 16:14

  • 1

    Мой любимый ответ, потому что он работает для каждой команды, которая перечисляет файлы, а не только для поиска или подстановочных знаков. Как отмечает Деджей, для этого требуется немного доверия.

    — Трэвис Уилсон
    1 сен ’16 в 20:50

  • 1

    Это не будет работать во многих случаях использования xargs, для которых: например, когда количество путей очень велико (cc @TravisWilson)

    — Хороший человек
    30 нояб.

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

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

  • 2

    Ха … уловка в том, + (вместо «обычного» ;), чтобы поместить все найденные файлы в один Сессия Vim — вариант I хранить забывая о. Вы, конечно, правы, и +1 за это. я использую vim $(find ...) просто по привычке. Однако на самом деле я просил Зачем операция с трубкой портит терминал, и Гравити прибил это своим объяснением.

    — DevSolar
    10 марта ’13 в 21: 452013-03-10 18:45

  • 2

    Это лучший ответ, и он работает как в BSD / OSX / GNU / Linux.

    — кевинарпе
    24 фев ’14 в 17:17

  • 1

    Кроме того, find — не единственный способ получить список файлов, которые должны одновременно редактироваться vim. Я могу использовать grep, чтобы найти все файлы с шаблоном и попытаться редактировать их одновременно.

    — Чандраншу
    12 ноя ’14 в 8: 412014-11-12 11:41

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *