Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. It's 100% free, no registration required.

I'm running fs_usage to detect access to an object on my filesystem.

sudo fs_usage -w | grep -E 'object'

I now want to run a touch command on that object every 5 seconds as long as there's no new output from the above command for a period of 5 seconds.

share|improve this question
    
Does fs_usage ever terminate? If not you need a different approach. What tail does is get you the last line(s). It can't know what the last line is until its input terminates, so as long as the pipe chain is held open tail will never output anything. –  jw013 Dec 3 '14 at 19:50
    
@jw013 Ok, now I get the problem. fs_usage does not terminate. Could you suggest another approach then? –  Christoph90 Dec 3 '14 at 19:53
    
What are you trying to accomplish? –  jw013 Dec 3 '14 at 19:59
    
I basically want to execute a bash command when there's no new output for 5 seconds and repeat that command every 5 seconds until fs_usage yields new output. –  Christoph90 Dec 3 '14 at 20:07
    
Update your question. What is this command that runs every 5 seconds supposed to accomplish? –  jw013 Dec 3 '14 at 20:16

2 Answers 2

sudo fs_usage -w | while true; do
    if read -rt5 && [[ $REPLY =~ objectpattern ]]; then
        # Some output happened in the last 5 seconds that matches object pattern
        :
    else 
        touch objectfile
    fi
done

Of course, using read -t means there's the possibility that there's some output not matching objectpattern; if that happens, the file will be touched. If you want to avoid that, we have to get a little more sophisticated.

timeout=5
sudo fs_usage -w | while true; do
    (( mark = SECONDS + timeout ))
    if !read -rt$timeout; then
        touch objectfile
        timeout=5
    elif ![[ $REPLY =~ objectpattern ]]; then
        # Some output happened within timeout seconds that does _not_ match.
        # Reduce timeout by the elapsed time.
        (( timeout = mark - SECONDS ))
        if (( timeout < 1 )); then
            touch objectfile
            timeout=5
        fi
    else
        timeout=5
    fi
done
share|improve this answer
    
For some reason this is not working for objectpattern = /Volumes/Data (Volume is never touched) –  Christoph90 Mar 9 at 8:04
    
Can you show how you're using it? –  kojiro Mar 9 at 17:29

If I understand you correctly, then probably you want to do:

sh -c '{ fsusage             #your command runs (indefintely?)
         kill -PIPE "$$"     #but when it completes, so does this shell
       } >&3 &               #backgrounded and all stdout writes to pipe
       while sleep 5         #meanwhile, every 5 seconds a loop prints
       do    echo            #a blank line w/ echo
       done' 3>&1 |          #also to a pipe, read by an unbuffered (GNU) sed
sed -u '
### if first input line, insert shell init to stdout
### for seds [aic] commands continue newlines w/ \escapes
### and otherwise \escape only all other backslashes
1i\
convenience_func(){ : this is a function  \\\
                      declared in target  \\\
                      shell and can be    \\\
                      called from sed.; }
### if line matches object change it to command
/object/c\
# this is an actual command sent to a shell for each match
### this is just a comment - note the \escaped newlines above                    
### delete all other nonblank lines; change all blanks
/./d;c\
# this is a command sent to a shell every ~5 seconds
' | sh -s -- This is the target shell and these are its   \
             positional parameters. These can be referred \
             to in sed\'s output like '"$1"' or '"$@"' as \
             an array. They can even be passed along to   \
             'convenience_func()' as arguments.

About 90% of the above consists of comments. Basically it can be boiled down to...

sh -c '(fsusage;kill "$$") >&3 &
       while sleep 5; do echo; done
' 3>&1| 
sed -nue '/pattern/c\' -e 'echo match
          /./!c\'      -e 'touch -- "$1"
' | sh -s -- filename
share|improve this answer
    
Unfortunately sed option -u seems to be unavailable on my machine (osx) –  Christoph90 Mar 9 at 8:02
    
@Christoph90 - in that case, try swapping -u for -l - on a bsd sed it should ensure line-buffered output. –  mikeserv Mar 10 at 0:42

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.