Як виконати вивід команди в поточній оболонці?


89

Я добре знаю програму source(вона ж .), яка бере вміст із файлу та виконує його в поточній оболонці.

Тепер я перетворюю деякий текст у команди оболонки, а потім запускаю їх наступним чином:

$ ls | sed ... | sh

lsє лише випадковим прикладом, оригінальним текстом може бути що завгодно. sedтеж, лише приклад для перетворення тексту. Цікавий біт sh. Я труба все, що я потрапив, shі він запускає це.

Моя проблема полягає в тому, що це означає запуск нової допоміжної оболонки. Я волів би, щоб команди працювали в моїй поточній оболонці. Як би я міг це зробити source some-file, якби мав команди в текстовому файлі.

Я не хочу створювати тимчасовий файл, бо відчуваю бруд.

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

оновлення

Гаразд, рішення, що використовують backtick, безумовно, працюють, але мені часто доводиться робити це під час перевірки та зміни вихідних даних, тому я б набагато віддав перевагу, якби в підсумку був спосіб спрямувати результат у щось.

сумне оновлення

Ах, /dev/stdinріч виглядала настільки гарною, але, у більш складному випадку, це не спрацювало.

Отже, у мене є таке:

find . -type f -iname '*.doc' | ack -v '\.doc$' | perl -pe 's/^((.*)\.doc)$/git mv -f $1 $2.doc/i' | source /dev/stdin

Що гарантує, що всі .docфайли мають розширення нижньої літери.

І з якими, до речі, можна впоратися xargs, але це окрім суті.

find . -type f -iname '*.doc' | ack -v '\.doc$' | perl -pe 's/^((.*)\.doc)$/$1 $2.doc/i' | xargs -L1 git mv

Отже, коли я запускаю перший, він відразу вийде, нічого не трапиться.


Чи працює ваша складна команда, коли ви спочатку переходите до тимчасового файлу, а потім створюєте його? Якщо ні, то в чому проблема згенерованого результату? Висновок вашої команди не працюватиме, якщо в іменах файлів є пробіли або якщо певні послідовності не захищені належним чином. Я хотів би додати лапки приблизно $ 1 і $ 2.doc як мінімум.
— Калеб Педерсон

Чи є якась вагома причина для необхідності запускати це в оригінальній оболонці? - ці приклади не маніпулюють поточною оболонкою, тому ви нічого не отримуєте, роблячи це. Швидке рішення полягає в тому, що ви перенаправляєте вихідні дані до файлу та отримуєте цей файл
— nos

@kaleb вихід працює нормально. у цьому конкретному випадку, навіть якщо я трубу до sh. імена файлів є безпечними в космосі, але дякуємо за увагу. Змінні середовища @nos git у вихідній оболонці. і знову ж таки, це лише приклади. питання на все життя.
— kch

source / dev / stdin не працював для мене, коли мені потрібні були призначені змінні. geirha на freenode bash вказав мені на mywiki.wooledge.org/BashFAQ/024 і запропонував спробувати джерело заміщення процесу <(команда), яке працювало для мене
— srcerer

Відповіді:


85
$ ls | sed ... | source /dev/stdin

ОНОВЛЕННЯ: Це працює в bash 4.0, а також у tcsh та dash (якщо змінити sourceна .). Очевидно, це було глючить у bash 3.2. З приміток до випуску bash 4.0 :

Виправлена ​​помилка, яка спричиняла `. ' не читати та виконувати команди з нестандартних файлів, таких як пристрої або іменовані канали.


В порядку. Тоді на bash 4.0.
— kch

О, і оскільки ви перераховуєте оболонки, це працює і в zsh.
— kch

1
Я вважав це геніальним, поки не спробував у msys / mingw (де немає папки / dev (або навіть пристроїв)! Я спробував безліч комбінацій eval, $ () та зворотних посилань ``. Я нічого не міг змусити працювати , отже, нарешті я просто перенаправив вихідні дані у тимчасовий файл, отримав тимчасовий файл і видалив його. В основному "sed ...> /tmp/$$.tmp &&. /tmp/$$.tmp && rm / tmp / $$. tmp ". Будь-хто отримав рішення msys / mingw без тимчасових файлів ???
— chriv

10
Просто зауваження: Цей, здається, не обробляє заявки на експорт, як очікувалося. Якщо конвеєрний рядок має оператор експорту, змінна exportet згодом буде недоступна у вашому терміналі, тоді як з eval export також чудово працює.
— Філ

1
Враховуючи, що MacOS X зазвичай має bash 3.2 (можливо, Yosemite має 4.0?), І потреба в обманці lastpipe у моєму випадку використання (експорт усіх змінних у файл шляхом попередньої обробки кожного рядка з sed, щоб додати 'експорт'), я вибрав іти з цим eval "$(sed ...)"підходом - але дякую за опис помилки 3,2!
— Alex Dupuy,

133

evalКоманда існує для цієї мети.

eval "$( ls | sed... )"

Більше з посібника з Bash :

евал

          eval [arguments]

Аргументи об'єднуються в одну команду, яка потім зчитується і виконується, а її статус виходу повертається як статус виходу eval. Якщо аргументів немає або лише порожні аргументи, статус повернення дорівнює нулю.


8
Єдина проблема в тому, що вам може знадобитися вставити; для розділення ваших команд. Я сам використовую цей метод для роботи на AIX, Sun, HP та Linux.
— Tanktalus

Танкталус, дякую за цей коментар, він щойно змусив мій сценарій працювати. На моїй машині eval не відокремлює команди в нових рядках, а використання джерела не працює навіть із крапками з комою. Рішенням є Eval із крапками з комою. Я хотів би дати вам кілька балів.
— Мілан Бабушков,

2
@ Мілан Бабушков: Я поставив його за вас ;-)
— Філ

3
Це чудово. Однак для мого випадку використання мені в кінцевому підсумку довелося ставити лапки навколо мого $ (), приблизно так: eval "$( ssh remote-host 'cat ~/.bash_profile' )"Зверніть увагу, що я справді використовую bash 3.2.
— Захарі Мюррей,

3
Це працює для мене там, де прийнята відповідь не допомогла.
— Ден Тененбаум

37

Ого, я знаю, що це давнє запитання, але нещодавно я зіткнувся з тією ж точною проблемою (саме так я сюди потрапив).

У будь-якому випадку - мені не подобається source /dev/stdinвідповідь, але я думаю, що знайшов кращу. Насправді це оманливо просто:

echo ls -la | xargs xargs

Приємно, правда? Насправді, це все ще не робить того, що ви хочете, тому що якщо у вас є кілька рядків, він буде об'єднувати їх в одну команду, а не запускати кожну команду окремо. Тож рішення, яке я знайшов:

ls | ... | xargs -L 1 xargs

-L 1опція означає , що ви використовуєте (в більшості) 1 рядок за виконання команди. Примітка: якщо ваш рядок закінчується кінцевим пробілом, він буде об'єднаний з наступним рядком! Тож переконайтеся, що кожен рядок закінчується непробілом.

Нарешті, ви можете це зробити

ls | ... | xargs -L 1 xargs -t

щоб побачити, які команди виконуються (-t це багатослівно).

Сподіваюся, хтось це читає!


30

Спробуйте використати заміну процесу , яка замінює висновок команди тимчасовим файлом, який потім можна отримати:

source <(echo id)

7
Що це за чаклунство?
— paulotorrens

Це була і моя перша думка. Хоча мені більше подобається рішення eval. @PauloTorrens <(xyz) просто виконує xyz і замінює <(xyz) на ім'я файлу, який буде писати результат xyz. Насправді легко зрозуміти, як це працює, наприклад, виконавши: echo <(echo id)надання результату /dev/fd/12(приклад - 12) cat <(echo id), надання результату id, а потім source <(echo id)надання такого ж результату, як просто написанняid
— netigger

2
@PauloTorrens, це називається заміною процесу . Див. Зв’язані документи для офіційного пояснення, але коротка відповідь полягає в тому, що "<()" - це спеціальний синтаксис, розроблений для таких випадків.
— Марк Стосберг,

1
@MarkStosberg, ти знаєш, це спеціальний синтаксис, чи просто (...)перенаправлена підшелупка ?
— paulotorrens

2
@PauloTorrens, Це спеціальний синтаксис саме для заміщення процесу.
— Mark Stosberg

6

Я вважаю, що це "правильна відповідь" на запитання:

ls | sed ... | while read line; do $line; done

Тобто, можна труби в whileпетлю; readкоманда команда займає один рядок від її stdinі привласнює його змінної $line. $lineпотім стає командою, що виконується в циклі; і він продовжується, доки в його введенні не буде подальших рядків.

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


5
`ls | sed ...`

Я начебто відчуваю, що ls | sed ... | source -було б симпатичніше, але, на жаль source, не розумію, що -це означає stdin.


побачивши відповідь mark4o, чи не здається, що це було прямо перед нами весь цей час?
— kch

Так, так. Я ніколи не пам’ятаю, що такі речі існують.
— хаос

1

Я думаю, що вашим рішенням є заміна команди зворотними посиланнями: http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_04.html

Див. Розділ 3.4.5


3
Якщо ви використовуєте bash, $( )синтаксис може бути кращим. '
— Зворотні

6
$ (command) і $ ((1 + 1)) працюють у всіх оболонках posix. Я думаю, що багатьох заважає vim, позначаючи їх як синтаксичні помилки, але це лише тому, що vim виділяє оригінальну оболонку Борна, яку використовують дуже мало. Щоб Vim правильно виділив, поставте це у свій .vimrc: let g: is_posix = 1
— pixelbeat

1

Для використання рішення mark4o на bash 3.2 (macos) тут можна використовувати рядок замість конвеєрів, як у цьому прикладі:

. /dev/stdin <<< "$(grep '^alias' ~/.profile)"

або використовуйте зворотні позначки:. / dev / stdin <<< `grep '^ alias' ~ / .profile`
— Вільям Х. Хупер,

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.