skip to Main Content

I’m basically trying to make debugging easier for other scripts of mine.

(Centos 7.6)

What I need is a script doing :

  1. tail -f the last file entry in a directory
  2. if a new file appears in this directory, it logs this new file, smoothly
  3. if I send a SIGINT (Ctrl+C), it doesn’t leave orphans
  4. with the less possible add-ons for the maximum portability

This is my non working solution :

CURRENT_FILE=`ls -1t | head -n1`
tail -n 100 -f "$CURRENT_FILE" &
PID=$!

while [ true ];
do
    #is there a new file in the directory ?
    NEW_FILE=`ls -1t | head -n1`
    if [[ "$CURRENT_FILE" != "$NEW_FILE" ]]; then
            #yes, so kill last tail
            kill -9 $PID
            clear

            #tail on the new file
            CURRENT_FILE=$NEW_FILE
            tail -n 100 -f "$CURRENT_FILE"
            PID=$!
    fi
    sleep 1s
done

The problem with this solution is that when I’m sending SIGINT (Ctrl+C), what I normally do when exiting a "tail -f", it leaves an orphan child in the background.
I’ve searched solution with "trap" but I don’t get it well, and it doesn’t seem to work with an eternal process like "tail -f".

I’ll be glad to here your thoughts about that and get into advanced bash programming.

2

Answers


  1. You can use trap solution at the beginning of your shell.

    #! /bin/bash
    trap ctrl_c INT
    function ctrl_c() {
        if [[ -n "$PID" ]]; then
            kill -9 $PID
        fi
        exit 0
    }
    
    CURRENT_FILE=`ls -1t | head -n1`
    tail -n 100 -f "$CURRENT_FILE" &
    PID=$!
    
    while [ true ];
    do
        #is there a new file in the directory ?
        NEW_FILE=`ls -1t | head -n1`
        if [[ "$CURRENT_FILE" != "$NEW_FILE" ]]; then
                #yes, so kill last tail
                kill -9 $PID
                clear
    
                #tail on the new file
                CURRENT_FILE=$NEW_FILE
                tail -n 100 -f "$CURRENT_FILE" &
                PID=$!
        fi
        sleep 1s
    done
    
    Login or Signup to reply.
  2. You can trap whenever the script exits and kill the process then. You don’t need -9 to kill your tail though, that’s overkill.

    You can also use inotify to tell you when something happens in the directory instead of sleeping and rechecking. Here’s a basic building block. inotify has a lot of events you can wait for. You can add detection if the file was moved/renamed so you don’t have to restart the tail in those cases etc.

    #!/bin/bash
    
    killpid() {
        if [[ -n $PID  ]]; then
            kill $PID
            PID=""
        fi
    }
    
    trap killpid EXIT
    
    DIR="."
    
    CURRENT_FILE="$(ls -1t "$DIR" | head -n1)"
    
    tailit() {
        echo "::: $CURRENT_FILE :::"
        tail -n 100 -f "$CURRENT_FILE" &
        PID=$!
    }
    
    tailit
    
    # wait for any file to be created, modified or deleted
    while EVENT=$(inotifywait -q -e create,modify,delete "$DIR"); do
        # extract event
        ev=$(sed -E "s/^${DIR}/ (S+) .+$/1/" <<< "$EVENT")
    
        # extract the affected file
        NEW_FILE=${EVENT#${DIR}/ $ev }
    
        case $ev in
            MODIFY)
                # start tailing the file if we aren't tailing it already
                if [[ $NEW_FILE != $CURRENT_FILE ]]; then
                    killpid
                    CURRENT_FILE="$NEW_FILE"
                    tailit
                fi
                ;;
            CREATE)
                # a new file, tail it
                killpid
                CURRENT_FILE="$NEW_FILE"
                tailit
                ;;
            DELETE)
                # stop tailing if the file we are tailing was deleted
                if [[ $NEW_FILE == $CURRENT_FILE ]]; then
                    echo "::: $CURRENT_FILE removed :::"
                    CURRENT_FILE=""
                    killpid
                fi
                ;;
        esac
    done
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search