• Uncategorized

About bash : Waiting-for-a-file-to-be-created-in-Bash

Question Detail

I need to create a bash script to wait for a file to be created. The script will use sleep command inside a while loop to periodically check on a file every 10 seconds. Print out a message while waiting. Display the content of the file once the file is created. Below is what I have tried to implement and it obviously does not work. At this point, I’m not entirely sure how to proceed.

let file=$1

while '( -f !  /tmp/$1)'
       sleep 10
       echo "still waiting"

echo "Content of the file $1:"

Question Answer

The problem here is with the test, not the sleep (as the original question hypothesized). The smallest possible fix might look as follows:

while ! test -f "/tmp/$1"; do
  sleep 10
  echo "Still waiting"

Keep in mind the syntax for a while loop:

while: while COMMANDS; do COMMANDS; done
    Expand and execute COMMANDS as long as the final command in the
    `while' COMMANDS has an exit status of zero.

That is to say, the first argument given to while, expanding the loop, is a command; it needs to follow the same syntax rules as any other shell command.

-f is valid as an argument to test — a command which is also accessible under the name [, requiring a ] as the last argument when used in that name — but it’s not valid as a command in and of itself — and when passed as part of a string, it’s not even a shell word that could be parsed as an individual command name or argument.

When you run '( -f ! /tmp/$1)' as a command, inside quotes, the shell is looking for an actual command with exactly that name (including spaces). You probably don’t have a file named '/usr/bin/( -f ! /tmp/$1)' in your PATH or any other command by that name found, so it’ll always fail — exiting the while loop immediately.

By the way — if you’re willing to make your code OS-specific, there are approaches other than using sleep to wait for a file to exist. Consider, for instance, inotifywait, from the inotify-tools package:

while ! test -f "/tmp/$1"; do
  echo "waiting for a change to the contents of /tmp" >&2
  inotifywait --timeout 10 --event create /tmp >/dev/null || {
    (( $? == 2 )) && continue  ## inotify exit status 2 means timeout expired
    echo "unable to sleep with inotifywait; doing unconditional 10-second loop" >&2
    sleep 10

The benefit of an inotify-based interface is that it returns immediately upon a filesystem change, and doesn’t incur polling overhead (which can be particularly significant if it prevents a system from sleeping).

By the way, some practice notes:

  • Quoting expansions in filenames (ie. "/tmp/$1") prevents names with spaces or wildcards from being expanded into multiple distinct arguments.
  • Using >&2 on echo commands meant to log for human consumption keeps stderr available for programmatic consumption
  • let is used for math, not general-purpose assignments. If you want to use "$file", nothing wrong with that — but the assignment should just be file=$1, with no preceding let.

You may also like...

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.