xargs та vi - "Введення не з терміналу"


14

У мене в системі близько 10 php.iniфайлів, які розташовані повсюдно, і я хотів швидко переглядати їх. Я спробував цю команду:

locate php.ini | xargs vi

Але viпопереджає мене, Input is not from a terminalі тоді консоль починає ставати дуже дивно - після цього мені потрібно натиснути, :q!щоб вийти, viа потім відключитись від сеансу ssh і знову підключитися, щоб консоль знову нормально поводилася.

Я думаю, що я начебто розумію, що тут відбувається - в основному команда не закінчена, коли viзапущена, тому команда, можливо, ще не закінчена і viне думає, що термінал знаходиться в нормальному режимі.

Я поняття не маю, як це виправити. Я дуже швидко шукав Google, а також unix.stackexchange.com.



Як бічна примітка, ви можете запустити resetдля скидання свого терміналу, коли він перекрутився (вам не потрібно відключатися від ssh сесії).
— wisbucky

Відповіді:


12
vi $(locate php.ini)

Примітка. У цьому виникнуть проблеми, якщо у ваших шляхах файлів є пробіли, але він функціонально еквівалентний вашій команді.
Ця наступна версія буде правильно обробляти пробіли, але є трохи складнішою (нові рядки у назвах файлів все одно будуть її порушувати)

(IFS=$'\n'; vi $(locate php.ini))


Пояснення:

Що відбувається, це те, що програми успадковують дескриптори файлів від процесу, який їх породив. xargsмає свій STDIN, підключений до STDOUT locate, тому viне має поняття, у чому насправді є оригінальний STDIN.


2
xargs - це чудово, один з моїх улюблених інструментів - він просто не підходить для використання з програмами, які використовують stdin ні для чого, крім каналу даних. мені подобається ваша відповідь та ваші пояснення, окрім цього, тому +1 все одно :)
— cas

@CraigSanders Мені це не подобається, тому що це занадто легко зловживати (використовувати неправильно) і врешті-решт зламати. Я ніколи не наштовхувався на те, що мені абсолютно доводилося використовувати xargsдля того, що не можна було б зробити безпосередньо з оболонкою (або find). Однак я можу придумати випадки, коли це було б найкращим рішенням. Отже, доки ви розумієте, що xargsробить, як вона розбиває аргументи, як вона запускає програму тощо і правильно використовує її, я б сказав: продовжуйте: - P
— Патрік,

його не можна перемогти за такі речі, як ... | awk '{print $3}' | xargs | sed -e 's/ /+/g' | bc(для додавання всіх значень поля 3). або з sed -e 's/ /|/g'побудувати повторне вираження. і так, як і будь-який інструмент, вам потрібно знати, як ним користуватися та які його обмеження та застереження.
— cas

vi $(...)Підхід також має проблеми з груповими символами в інших , ніж снарядах zsh.
— Стефан Шазелас

Також зауважте, що з xargsпідходом до проблеми пробілу, проблема імен файлів з одиничними цитатами, подвійними цитатами та зворотними косими рисами також є проблемою.
— Стефан Шазелас

10

Це питання раніше задавали на форумі Super User .

Цитуючи відповідь на @ grawity на це питання:

Коли ви викликаєте програму через xargs, stdin (стандартний вхід) програми вказує на / dev / null. (Оскільки xargs не знає оригінал stdin, він робить наступне найкраще.)

Vim очікує, що його stdin буде таким же, як і керуючий термінал, і виконує різні пов'язані з терміналом ioctl на stdin безпосередньо. Після завершення роботи / dev / null (або будь-якого нетипового дескриптора файлів) ці йоктли безглузді і повертають ENOTTY, що мовчки ігнорується.

Про це йдеться у сторінках керівництва для xarg. З OSX / BSD:

-o Повторно відкрити stdin як / dev / tty в дочірньому процесі перед виконанням команди. Це корисно, якщо ви хочете, щоб xargs запустив інтерактивну програму.

Отже, на OSX ви можете використовувати таку команду:

find . -name "php.ini" | xargs -o vim

Хоча прямого перемикача у версії GNU немає, ця команда буде працювати. (Обов’язково додайте dummyрядок, інакше перший файл буде випав.)

find . -name "php.ini" | xargs bash -c '</dev/tty vim "$@"' dummy

Вищезазначені рішення є люб’язністю Хайме Макгуйгана на SuperUser . Додаючи їх сюди для будь-яких майбутніх відвідувачів, які шукають на сайті цю помилку


3
+1 спасибі за підказку -o. Я використовував xargs роками і ніколи не помічав, що .... просто перевірив сторінку man у моїй системі, це тому, що це не xargs GNU. Сторінка man надає xargs sh -c 'emacs "$@" < /dev/tty' emacsбільш гнучку та портативну альтернативу (хоча GNU начебто смішно віддавати перевагу мобільності до функцій :).
— cas

2

За допомогою GNU findutilsта оболонки з підтримкою заміни процесів (ksh, zsh, bash) ви можете:

xargs -r0a <(locate -0 php.ini) vi

Ідея полягає у тому, щоб передати список файлів через, -a filenameа не stdin. Використання -0гарантує, що воно працює незалежно від того, які символи чи символи можуть містити імена файлів.

З zsh, ви можете зробити:

vi ${(0)"$(locate -0 php.ini)"}

(де 0прапор розширення параметра для розділення на NUL).

Однак зауважте, що всупереч xargs -rцьому все-таки працює viбез аргументів, якщо не знайдено жодного файлу.


0

Редагувати кілька php.ini в одному редакторі?

Спробуйте: vim -o $(locate php.ini)


0

Ця помилка трапляється, коли викликається vim, і він підключений до виходу попереднього конвеєра, а не до терміналу і отримує різні неочікувані введення (наприклад, NUL). Те саме відбувається під час запуску:, vim < /dev/nullтому resetкоманда в цьому випадку допомагає. Це добре пояснюється високою популярністю у суперпользователя .

В Unix / OSX ви можете використовувати xargsз -oпараметром, як:

locate php.ini | xargs -o vim

-oПовторно відкрийте stdin як / dev / tty в дочірньому процесі перед виконанням команди. Це корисно, якщо ви хочете, щоб xargs запустив інтерактивну програму.

У Linux спробуйте наступне вирішення:

locate php.ini | xargs -J% sh -c 'vim < /dev/tty $@'

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

locate php.ini | parallel -X --tty vi

Примітка: parallelна Unix / OSX не працюватиме, оскільки він має різні параметри і не підтримує tty.

У багатьох інших популярних командах передбачено також розподіл псевдо-tty (як -tу ssh), тому зверніться за допомогою.

Крім того, використовуйте findдля передавання імен файлів для редагування, тому не потрібно xargs, просто використовуйте -exec, наприклад:

find /etc -name php.ini -exec vim {} +

0

@ Злом Патріка IFSпотрібен лише для німих снарядів, як bashі zsh. fish розділяє рядок на нові рядки за замовчуванням.

$ vim (locate php.ini)

І допоможе нам Бог, якщо один із нас насправді має файл із новим рядком на його ім’я. Після 17 років використання Linux я не бачив жодного разу. Я б тільки переймався підтримкою назви файлів новими рядками в них для сценаріїв, які повинні працювати незалежно від будь-якого, але такі сценарії, ймовірно, не працюють vim інтерактивно.


zshділиться на SPC, TAB, NL та NUL за замовчуванням. Те, що він не робить в порівнянні з цим, bash- виконувати глобул за результатом, тому символи підстановки у назвах файлів не є проблемою. У zsh, ви б зробили IFS=$'\0'; vi $(locate -0 php.ini)або, як я показав у своїй відповіді, vi ${(0)"$(locate -0 php.ini)"}для оператора чіткого розщеплення. Також зверніть увагу на tcsh'svi "`locate php.ini`"
— Stéphane Chazelas

ах, лайно. Гаразд це працює: $ f='not there'<ret>$ ls $f<ret>але це не так: ls echo not there. Гаразд виглядає, що мені потрібно трохи оновити це.
— загадковийфізик

Так, zsh не робить правильно, коли ти робиш ls "$(echo test; echo other test)". Тільки риба робить правильно.
— загадковийфізик

Якщо припустити, що ви мали на увазі те саме без лапок, це не "правильно", це розділення на лінії, це просто інший вибір. zsh розбивається на слова за замовчуванням (як і всі інші оболонки), і їх можна сказати розділити на рядки або на NUL, $IFSабо через явні оператори ( fі 0прапорці розширення параметрів). Для довільних імен файлів розділення за словом або розділення за рядком однаково неправильно , вам потрібно розділити на NUL або проаналізувати деяке кодування, що fishне може зробити. В zsh, це IFS=$'\0'; ls -ld -- $(printf '%s\0' "$file1" "$file2")абоls -ld -- ${(0)"$(printf '%s\0' "$file1" "$file2")"}
— Стефан Шазелас

Мех. Розщеплення на нові лінії досить добре. Як йдеться у відповіді, нові рядки у назви файлів зустрічаються вкрай рідко. Я буквально ніколи не бачив, щоб це сталося за 17 років. А нові рядки - це набагато зручніші роздільники, ніж нулі.
— загадковийфізик

0

Швидкий спосіб зробити це, якщо ви не можете гарантувати жоден з файлових шляхів містять SPC, TAB, NL, *, ?, [символи (також \і {...}в деяких оболонках) є використання бек-тиків (AKA серйозних акцентів) , щоб виконати команду до інша команда виконується.

Напр

vi `find / -type f -name 'php.ini'`

Команда, що міститься в back-ticks, виконає спочатку. Виведення з міститься команди потім виконується командою, заявленою перед зворотними тиками.

Наприклад, у верхньому рядку find / -type f -name 'php.ini'команда спочатку виконає, надішле висновок, а потім viбуде виконана за результатом split + glob, застосованого до цього виводу.


3
зворотні тики занадто легко плутати за одноцитати. використовувати $(find ...)замість цього.
— cas

1
здогадуючись, це також порушиться на пробіли та / або нові рядки у назвах файлів?
— cwd

Це те, як ви виконуєте команди оболонки в сценаріїх bash. У моїх сценаріях або під час використання в одному вкладиші я ніколи не переривав пробіли чи нові рядки. Однак я ніколи не намагався відкрити кілька файлів за viдопомогою цього методу. Цілком можливо, що він може переламатись на нові рядки чи пробіли, залежно від того, як viчитається та виконується вихід.
— такоту вівторок
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.