user2843389
user2843389

Reputation:

How to use tail -f in a shell script?

I am writing a shell script that is supposed to monitor some log files and redirect the output to a file. I am using tail -f command to do so, but it doesnt seem to work. Here is my script.

#!/bin/bash

ssh -q sc-2 tail -f /home/logs/oam-2.log > /root/logs/file.log &

echo "press return to strop the trace"

read A

kill `ps -ef| grep tail| grep sc-2| grep "\.log"| awk '{print $2}'` 2>/dev/null &

When I run the script, only 5 lines are printed in the file.log

Upvotes: 2

Views: 4079

Answers (1)

iscfrc
iscfrc

Reputation: 421

Although you are sending the ssh process to the background via &, its file descriptors are still "attached" to your terminal. A demonstration:

# Terminal 1
$ ssh $host sleep 600 &
[3] 14340

# Terminal 2
$ ls -l /proc/14340/fd
lrwx------ 1 iscfrc iscfrc 64 Oct  3 10:30 0 -> /dev/pts/1
lrwx------ 1 iscfrc iscfrc 64 Oct  3 10:30 1 -> /dev/pts/1
lrwx------ 1 iscfrc iscfrc 64 Oct  3 10:30 2 -> /dev/pts/1
lr-x------ 1 iscfrc iscfrc 64 Oct  3 10:30 3 -> socket:[125941180]
lrwx------ 1 iscfrc iscfrc 64 Oct  3 10:31 4 -> /dev/pts/1
lrwx------ 1 iscfrc iscfrc 64 Oct  3 10:31 5 -> /dev/pts/1
lrwx------ 1 iscfrc iscfrc 64 Oct  3 10:31 6 -> /dev/pts/1

Note how all the FDs (Minus the TCP socket) are attached to /dev/pts/1.

So what happens when your script runs ssh, puts it in to the background, but the ssh process continues to poll the terminal for input?

# Terminal 2
$ sudo strace -p 14340
Process 14340 attached - interrupt to quit
select(7, [3 4], [], NULL, NULL

# Terminal 1
$ read

# Terminal 2, output
select(7, [3 4], [], NULL, NULL)        = 1 (in [4])
read(4, 0xbff1dbbc, 16384)              = ? ERESTARTSYS (To be restarted)
--- SIGTTIN (Stopped (tty input)) @ 0 (0) ---
--- SIGTTIN (Stopped (tty input)) @ 0 (0) ---
# (infinite loop of the previous three lines)

It goes in to panic when attempting to read() input from file descriptor 4, which you can see above is the terminal. (4 -> /dev/pts/1)

The solution is to disconnect ssh from the terminal's input; I recommend using nohup:

nohup ssh -q sc-2 tail -f /home/logs/oam-2.log > /root/logs/file.log &

Additional suggestions for consideration:

  1. Add the -T argument to your ssh command to disable TTY allocation on the remote host. Since you don't need to interact with the tail command a TTY is unnecessary.
  2. Store the PID of the ssh command in to a variable via $! in order to save yourself needing to parse the output of ps.
  3. And to be even more spartan, you can save yourself an echo by using read's -p option. (p is for prompt)

Combining all of the above:

nohup ssh -qT sc-2 tail -f /home/logs/oam-2.log >/root/logs/file.log &
sshpid=$!
read -p $'press return to stop the trace\n'
kill $sshpid

I tested the above (With my own host/files paths filled in) and it ran for >10 minutes without stopping.

Upvotes: 2

Related Questions