Guille
Guille

Reputation: 75

Backticks can't handle pipes in variable

I have a problem with one script in bash with CAT command.

This works:

 #!/bin/bash
fil="| grep LSmonitor";
log="/var/log/sys.log ";
lines=`cat $log | grep LSmonitor | wc -l`;
echo $lines;

Output: 139

This does not:

#!/bin/bash
fil="| grep LSmonitor";
log="/var/log/sys.log ";

string="cat $log $fil | wc -l";
echo $string;
`$string`;

Output:

cat /var/log/sys.log | grep LSmonitor | wc -l
cat: opcion invalida -- 'l'
Pruebe 'cat --help' para mas informacion.

$fil is a parameter in this example static, but in real script, parameter is get from html form POST, and if I print I can see that the content of $fil is correct.

Upvotes: 0

Views: 482

Answers (3)

ruakh
ruakh

Reputation: 183371

Firstly, allow me to say that this sounds like a really bad idea:

[…] in real script, parameter is get from html form POST, […]

You should not be allowing the content of POST requests to be run by your shell. This is a massive attack vector, and whatever mechanisms you have in place to try to protect it are probably not as effective as you think.

Secondly, | inside variables are not treated as special. This isn't specific to backticks. Parameter expansion (e.g., replacing $fil with | grep LSmonitor) happens after the command is parsed and mostly processed. There's a little bit of post-processing that's done on the results of parameter expansion (including "word splitting", which is why $fil is equivalent to the three arguments '|' grep LSmonitor rather than to the single argument '| grep LSmonitor'), but nothing as dramatic as you describe. So, for example, this:

pipe='|'
echo $pipe cat

prints this:

| cat

Since your use-case is so frightening, I'm half-tempted to not explain how you can do what you want — I think you'll be better off not doing this — but since Stack Overflow answers are intended to be useful for more people than just the original poster, an example of how you can do this is below. I encourage the OP not to read on.


fil='| grep LSmonitor'
log=/var/log/sys.log

string="cat $log $fil | wc -l"
lines="$(eval "$string")"
echo "$lines"

Upvotes: 2

neverendingqs
neverendingqs

Reputation: 4276

Try using eval (taken from https://stackoverflow.com/a/11531431/2687324).

It looks like it's interpreting | as a string, not a pipe, so when it reaches -l, it treats it as if you're trying to pass in -l to cat instead of wc.

The other answers outline why you shouldn't do it this way.

grep LSmonitor /var/log/syslog | wc -l will do what you're looking for.

Upvotes: -2

glenn jackman
glenn jackman

Reputation: 246847

In this case, since you're building a pipeline as a string, you would need:

eval "$string"

But DON'T DO THIS!!!! -- someone can easily enter the filter

; rm -rf *

and then you're hosed.

If you want a regex-based filter, get the user to just enter the regex, and then you'll do:

grep "$fil" "$log" | wc -l

Upvotes: 3

Related Questions