1

I'm using this script to monitor the downloads folder for new .bin files being created. However, it doesn't seem to be working. If I remove the grep, I can make it copy any file created in the Downloads folder, but with the grep it's not working. I suspect the problem is how I'm trying to compare the two values, but I'm really not sure what to do.

#!/bin/sh

downloadDir="$HOME/Downloads/"
mbedDir="/media/mbed"

inotifywait -m --format %f -e create $downloadDir -q | \
while read line; do
    if [ $(ls $downloadDir -a1 | grep '[^.].*bin' | head -1) == $line ]; then
        cp "$downloadDir/$line" "$mbedDir/$line"
    fi
done
Lesmana
  • 25,663
  • 9
  • 82
  • 87
Squidly
  • 2,707
  • 19
  • 43
  • I'm not clear on what it's supposed to be doing. What's the purpose of the comparison? As written, it seems to only copy the file if it's the first (alphabetically) file containing "bin" (not necessarily ending in "bin", because the grep pattern isn't anchored to the end of the line). – Gordon Davisson Nov 16 '11 at 23:29
  • 1
    2. things, 1. always surround variables of an unknown nature with db-quotes, i.e. `"$line"` If you wind up with a space in value for `$line` that can break things, 2. turn on the shell debugging, i.e. `set -vx` above your while loop. Then you will see each line/block of code AND then the values that assigned to each of the variables. Then it is easy to see where these are failing. If you can't figure it out, edit your post to include relavent debugging output. Good luck. – shellter Nov 16 '11 at 23:41
  • @GordonDavisson The purpose of the comparison is to only move files matching a certain regex, although I did forget to anchor said regex. I couldn't see how to make inotify only move things that matched a pattern. – Squidly Nov 17 '11 at 12:39

1 Answers1

2

The ls $downloadDir -a1 | grep '[^.].*bin' | head -1 is the wrong way to go about this. To see why, suppose you had files named a.txt and b.bin in the download directory, and then c.bin was added. inotifywait would print c.bin, ls would print a.txt\nb.bin\nc.bin (with actual newlines, not \n), grep would thin that to b.bin\nc.bin, head would remove all but the first line leaving b.bin, which would not match c.bin. You need to be checking $line to see if it ends in .bin, not scanning a directory listing. I'll give you three ways to do this:

First option, use grep to check $line, not the listing:

if echo "$line" | grep -q '[.]bin$'; then

Note that I'm using the -q option to supress grep's output, and instead simply letting the if command check its exit status (success if it found a match, failure if not). Also, the RE is anchored to the end of the line, and the period is in brackets so it'll only match an actual period (normally, . in a regular expression matches any single character). \.bin$ would also work here.

Second option, use the shell's ability to edit variable contents to see if $line ends in .bin:

if [ "${line%.bin}" != "$line" ]; then

the "${line%.bin}" part gives the value of $line with .bin trimmed from the end if it's there. If that's not the same as $line itself, then $line must've ended with .bin.

Third option, use bash's [[ ]] expression to do pattern matching directly:

if [[ "$line" == *.bin ]]; then

This is (IMHO) the simplest and clearest of the bunch, but it only works in bash (i.e. you must start the script with #!/bin/bash).

Other notes: to avoid some possible issues with whitespace and backslashes in filenames, use while IFS= read -r line; do and follow @shellter's recommendation about double-quotes religiously.

Also, I'm not very familiar with inotifywait, but AIUI its -e create option will notify you when the file is created, not when its contents are fully written out. Depending on the timing, you may wind up copying partially-written files.

Finally, you don't have any checking for duplicate filenames. What should happen if you download a file named foo.bin, it gets copied, you delete the original, then download a different file named foo.bin. As the script is now, it'll silently overwrite the first foo.bin. If this isn't what you want, you should add something like:

if [ ! -e "$mbedDir/$line" ]; then
    cp "$downloadDir/$line" "$mbedDir/$line"
elif ! cmp -s "$downloadDir/$line" "$mbedDir/$line"; then
    echo "Eeek, a duplicate filename!" >&2
    # or possibly something more constructive than that...
fi
Lesmana
  • 25,663
  • 9
  • 82
  • 87
Gordon Davisson
  • 118,432
  • 16
  • 123
  • 151