9

I am trying to pipe the output of a tail command into another bash script to process:

tail -n +1 -f your_log_file | myscript.sh

However, when I run it, the $1 parameter (inside the myscript.sh) never gets reached. What am I missing? How do I pipe the output to be the input parameter of the script?

PS - I want tail to run forever and continue piping each individual line into the script.

Edit For now the entire contents of myscripts.sh are:

echo $1;
slthomason
  • 373
  • 2
  • 4
  • 12
  • You're missing an argument to assign to `$1`. The pipe connects the output of tail to the standard input of your script. Can you post `myscript.sh`, or at least enough to determine whether you need an argument (and what that argument should be), and how it will handle the data presented on its standard input. – chepner Aug 22 '12 at 19:11

3 Answers3

18

Generally, here is one way to handle standard input to a script:

#!/bin/bash

while read line; do
    echo $line
done

That is a very rough bash equivalent to cat. It does demonstrate a key fact: each command inside the script inherits its standard input from the shell, so you don't really need to do anything special to get access to the data coming in. read takes its input from the shell, which (in your case) is getting its input from the tail process connected to it via the pipe.

As another example, consider this script; we'll call it 'mygrep.sh'.

#!/bin/bash

grep "$1"

Now the pipeline

some-text-producing-command | ./mygrep.sh bob

behaves identically to

some-text-producing-command | grep bob

$1 is set if you call your script like this:

./myscript.sh foo

Then $1 has the value "foo".

The positional parameters and standard input are separate; you could do this

tail -n +1 -f your_log_file | myscript.sh foo

Now standard input is still coming from the tail process, and $1 is still set to 'foo'.

chepner
  • 497,756
  • 71
  • 530
  • 681
  • Not sure I follow. Where is foo coming from / getting populated? – slthomason Aug 22 '12 at 19:26
  • 'foo' is just an arbitrary string on the command line. If there is a string that follows your command, that is what gets assigned to `$1`. If there is a second string, that gets assigned to `$2`, and so forth. In your original example, you had no arguments following `myscript.sh`, which is why `$1` had no value. – chepner Aug 22 '12 at 19:29
  • right - so how do I pipe the output of tail into myscript.sh? do I just incorporate it into myscript.sh? If so, how? – slthomason Aug 22 '12 at 19:29
  • Any command in your script that reads from standard input will read from the pipe. Run the sample script at the top of my answer in a pipeline to see how it works. – chepner Aug 22 '12 at 19:32
  • Sorry for being dense. Worked perfectly when I plugged it in correctly! – slthomason Aug 22 '12 at 19:38
  • 1
    @chepner, for a `cat` equivalent, the loop should have `echo "$line"`, not `echo -n "$line"`. – Henk Langeveld Aug 22 '12 at 21:49
  • This doesn't work with `ls | awk '\pattern\' {getline; print $2}' | ./myscript.sh`. Thoughts? – Khurshid Alam Oct 30 '16 at 07:23
1

Perhaps your were confused with awk?

tail -n +1 -f your_log_file | awk '{
    print $1
}'

would print the first column from the output of the tail command.

In the shell, a similar effect can be achieved with:

tail -n +1 -f your_log_file | while read first junk; do
    echo "$first"
done

Alternatively, you could put the whole while ... done loop inside myscript.sh

Henk Langeveld
  • 8,088
  • 1
  • 43
  • 57
1

Piping connects the output (stdout) of one process to the input (stdin) of another process. stdin is not the same thing as the arguments sent to a process when it starts.

What you want to do is convert the lines in the output of your first process into arguments for the the second process. This is exactly what the xargs command is for.

All you need to do is pipe an xargs in between the initial command and it will work:

tail -n +1 -f your_log_file | xargs | myscript.sh

Russ
  • 10,835
  • 12
  • 42
  • 57