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


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.