How to execute the output of a command within the current shell?

BashShellUnixPipe

Bash Problem Overview


I'm well aware of the source (aka .) utility, which will take the contents from a file and execute them within the current shell.

Now, I'm transforming some text into shell commands, and then running them, as follows:

$ ls | sed ... | sh

ls is just a random example, the original text can be anything. sed too, just an example for transforming text. The interesting bit is sh. I pipe whatever I got to sh and it runs it.

My problem is, that means starting a new sub shell. I'd rather have the commands run within my current shell. Like I would be able to do with source some-file, if I had the commands in a text file.

I don't want to create a temp file because feels dirty.

Alternatively, I'd like to start my sub shell with the exact same characteristics as my current shell.

update

Ok, the solutions using backtick certainly work, but I often need to do this while I'm checking and changing the output, so I'd much prefer if there was a way to pipe the result into something in the end.

sad update

Ah, the /dev/stdin thing looked so pretty, but, in a more complex case, it didn't work.

So, I have this:

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

Which ensures all .doc files have their extension lowercased.

And which incidentally, can be handled with xargs, but that's besides the point.

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

So, when I run the former, it'll exit right away, nothing happens.

Bash Solutions


Solution 1 - Bash

The eval command exists for this very purpose.

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

More from the bash manual:

> eval > > eval [arguments] > > The arguments are concatenated together > into a single command, which > is then read and executed, and its > exit status returned as the exit > status of eval. If there are no > arguments or only empty arguments, the > return status is zero.

Solution 2 - Bash

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

UPDATE: This works in bash 4.0, as well as tcsh, and dash (if you change source to .). Apparently this was buggy in bash 3.2. From the bash 4.0 release notes:

> Fixed a bug that caused `.' to fail to read and execute commands from non-regular files such as devices or named pipes.

Solution 3 - Bash

Wow, I know this is an old question, but I've found myself with the same exact problem recently (that's how I got here).

Anyway - I don't like the source /dev/stdin answer, but I think I found a better one. It's deceptively simple actually:

echo ls -la | xargs xargs

Nice, right? Actually, this still doesn't do what you want, because if you have multiple lines it will concat them into a single command instead of running each command separately. So the solution I found is:

ls | ... | xargs -L 1 xargs

the -L 1 option means you use (at most) 1 line per command execution. Note: if your line ends with a trailing space, it will be concatenated with the next line! So make sure each line ends with a non-space.

Finally, you can do

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

to see what commands are executed (-t is verbose).

Hope someone reads this!

Solution 4 - Bash

Try using process substitution, which replaces output of a command with a temporary file which can then be sourced:

source <(echo id)

Solution 5 - Bash

`ls | sed ...`

I sort of feel like ls | sed ... | source - would be prettier, but unfortunately source doesn't understand - to mean stdin.

Solution 6 - Bash

I believe this is "the right answer" to the question:

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

That is, one can pipe into a while loop; the read command command takes one line from its stdin and assigns it to the variable $line. $line then becomes the command executed within the loop; and it continues until there are no further lines in its input.

This still won't work with some control structures (like another loop), but it fits the bill in this case.

Solution 7 - Bash

To use the mark4o's solution on bash 3.2 (macos) a here string can be used instead of pipelines like in this example:

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

Solution 8 - Bash

I think your solution is command substitution with backticks: http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_04.html

See section 3.4.5

Solution 9 - Bash

Why not use source then?

$ ls | sed ... > out.sh ; source out.sh

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionkchView Question on Stackoverflow
Solution 1 - BashJulianoView Answer on Stackoverflow
Solution 2 - Bashmark4oView Answer on Stackoverflow
Solution 3 - BashrabenskyView Answer on Stackoverflow
Solution 4 - BashJacek KrysztofikView Answer on Stackoverflow
Solution 5 - BashchaosView Answer on Stackoverflow
Solution 6 - BashGeoff NixonView Answer on Stackoverflow
Solution 7 - Bashish-westView Answer on Stackoverflow
Solution 8 - BashEric WendelinView Answer on Stackoverflow
Solution 9 - BashKaleb PedersonView Answer on Stackoverflow