Примечание: вы можете выполнить эту операцию полностью внутри vim, не используя ее findили xargsвообще не используя . Откройте vim без аргументов, затем запустите, :args **/*.txt<CR>чтобы установить аргументы vim из редактора.
Тревор Пауэлл
3
@TrevorPowell: За все эти годы vim не переставал меня удивлять.
Когда вы вызываете программу через 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>s. В Linux, работает stty saneсделают терминал пригодного для использования (хотя это будет потеряло такие варианты , как iutf8, возможно , вызывая незначительные раздражали позже).
Вы можете считать это ошибкой в Vim, поскольку он может открываться /dev/ttyдля управления терминалом, но не открывается. (В какой-то момент во время запуска Vim дублирует свой stderr на stdin, что позволяет ему читать ваши входные команды - с открытого для записи fd - но даже это делается недостаточно рано.)
@rahmanisback: другие ответы, плюс комментарий Тревора, в первую очередь предоставили способы избежать поломки терминала. Я принял ответ Гравити, потому что мой вопрос был «почему», а не «как избежать» - это покрыто другим вопросом, который фактически породил этот.
DevSolar
@DevSolar Понятно, но подумайте о разочарованных людях, таких как я, которые просто гуглят, как избавиться от такого поведения, а не - к сожалению, - имеют достаточно времени прямо сейчас, чтобы изучить «почему», что, тем не менее, очень интересно.
doc_id
4
когда мой терминал ломается, вот так, я использую resetвместо, stty saneи после этого он работает нормально.
Capi Etheriel
137
(Следуя объяснениям Гравити, это xargsуказывает stdinна /dev/null.)
Решение этой проблемы заключается в добавлении -oпараметра к xargs. От man xargs:
-o
Откройте /dev/ttyкоманду stdin, как в дочернем процессе, перед выполнением команды. Это полезно, если вы хотите xargsзапустить интерактивное приложение.
Таким образом, следующая строка кода должна работать для вас:
находить . имя "* .txt" | xargs -o vim
GNU xargs поддерживает это расширение с некоторых выпусков в 2017 году (с длинным именем опции --open-tty).
Для более старых или других версий xargs вы можете явно передать их /dev/ttyдля решения проблемы:
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 one))
Главный вопрос был «почему», а не «как этого избежать», и он был удовлетворен два с половиной года назад.
DevSolar
5
Это, конечно, не работает должным образом, когда имена файлов содержат пробелы или другие специальные символы, а также представляет угрозу безопасности.
Деджей Клейтон,
1
Мой любимый ответ, потому что он работает для каждой команды, в которой перечислены файлы, а не только для поиска или подстановочных знаков. Это требует небольшого доверия, как указывает Дежей.
Трэвис Уилсон
1
Это не будет работать во многих случаях использования, для которых предназначен xargs: например, когда число путей очень велико (cc @TravisWilson)
Good Person
21
Это должно работать нормально, если вы используете опцию -exec при поиске, а не в xargs.
Хм ... хитрость есть +(вместо "обычного" \;), чтобы получить все найденные файлы в одной сессии Vim - опция, о которой я постоянно забываю. Вы правы, конечно, и +1 за это. Я пользуюсь vim $(find ...)просто по привычке. Тем не менее, я на самом деле спрашивал, почему операция по трубе портит терминал, и Гравити прибил это к его объяснению.
DevSolar
2
Это лучший ответ, и он работает как на BSD / OSX / GNU / Linux.
Кевинарпе
1
Кроме того, find - не единственный способ получить список файлов, которые должны редактироваться одновременно vim. Я могу использовать grep, чтобы найти все файлы с шаблоном и попытаться редактировать их одновременно.
Чандраншу
8
Вместо этого используйте GNU Parallel:
find . -name "*.txt" | parallel -j1 --tty vim
Или, если вы хотите открыть все файлы за один раз:
find . -name "*.txt" | parallel -Xj1 --tty vim
Он даже правильно работает с именами файлов, такими как:
Доступен не повсеместно. Большую часть дня я работаю на серверах, где я не могу устанавливать дополнительные инструменты. Но все равно спасибо за подсказку.
DevSolar
Если вы можете сделать 'cat> file; chmod + x file ', тогда вы можете установить GNU Parallel: это просто скрипт на perl. Если вам нужны справочные страницы и тому подобное, вы можете установить их в своем домашнем каталоге: ./configure --prefix = $ HOME && make && make install
Ole Tange
2
Хорошо, попробовал это - но параллель не открывает все файлы, она открывает их последовательно . Это также довольно глоток для простой операции. vim $(find . -name "*.txt")проще, и вы получаете все файлы, открытые одновременно.
DevSolar
5
@DevSolar: Несколько не связано, но у обоих find | xargsи $(find)будут большие проблемы с пробелами в именах файлов.
Гравитация
2
@ Grawity Правильно, но не существует простого способа обойти это (что я знаю). Вы должны были бы начать возиться с $IFS, -print0и прочее, а затем покинули сферу решения командной строки , один выстрел , и достиг точки , где вы должны придумать сценарий ... есть причина , почему пробелы в именах файлов не рекомендуется ,
DevSolar
0
может быть, не самый лучший, но вот сценарий, который я использую (по имени vim-open):
Что касается нескольких других ответов, обратите внимание, что реальный вопрос был «почему», а не «как этого избежать». (Для которого я все еще указал бы на комментарий Тревора по моему вопросу как наиболее надежный способ, который не требует сценариев, псевдонимов или чего-либо еще.)
find
илиxargs
вообще не используя . Откройте vim без аргументов, затем запустите,:args **/*.txt<CR>
чтобы установить аргументы vim из редактора.grep -l .. | xargs vim
выдает предупреждение, почему? в Unix SEОтветы:
Когда вы вызываете программу через
xargs
, стандартный ввод программы указывает на/dev/null
. (Так как xargs не знает оригинальный stdin, он делает следующую лучшую вещь.)Vim ожидает, что его stdin будет таким же, как его управляющий терминал, и напрямую выполняет различные связанные с терминалом ioctl на stdin. Когда все сделано
/dev/null
(или любой не-tty файловый дескриптор), эти ioctl не имеют смысла и возвращают ENOTTY, что молча игнорируется.Я предполагаю более конкретную причину: при запуске Vim считывает и запоминает старые настройки терминала и восстанавливает их при выходе. В нашей ситуации, когда «старые настройки» запрашиваются для не-tty fd (дескриптор файла), Vim получает все значения пустыми и все опции отключены и небрежно устанавливает их для вашего терминала.
Вы можете увидеть это, запустив
vim < /dev/null
, выйдя из него, затем запустивstty
, что выдаст много<undef>
s. В Linux, работаетstty sane
сделают терминал пригодного для использования (хотя это будет потеряло такие варианты , какiutf8
, возможно , вызывая незначительные раздражали позже).Вы можете считать это ошибкой в Vim, поскольку он может открываться
/dev/tty
для управления терминалом, но не открывается. (В какой-то момент во время запуска Vim дублирует свой stderr на stdin, что позволяет ему читать ваши входные команды - с открытого для записи fd - но даже это делается недостаточно рано.)источник
stty sane
reset
вместо,stty sane
и после этого он работает нормально.(Следуя объяснениям Гравити, это
xargs
указываетstdin
на/dev/null
.)Решение этой проблемы заключается в добавлении
-o
параметра кxargs
. Отman xargs
:Таким образом, следующая строка кода должна работать для вас:
GNU xargs поддерживает это расширение с некоторых выпусков в 2017 году (с длинным именем опции
--open-tty
).Для более старых или других версий xargs вы можете явно передать их
/dev/tty
для решения проблемы:(Здесь
ignoreme
нужно взять $ 0, так что $ @ - это все аргументы из xargs.)источник
$@
похоже, неправильно переводит аргументы.function vimin () { xargs sh -c 'vim "$@" < /dev/tty' vim; }
ignoreme
строка, см. В vi.stackexchange.com/a/17813The -J, -o, -P and -R options are non-standard FreeBSD extensions which may not be available on other operating systems.
(Это не было доступно на macOS для меня, потому что я установил xargs из homebrew (GNU one))Самый простой способ:
источник
Это должно работать нормально, если вы используете опцию -exec при поиске, а не в xargs.
источник
+
(вместо "обычного"\;
), чтобы получить все найденные файлы в одной сессии Vim - опция, о которой я постоянно забываю. Вы правы, конечно, и +1 за это. Я пользуюсьvim $(find ...)
просто по привычке. Тем не менее, я на самом деле спрашивал, почему операция по трубе портит терминал, и Гравити прибил это к его объяснению.Вместо этого используйте GNU Parallel:
Или, если вы хотите открыть все файлы за один раз:
Он даже правильно работает с именами файлов, такими как:
Посмотрите вступительное видео, чтобы узнать больше: http://www.youtube.com/watch?v=OpaiGYxkSuQ
источник
vim $(find . -name "*.txt")
проще, и вы получаете все файлы, открытые одновременно.find | xargs
и$(find)
будут большие проблемы с пробелами в именах файлов.$IFS
,-print0
и прочее, а затем покинули сферу решения командной строки , один выстрел , и достиг точки , где вы должны придумать сценарий ... есть причина , почему пробелы в именах файлов не рекомендуется ,может быть, не самый лучший, но вот сценарий, который я использую (по имени
vim-open
):будет работать с
vim-open a b c
и,ls | vim-open
например,источник