При вызове vim
через find | xargs
, как это:
find . -name "*.txt" | xargs vim
вы получаете предупреждение о
Input is not from a terminal
и терминал с довольно сильно нарушенным поведением впоследствии. Почему в том, что?
Этот вопрос явно касался Зачем, а не о как избежать. Об этом спросили и ответили в другом месте.
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, но даже это делается недостаточно рано.)
(Следуя объяснению Гравити, 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:46The -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
+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
когда мой терминал ломается, я использую
reset
вместоstty sane
и после этого он работает нормально.— Капи Этериэль
21 апр.