5

We have a shell script that builds a long piped command chain in a variable and executes it with eval (the following code is simplified to the essential):

 cmd="cat /some/files | grep -v \"this\" | grep -v \"that\""
 cmd="$cmd | grep -v \"much more dynamical filter with variables\""
 ...
 result=`eval $cmd`

All worked fine so far but now it seems that the content of the cmd variable exceeds a limit. When it exceeds about 95970 bytes I will receive the error (although syntax is correct):

eval: line ...: syntax error near unexpected token `|'

I did some research, but I didn't get a clue (getconf ARG_MAX echoes 2621440, ulimit -a doesn't helped me, too).

Could someone please explain which limit this could be and maybe how to increase the limit or what is the best way to avoid it?


EDIT: I have tested it now on three different servers (centos) with a designated script. On all servers I ended up reaching 3333 pipes in one command with eval.

And I have found another page where someone experienced the same but without eval. So it seems to be just a limit of pipes.

Knowing that the limitation is probably caused by the number of pipes will help me to workaround the problem. So this is not the question anymore.

But I am still interested in how this limit is set or at least how to detect the value of the limit (probably not on every system 3333) without running a script for that.

It can be reproduced with:

yes cat | head -n 3334 | paste -sd '|' - | bash
hellcode
  • 742
  • 1
    Why do it through eval? Is there are some objection to do it directly result=$(grep -v "this" /some/files | grep -v "that") or through function cmd(){......}? – Costas Feb 20 '15 at 13:53
  • You can shorten grep -v this | grep -v that to grep -v 'this\|that'. – choroba Feb 20 '15 at 14:13
  • @Costas: The command has to be build dynamically with external data. Therfore I have to collect the different grep commands in a variable. The example above is extremely simplified. – hellcode Feb 20 '15 at 14:32
  • @Costas: How did you mean through function? – hellcode Feb 20 '15 at 15:07
  • How many greps do you need? And does eval "$sixkstring" "$sixkstring" work? Also, since you're building it programmatically, what is the problem with { printf %s\\n 'grep -v this |'; echo grep -v that \|; ...; } | sh – mikeserv Feb 20 '15 at 15:11
  • as for grep, you can construct your grep pattern in a grep file. 2) as for tou syntax error, are you sure you don't generate something like " grep -v $FOO | " where FOO is empty ? 3) what's wrong with result=$( $cmd ) ?
  • – Archemar Feb 20 '15 at 15:12
  • @mikeserv: actually there are more than 3000 greps with dynamic filter values in it. – hellcode Feb 20 '15 at 15:14
  • 4
    @hellcode - that has got to be the ugliest idea I've ever heard. You've got to come up with something else - you need some organizational process (I'd use sed) to parse all of that into something sensible. You don't just toss 3000 greps in the process pool for this and that. – mikeserv Feb 20 '15 at 15:15
  • @Archemar: 1) with big effort I could group many of the patterns to one big pattern file but unless I don't know the reason for the limitation I wouldn't do that 2) yes, I am sure. – hellcode Feb 20 '15 at 15:21
  • @Archemar: 3) it doesn't work with pipes – hellcode Feb 20 '15 at 15:48
  • It's difficult to tell without seeing your real code. I do see at least one mistake (though it wouldn't cause this problem with this exact code): missing double quotes in eval "$cmd". To debug this, arrange to build the string with plenty of newlines, print out the string before evaluating it, and check what the line number in the error corresponds to. If you need more help, post code that would allow us to reproduce the error. – Gilles 'SO- stop being evil' Feb 20 '15 at 22:18
  • @hellcode If you need more than 3333 pipes for your program, there is a good chance that there is something really wrong with your design. Maybe you could tell us more about what you really have to do? To get back to the topic at hand, it is possible to make the bulk of what you want to do in a single grep command : cat file.txt | grep -v 'str1' | grep -v 'str2' is equivalent to grep -v -e 'str1' -e 'str2' file.txt. – user43791 Feb 20 '15 at 22:24
  • @Gilles: Why is it a mistake? As I can see the double quotes don't change anything. The line number in the error corresponds to the code line with eval. – hellcode Feb 20 '15 at 22:41
  • @hellcode http://unix.stackexchange.com/questions/131766/why-does-my-shell-script-choke-on-whitespace-or-other-special-characters The quotes wouldn't change anything in the snippet you posted, but they might have an effect on your real code, depending on what's actually in there. – Gilles 'SO- stop being evil' Feb 20 '15 at 22:48
  • @Gilles: Thank you for the link and the explanations. In my case it doesn't matter. – hellcode Feb 20 '15 at 22:58
  • 1
    @hellcode: are all the grep commands really grep -v "some pattern"? If so, you can use grep -v -e "some pattern" -e "some other pattern" -e "yet another pattern", and that grep command could be built up using an array instead of eval, which would require less quoting. – rici Feb 21 '15 at 04:47
  • @rici - definitely - or even just as a $(command substitution) in a heredoc w/ one pattern per line. 3000 patterns might be a little much for a single grep, but that kind of thing can be distributed - at least it wouldn't require a shell to ask the kernel to allocate 3000 pipes and start 3000 subshells and 3000 grep processes. – mikeserv Feb 21 '15 at 05:40
  • @StéphaneChazelas: I expanded my comment into an answer. – rici Feb 23 '15 at 21:53