3

Using Enterprise Linux 5/6, Bash 4.x I want this type of logic:

# IF temp file exists, exit because we are restarting already     
find /tmp/restarting_server -mmin -10 -exec exit 1

lsof -i TCP:1234 || declare Server_is_down=TRUE
    if ! [ -z $Server_is_down ]; then restart_server
    fi
# Restart Server Function
restart_server() {
    touch /tmp/restarting_server 
    service server restart
    rm -f /tmp/restarting_server 
}

The problem is find's -exec doesn't like builtins it seems. I know I can do an if then statement to check for the file and exit there, but I want to know how to do this in a find -exec (or I'd settle for a good xargs solution).

6
  • What is supposed to exit? The find command or the script itself?
    – terdon
    Commented Mar 13, 2014 at 16:27
  • @terdon The script should exit with 1. Commented Mar 13, 2014 at 16:29
  • For more on find's exit status, scroll down to the near the end of man find: "find exits with status 0 if all files are processed successfully,greater than 0 if errors occur." Not finding a match is not considered an error because the command actually ran correctly, it simply had no results. You'll get an error if you try searching in a directory that does not exist for example: find /hahagd/ ; echo $?.
    – terdon
    Commented Mar 13, 2014 at 16:32
  • 1
    You can't exit the parent script from within find. find -exec is running in a separate subshell and has no knowledge of the script. The way to exit is what l0b0 and devnull have suggested.
    – terdon
    Commented Mar 13, 2014 at 16:45
  • 1
    Im pretty sure you can.
    – mikeserv
    Commented Mar 13, 2014 at 17:09

4 Answers 4

4

You could check if it returned a result:

[ -z "$(find /tmp/restarting_server -prune -mmin -10)" ] || exit 1
1
  • Not bad. exit status of find doesn't work $? it's 0 whether it finds it or not, but this is a nice idea. Commented Mar 13, 2014 at 16:24
3
while [ -f /tmp/restarting_server ] ; do {
    [ $((i=i+1)) -ge 10 ] && exit 1
    sleep 1 
} ; done

You dont need find if you know already the exact filename, pathname, and conditions under which a file should be acceptably found.

So the other problem - exiting your script from your -exec statement - is maybe not the problem you consider it to be. find doesn't build in an option for killing its parent shell because the shell already provides it.

find … -exec sh -c kill\ $$ \;

Consider also that you can use this construct for signalling based on existing paths even if you're not certain before hand where they'll be:

trap 'stuff to do' USR1    
… #later… 
find … -exec sh -c kill\ -USR1\ $$ \;

And this opens a lot of other options to you as well:

find … -exec sh -c exec… 

EDIT:

I've just thought of other options involving parameter expansion to make your shell exit without find's -exec at all that could be used in other ways:

hold_file="$(find . -name file)"
${hold_file:+false} : || exit 1

or

N= ; hold_file="$(find . -name file)"
${hold_file:+${N:?lock file remains, exiting...}}

Both of these will only cause an exit if the variable to which you assign find's stdout is neither null or unset. And of course, should you desire to fail based on an empty find, you can do the same with :- instead of :+ and omitting the $Null value variable entirely.

Or just to alter find's exit status:

$(find . -name file -exec echo false \; ) : || exit 1
10
  • This is a good answer. I thought of the find … -exec sh -c kill\ $$ (see it in my comments above, however I chose to put $$ into a different variable earlier on). I guess I assumed there was some feature of find that I wasn't aware of which was useful for this. Perhaps I am too set on using the wrong tool for the job here, I just thought it made a nice one liner. Commented Mar 13, 2014 at 17:11
  • 2
    Bah. 'Wrong tool for the job' is probably a mantra you should abandon. Do what works.
    – mikeserv
    Commented Mar 13, 2014 at 17:31
  • Oh, and you dont need to use a different $var because the shell evaluates the expansions before running the command - so when find receives that argument string it will already be evaluated. I dont know if find properly evaluates $$, but if so one escape like \$$ would kill the find process and '\$$' would kill its child sh process.
    – mikeserv
    Commented Mar 13, 2014 at 17:53
  • 1
    You can use -exec kill $$ \;, no need to invoke an intermediate shell for this. You'll need the final ; however. Commented Mar 13, 2014 at 23:36
  • 1
    @mikeserv no it isn't. if you don't use the right tool for the job, you're creating brittle code that can't be easily modified because you had to jump through hoops to get the original to work. plus, why wouldn't you use the right tool? refusing to do so without an external motivation is just stupid.
    – strugee
    Commented Mar 14, 2014 at 2:30
2

find is a program of its own. The argument to -exec is another command. When a command like find … -exec foo {} \; is executed, the shell has find as a subprocess, and each instance of foo is a subprocess of find. The command exit exists only as a shell builtin, not as an independent command; an exit program would be impossible since it would have to make its parent process exit.

The solution to your immediate problem is thus to make find report whether it found a file or not, and make the shell exit if there was a match. You can't use the return status of find, because it considers the absence of any match to be a success reason. Instead, test the output.

if [ -n "$(find /tmp/restarting_server -mmin -10)" ]; then exit; fi

Here, the file should be a regular file if it exists at all. However, in general, the file could be a directory, in which case find would traverse it. So tell find to stop when it finds a match.

if [ -n "$(find /tmp/restarting_server -mmin -10 -prune)" ]; then exit; fi

However, the resulting script is not reliable. You are not implementing a lock correctly. If two instances of the script start at almost the same time, it's possible to have, for example:

  1. Script 1 starts, sees no restarting_server file, and proceeds merrily.
  2. Script 2 starts, sees no restarting_server file, and proceeds merrily.
  3. Script 1 sees that the server is down and decides to restart it.
  4. Script 2 sees that the server is down and decides to restart it.
  5. Script 1 creates /tmp/restarting_server.
  6. Script 2 updates the timestamp on /tmp/restarting_server.
  7. Script 1 calls service server restart.
  8. Script 2 calls service server restart.
  9. Script 1 removes /tmp/restarting_server.
  10. Script 2 calls rm -f /tmp/restarting_server which does nothing.

Call flock to implement proper locking.

(
  lsof -i TCP:1234 >/dev/null ||
  service server restart
) 3>/var/lock/maybe_restarting_server

You don't need a timeout to invalidate the lock across reboots, since the locking is held in-memory.

1

You can simply do this:

if [ "$(find /tmp -maxdepth 1 -mindepth 1 -name restarting_server -mmin -10)" = '/tmp/restarting_server' ]
then
    exit 1
fi

This works even if you're using set -o errexit (which you should).

5
  • This is wrong I believe. Find will return 0 whether it finds the item or not. Your solution will always exit 1 Commented Mar 13, 2014 at 16:26
  • @GreggLeventhal you're right. Fixed.
    – l0b0
    Commented Mar 13, 2014 at 16:29
  • Thanks. This solution isn't bad, but I was hoping to learn how to exit an enclosing script from the -exec argument of a find within that script. Something like script_pid=$$;find /tmp/tempfile -exec kill $script_pid \; But better. Commented Mar 13, 2014 at 16:42
  • @GreggLeventhal It's not clear to me why you would want to do that. This way you can trivially add for example logging and cleanup code without making the code a mess.
    – l0b0
    Commented Mar 13, 2014 at 16:46
  • Haha, I know that page (i've even been on his IRC ). Perhaps find simply isn't meant to behave that way since as Terdon points out, exec is run in a subshell which in itself removes it from the context of the script that called it. I guess I was just wondering it there is a find based solution that I was missing, but I guess not. Occam's razor. Commented Mar 13, 2014 at 17:06

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .