How to execute the output of a command within the current shell?
Solution 1
$ 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 2
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 3
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
Try using process substitution, which replaces output of a command with a temporary file which can then be sourced:
source <(echo id)
Solution 5
`ls | sed ...`
I sort of feel like ls | sed ... | source -
would be prettier, but unfortunately source
doesn't understand -
to mean stdin
.
Related videos on Youtube
kch
Updated on December 21, 2020Comments
-
kch over 3 years
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 issh
. I pipe whatever I got tosh
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.
-
Kaleb Pederson almost 15 yearsDoes your complex command work when you pipe to a temp file first and then source it? If not, what's the problem with the generated output? The output of your command won't work if your filenames have spaces in them or if certain sequences aren't escaped properly. I'd want to add quotes around $1 and $2.doc at a minimum.
-
nos almost 15 yearsIs there any good reason for having to run this in the original shell ? - these examples doesn't manipulate the current shell so you gain nothing by doing so. The quick solution is you redirect output to a file and source that file though
-
kch almost 15 years@kaleb the output runs fine. in this particular case, even if i pipe to sh. the file names are space-safe, but thanks for noting. @nos git environment variables on the original shell. and again, these are just examples. the question is for life.
-
srcerer over 9 yearssource /dev/stdin didn't work for me when needing assigned variables to stick around. geirha on freenode bash pointed me to mywiki.wooledge.org/BashFAQ/024 and suggested I try a process substitution source <(command) which worked for me
-
-
chaos almost 15 yearsHe mentioned that temp files are icky.
-
kch almost 15 yearsafter seeing mark4o's answer, doesn't it feel like it was right in our faces all this time?
-
chaos almost 15 yearsHeh, yeah. I never remember that that stuff exists.
-
dmckee --- ex-moderator kitten almost 15 yearsIf you're using bash the
$( )
syntax might be preferred. 'Course backticks work in more shells... -
pixelbeat almost 15 years$(command) and $((1+1)) work in all posix shells. I think many are put off by vim marking them as syntax errors, but that's just because vim is highlighting for the original Bourne shell which very few use. To get vim to highlight correctly put this in your .vimrc: let g:is_posix = 1
-
Tanktalus over 14 yearsThe only issue here is that you may need to insert ;'s to separate your commands. I use this method myself to work on AIX, Sun, HP, and Linux.
-
Milan Babuškov about 13 yearsTanktalus, thanks for that comment, it just made my script work. On my machine eval does not separate commands on newlines and using source does not work even with semi-colons. Eval with semi-colons is the solution. I wish I could give you some points.
-
chriv over 11 yearsI thought this was ingenious until I tried it in msys/mingw (where there is no /dev folder (or even devices)! I tried many combinations of eval, $(), and backticks ``. I couldn't make anything work, so I finally just redirected output into a temp file, sourced the temp file, and the removed it. Basically "sed ... > /tmp/$$.tmp && . /tmp/$$.tmp && rm /tmp/$$.tmp". Anybody got a msys/mingw solution without temp files???
-
Phil over 11 years@MilanBabuškov: I ranked him up for you ;-)
-
Phil over 11 yearsJust a remark: This one does not seem to handle export statements as expected. If the piped string has an export statement, the exportet variable is not available in your terminal environment afterwards, whereas with eval export also works perfectly.
-
Zachary Murray over 10 yearsThis is excellent. However, for my use case, I ended up having to put quotes around my $(), like so:
eval "$( ssh remote-host 'cat ~/.bash_profile' )"
Note that I am indeed using bash 3.2. -
Dan Tenenbaum over 9 yearsThis works for me where the accepted answer did not.
-
that other guy over 9 yearsIn bash without the lastpipe option set, this creates a subshell just like
| bash
would -
Alex Dupuy almost 9 yearsGiven that MacOS X usually has bash 3.2 (maybe Yosemite has 4.0?), and the need for lastpipe trickery in my use case (exporting all the variables in a file by pre-processing each line with sed to add 'export ') I chose to go with the
eval "$(sed ...)"
approach - but thanks for the 3.2 bug heads-up! -
shrx almost 9 years@AlexDupuy nope, Yosemite still provides the old
3.2.57(1)-release (x86_64-apple-darwin14)
. -
paulotorrens over 8 yearsWhich kind of sorcery is this?
-
netdigger over 8 yearsThis was my first thought as well. Though I like the eval solution better. @PauloTorrens <(xyz) simply executes xyz and replaces <(xyz) with the name of a file that will write the output of xyz. It's really easy to understand how this works by doing for example:
echo <(echo id)
, giving the output/dev/fd/12
(12 is an example),cat <(echo id)
, giving the outputid
, and thensource <(echo id)
giving the same output as simply writingid
-
Mark Stosberg about 8 years@PauloTorrens, this is called process substitution. See linked docs for official explanation, but the short answer is that "<()" is a special syntax that was designed for cases like this in mind.
-
paulotorrens about 8 years@MarkStosberg, do you know if this is a special syntax, or just a subshell
(...)
redirected? -
Mark Stosberg about 8 years@PauloTorrens, It is a special syntax just for process substitution.
-
nhed almost 8 yearsThe problem with this answer is that the source is executed in a subshell and thus the values are lost at the invoking shell this variation tho worked for me
source <(sed ...)
... but now I see that it was already given here stackoverflow.com/a/22233248/652904 by @rishta -
William H. Hooper over 4 yearsor use backticks: . /dev/stdin <<< `grep '^alias' ~/.profile`
-
JP de la Torre about 2 yearsI'm from the future and this is still the best solution. Clear, short, simple and free of side effects. Specially now that backticks are considered obsolete and anti-pattern.