11

I tried to run commands using pipes.

Basic:

single="ls -l"
$single

which works as expected

Pipes:

multi="ls -l | grep e"
$multi
ls: |: No such file or directory
ls: grep: No such file or directory
ls: e: No such file or directory

...no surprise

bash < $multi

$multi: ambiguous redirect

next try

bash $multi
/bin/ls: /bin/ls: cannot execute binary file

Only

echo $multi > tmp.sh
bash tmp.sh

worked.

Is there a way to execute more complex commands without creating a script for execution?

stacker
  • 68,052
  • 28
  • 140
  • 210

3 Answers3

19

You're demonstrating the difference between the shell and the kernel.

"ls -l" is executable by the system execve() call. You can man execve for details, but that's probably too much detail for you.

"ls -l | grep e" needs shell interpretation to set up the pipe. Without using a shell, the '|' character is just passed into execve() as an argument to ls. This is why you see the "No such file or directory" errors.

Solution:

cmd="ls -l | grep e"
bash -c "$cmd"
bukzor
  • 37,539
  • 11
  • 77
  • 111
  • Thanks, any idea why "bash -c" prints the output in a single line? I would like to have the same output as entered manually at command line. – stacker Aug 12 '10 at 16:27
  • @stacker I don't think that's true. I get the same result as at the command line. Perhaps your grep is only returning a single line? – bukzor Aug 12 '10 at 16:33
  • This happend because I forgot to enclose the variable in double quotes. – stacker Aug 12 '10 at 16:38
  • In my case I was trying to call a command that wasn't in the system path, I added its folder to my system path like shown here: http://unix.stackexchange.com/a/26059/61349 – ThorSummoner Feb 23 '15 at 22:32
0

You need a heredoc to do this correctly. In answer to POSIX compliant way to see if a function is defined in an sh script, I detailed how to read a script into a variable, programmatically parse it for information and/or modify it as necessary, then execute it from another script or shell function. That's basically what you're trying to do, and the heredoc makes it possible because it provides a file descriptor:

% multi='ls -l | grep e'
% sh <<_EOF_
> ${multi}
> _EOF_
< desired output >

That would solve your simple example case. See my other answer for more.

-Mike

Community
  • 1
  • 1
mikeserv
  • 694
  • 7
  • 9
-3

when you want to run commands with pipes, just run it. Don't ever put the command into a variable and try to run it. Simply execute it

ls -l |grep

If you want to capture the output, use $()

var=$(ls -l |grep .. )

ghostdog74
  • 327,991
  • 56
  • 259
  • 343
  • The commands come from a different server and should be excecuted on the target machine. Thus it can't be hard coded. – stacker Aug 12 '10 at 16:29