LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   pipe as Bash function input (https://www.linuxquestions.org/questions/programming-9/pipe-as-bash-function-input-709376/)

eldorel 03-05-2009 09:02 AM

pipe as Bash function input
 
I maintain a system that deals with hundreds of text files at a time and find myself using infinite loops at the command line fairly frequently for doing things like monitoring a directory for a change, or check the number of files in a location every 5 seconds.

Usually this entails using something similar to

Code:

  while [ 1 ]; do clear; date; ls -al stuff/ | grep -v drwx | cut -d " " -f 6-  ; sleep 5; done
OR

Code:

  while [ 1 ]; do clear; date; ls -al stuff/ | wc -l; sleep 5; done
Since the majority of this is always the same, I decided to write a short function to handle the looping portion.

Code:

loop ()
{
        while [ 1 ]; do
                clear
                date
                echo `$@`
                sleep 1
        done
}

This works fine when executed as long as there are no pipes in the input command to be looped.

For example:

This works
Code:

loop ls -al stuff/
This doesn't
Code:

loop "ls -al stuff/ | wc -l"
The problem appears to be that the pipe is treated as only a character, even when wrapped in backticks.

Hopefully someone can just tell me there's an easier way to do this.

Thanks in advance,
eldorel

ta0kira 03-05-2009 10:15 AM

Try using eval $@ instead of echo `$@`.
ta0kira

Hko 03-05-2009 10:19 AM

Just use the "watch" program. It is more flexible, made and tested especially to do this kind of things (see: man watch).

It comes as part of the procps package together with stuff like "top" and "vmstat", so I suppose all but the most stripped down distro's wil have it installed by default.

To do your thing with "watch":
Code:

watch "ls -al /tmp | wc -l"

colucix 03-05-2009 10:25 AM

The problem is that when you run simply
Code:

loop ls -al stuff/ | wc -l
the pipe is not passed as argument to the function but it is resolved in the parent shell. This means the command at right hand side of the pipe waits for the completion of the input before giving the result. Since you have an infinite loop in the function, the wc -l command waits forever without being executed.

However, if you have a command capable of taking buffered input on the right hand side of the pipe, it works. An example is grep
Code:

loop ls -al stuff/ | grep -v drwx
If you want to monitor the number of files continuously, you have to pass a literal pipe as argument by protecting it from the shell using quotes, as you already noticed. The problem now is the way you run that string as a command inside the function. First of all you don't really need echo, nor back ticks. Instead it has the side effect of displaying the output in a single line. If you want to run a command which is stored in a variable or in the arguments of the script, just do
Code:

$var
or
Code:

$@
respectively. Moreover if you have some special characters (like a pipe) that needs to be evaluated by the shell and not interpreted literally, just use eval. In conclusion - to work with pipes, the way you expect - your function should be
Code:

loop ()
{
        while true
        do
          clear
          date
          eval $@
          sleep 1
        done
}

Edit: Beaten by the more concise post by ta0kira! :)
Hko, good advice! I always forget the watch command.

eldorel 03-05-2009 10:38 AM

Thank you
 
Eval... Duh.

See, I knew you guys would be able to pinpoint my mistake. Guess that's what I get for trying to fix a problem before I get my coffee, Thanks.

As for reinventing the wheel, watch is exactly what I was looking for.

Thanks for all of your help guys..

eldorel


All times are GMT -5. The time now is 08:24 AM.