128

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

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

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

Input is not from a terminal

и терминал с в значительной степени сломанным поведением впоследствии. Это почему?

5 ответов5

125

Исходя из ответа Гравити , xargs указывает на стандартный stdin /dev/null


От OSX/BSD man xargs

-o      Reopen stdin as /dev/tty in the child process 
        before executing the command.  This is useful 
        if you want xargs to run an interactive application.

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

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

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

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

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

89

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

31

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

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

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

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

8

Вместо этого используйте 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

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .