Терминал заблокирован после вызова Vim с Xargs

30

Я иногда пытался вызвать Vim, используя xargsэто:

find . -name '*.java' | xargs vim

… Какие работы:

  1. Когда Vim запускается, я вижу следующее предупреждение:

    Vim: Warning: Input is not from a terminal
    
  2. Редактирование работает - :filesправильно перечисляет все .javaфайлы, как и ожидалось.
  3. Я могу сохранить и выйти.

Однако после выхода из Vim мой терминал пропал:

  • Все, что я печатаю в командной строке, не отражается.
  • Возврат каретки вообще не появляется, а переводы строки появляются только иногда.

Это продолжается до тех пор, пока я не выполню reset(1)команду для повторной инициализации терминала.

Это ошибка Vim, или есть более удовлетворительное объяснение, почему он так взаимодействует с терминалом? Я видел, как это происходит в Vim до версии 7.3 (версия, кажется, не имеет значения) в Linux и различных Unices.

Я знаю один обходной путь, а именно vim $(find . -name '*.java'). Другие обходные пути будут приветствоваться, хотя это не мой главный вопрос.

200_success
источник
Я вроде бы хотел, чтобы вы заполнили этот сайт как можно большим количеством вопросов, но… на этот вопрос за многие годы на SO / SU и в других местах отвечали десятки раз: xargsиспользуется пустышка stdin, которую Vim не может использовать, и перерывы все потом.
romainl
1
@romainl Я не активен на этих сайтах, и я никогда не видел, чтобы их там спрашивали - честно.
200_success
Связано: superuser.com/questions/336016/… - возможно, кто-то может сгущать топ-ответы до отличного ответа.
Муру
2
Отличный вопрос - это случалось со мной много раз. Кроме того, я не использую другие сайты SO - если это связано с vim, я бы хотел найти его здесь.
Craigp

Ответы:

21

Это происходит, когда vim вызывается и подключается к выходу предыдущего конвейера, а не к терминалу и получает другой неожиданный ввод (например, NUL). То же самое происходит при запуске:, vim < /dev/nullтак что resetкоманда в этом случае помогает. Это хорошо объясняется благодарностью суперпользователя .

Если вы используете findдля передачи имен файлов для редактирования, вам не нужно xargs, просто используйте -exec, например:

find . -name '*.java' -exec vim {} +

Если вы хотите использовать xargs, в Unix / macOS вы должны использовать -oпараметр, например:

find . -name '*.java' | xargs -o vim

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

или:

find . -name "*.java" -type f -print0 | xargs -o -0 vim

Примечание. Использование -print0/ -0будет поддерживать имена файлов с пробелами.


find+ BSDxargs

В Unix / macOS вы можете попробовать следующий обходной путь:

find . -name '*.java' | xargs -J% sh -c 'vim < /dev/tty $@'

Вы также можете использовать подстановки команд синтаксиса, например:

vim $(find . -name '*.java')
vim `find . -name '*.java`

В качестве альтернативы используйте GNU parallelвместо xargsпринудительного распределения tty, например:

find . -name '*.java' | parallel -X --tty vi

Примечание: parallelв Unix / OSX не будет работать, так как он имеет разные параметры и не поддерживает tty.

Многие другие популярные команды также предоставляют псевдо-tty распределение (например, -tв ssh), поэтому обратитесь за помощью.

Другое предложение будет использовать:

Связанный:

kenorb
источник
Мой GNU xargsне имеет -J. Похоже, это опция MacOS (BSD), которой нет в версии GNU.
Приостановлено до дальнейшего уведомления.
12

Обходной путь: используйте буфер в качестве навигатора файловой системы

Используйте vim -команду, чтобы прочитать список путей из стандартного ввода. Вима :help --объясняет это: 1

Начните редактировать новый буфер, который заполнен текстом, который читается из стандартного ввода. Команды, которые обычно читаются из stdin, теперь будут читаться из stderr. Пример:

find . -name "*.c" -print | vim -

Затем вы можете использовать gfили Ctrl-wCtrl-fдля перехода к файлу в том же буфере или в новом разделенном окне соответственно.

Обходной путь: список аргументов

Другой отличный способ - использовать список аргументов Vim. Команда :argadd **/*.javaзаполнит список аргументов Vim всеми java-файлами, найденными внутри текущего каталога рекурсивно. Затем вы можете использовать :nextи :prevдля перемещения между файлами.


1 Первый -говорит Vim, что вы хотите найти справку для параметра командной строки, второй - это флаг командной строки, который вы ищете. Читайте :help help-contextбольше трюков, как это.

Акшай
источник
8

Кроме того reset, вы можете попробовать:

stty sane

что также должно сделать ваш терминал снова пригодным для использования.

Смотрите здесь для объяснений. И почему-то это может считаться плохим поведением, по крайней мере, у Neovim в настоящее время нет этой проблемы.

ryenus
источник
Это не совсем отвечает на вопрос, но это помогло мне, так что +1.
Восстановить Монику iamnotmaynard
Это ДАЕТ ответ на мой вопрос;) Хотя после прочтения ОП, я думаю, resetчто тоже должен.
Шридхар Сарнобат
1

Причина заключается в том, что xargsнаборы stdinдля /dev/null, в то время как vimпотребности stdinбыть /dev/tty.


xargsРешение BSD (например, Mac):

echo -e 'file1\nfile2' | xargs -o vim

-oустанавливает stdinдочерний процесс xarg ( vimв данном случае) в dev/tty.


xargsРешение GNU (например, Linux):

GNU xargsне имеет -oвозможности. Вместо этого вам придется использовать более сложный обходной путь. (Примечание: очень важно иметь конечную zeroстроку, не забывайте об этом.)

echo -e 'file1\nfile2' | xargs bash -c '</dev/tty vim "$@"' zero

Вы также можете сделать его псевдонимом:

alias vimin='xargs bash -c '\''</dev/tty vim "$@"'\'' zero'
echo -e 'file1\nfile2' | vimin

Подробное объяснение решения GNU xargs

Давайте разберем это шаг за шагом:

echo -e 'file1\nfile2' | xargs bash -c '</dev/tty vim "$@"' zero

1. xargsпросто добавляет stdinк концу строки, поэтому xargsбудет выполнять это:

    bash -c '</dev/tty vim "$@"' zero file1 file2

2. Формат для bash -cесть bash -c 'COMMAND_STRING' $0 $1 $2 etc.

"$@"расширяется до позиционных параметров "$1", "$2"и т. д. Он не содержит «$ 0», потому что это специальный параметр для имени скрипта, а не позиционный параметр. Вот почему нам нужно добавить фиктивную строку zero(это может быть любая строка), чтобы занять место $0. В противном случае вы потеряете первый файл.

Итак, после расширения "$@"вы получите:

    bash -c '</dev/tty vim file1 file2' 

3. bash -cвыполнит COMMAND_STRING:

    </dev/tty vim file1 file2 

</dev/ttyустанавливает stdinдля /dev/ttyтак , что vimможет работать в интерактивном режиме.

wisbucky
источник
+1 Казалось бы, дубликат ответа с наибольшим количеством голосов, но это не так. $0Заполнитель (здесь «ноль») является ключевым.
Приостановлено до дальнейшего уведомления.
0

Я обнаружил, что все эти ответы отсутствуют. После преодоления проблемы с xargs, самый простой способ - для меня! - выполнить поиск и открытие в универсальном окне, заключается в следующем:

vim -O `grep -lir mySearchTerm *`

У меня нет проблемы , используя findс xargsи grep, но я считаю , что синтаксис раздражает. И как ежедневный кодер, который использует vimи терминал в качестве своей IDE, и постоянно ищет в файлах свойства, это то, что я использовал.

Есть время и место для find . -path ./node_modules -prune -o -name '*.js' | xargs grep -i mySearchTermоткрытия файлов с помощью vim и других методов. Для меня это редко.

Надеюсь, это кому-нибудь поможет.

Gavin
источник
2
FWIW рекомендуется не использовать устаревшую нотацию с помощью обратных кавычек и использовать $(...)вместо нее . Это не так важно в вашей командной строке, как в сценарии, но я думаю, что все же лучше использовать это в своих ответах :) (ссылки здесь и здесь )
statox