views:

355

answers:

6

Hi,

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.

+2  A: 

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

Eric Wendelin
If you're using bash the `$( )` syntax might be preferred. 'Course backticks work in *more* shells...
dmckee
$(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
pixelbeat
+2  A: 
`ls | sed ...`

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

chaos
Yea, I tried that. That's so wrong of `source`
kch
after seeing mark4o's answer, doesn't it feel like it was right in our faces all this time?
kch
Heh, yeah. I never remember that that stuff exists.
chaos
A: 

Why not use source then?

$ ls | sed ... > out.sh ; source out.sh
Kaleb Pederson
He mentioned that temp files are icky.
chaos
Oh yeah, missed that :(.
Kaleb Pederson
+3  A: 
$ 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.

mark4o
That's clever. I like it.
kch
Ok. On to bash 4.0 then.
kch
Oh, and since you're listing the shells, it works in zsh too.
kch
+2  A: 

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.

Juliano
The 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.
Tanktalus
Tanktalus: No, you don't. Eval really interprets the string as a script, with newlines separating commands. There is no need to use semicolons.
Juliano
In fact, I would like to know why the accepted answer was the hack of piping output to `source` instead of this one, that suggests the proper command that exists for this very purpose.
Juliano
A: 

. <(ls | sed ... | sh)

Patrick