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.