问题
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
回答1:
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
来源:https://stackoverflow.com/questions/8159472/in-a-small-script-to-monitor-a-folder-for-new-files-the-script-seems-to-be-find