Как выполнить вывод команды в текущей оболочке?

89

Мне хорошо sourceизвестна .утилита (также известная как ), которая берет содержимое из файла и выполняет его в текущей оболочке.

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

$ ls | sed ... | sh

lsэто просто случайный пример, исходный текст может быть любым. sedтоже просто пример преобразования текста. Интересный момент sh. Я прокручиваю все, что у меня есть, shи оно работает.

Моя проблема в том, что это означает запуск новой вспомогательной оболочки. Я бы предпочел, чтобы команды выполнялись в моей текущей оболочке. Если бы у source some-fileменя были команды в текстовом файле, я бы смог это сделать .

Я не хочу создавать временный файл, потому что кажется грязным.

В качестве альтернативы я хотел бы запустить свою вспомогательную оболочку с теми же характеристиками, что и моя текущая оболочка.

Обновить

Хорошо, решения, использующие обратный апостроф, безусловно, работают, но мне часто приходится делать это, пока я проверяю и изменяю вывод, поэтому я бы предпочел, чтобы был способ передать результат во что-то в конце.

печальное обновление

Ах, /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 доллара США.
Калеб Педерсон,
Есть ли веская причина запускать это в исходной оболочке? - эти примеры не управляют текущей оболочкой, поэтому вы ничего не получите от этого. Быстрое решение - вы перенаправляете вывод в файл и исходите из этого файла
№№
@kaleb вывод работает нормально. в данном конкретном случае, даже если я буду трубить в ш. имена файлов свободны от места, но спасибо за внимание. @nos git переменные среды в исходной оболочке. и опять же, это всего лишь примеры. вопрос на всю жизнь.
Kch
source / dev / stdin не работал у меня, когда мне нужно было, чтобы назначенные переменные оставались. geirha на freenode bash указала мне на mywiki.wooledge.org/BashFAQ/024 и предложила мне попробовать источник подстановки процесса <(команда), который у меня
сработал

Ответы:

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

ОБНОВЛЕНИЕ: это работает в bash 4.0, а также в tcsh и dash (если вы перейдете sourceна .). По-видимому, это было ошибкой в ​​bash 3.2. Из примечаний к выпуску bash 4.0 :

Исправлена ​​ошибка, вызывающая `. ' не читать и выполнять команды из нестандартных файлов, таких как устройства или именованные каналы.

mark4o
источник
ОК. Тогда переходите к 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 экспорт также отлично работает.
Фил
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 не разделяет команды на символы новой строки, и использование исходного кода не работает даже с точкой с запятой. Решение с точкой с запятой. Хотел бы я дать вам несколько очков.
Милан Бабушков
2
@ MilanBabuškov: Я оценил его для вас ;-)
Фил
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, это специальный синтаксис как раз для подстановки процесса.
Марк Стосберг,
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, $( )синтаксис может быть предпочтительнее. 'Курсовые обратные кавычки работают в большем количестве снарядов ...
dmckee --- котенок экс-модератора
6
$ (команда) и $ ((1 + 1)) работают во всех оболочках posix. Я думаю, что многих отталкивает то, что vim помечает их как синтаксические ошибки, но это только потому, что vim выделяет исходную оболочку Bourne, которую очень немногие используют. Чтобы vim правильно выделял, поместите это в свой .vimrc: let g: is_posix = 1
pixelbeat
1

Чтобы использовать решение mark4o в bash 3.2 (macos), вместо конвейеров, как в этом примере, можно использовать строку here:

. /dev/stdin <<< "$(grep '^alias' ~/.profile)"
иш-запад
источник
или используйте обратные кавычки:. / dev / stdin <<< `grep '^ alias' ~ / .profile`
Уильям Х. Хупер,
0

Почему бы sourceтогда не использовать ?

$ ls | sed ... > out.sh ; source out.sh
Калеб Педерсон
источник
Он упомянул, что временные файлы неприятны.
хаос
Ах да, пропустил :(.
Калеб Педерсон,