Super User is a question and answer site for computer enthusiasts and power users. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I want to move files in a directory that are not currently in use. I have a sense that some combination of lsof, find, and xargs could work but I can't quite get there. So far, I created the following command:

lsof mydir/*|awk '(NR>1){print $9}

This gives me a list of files that ARE being used. If I could just get a list of files that are NOT being used, then something like xargs could issue a mv on those files. I just can't seem to find an elegant way of doing this. Does anyone have any hints for me?

share|improve this question

migrated from Dec 8 '10 at 1:18

This question came from our site for professional and enthusiast programmers.

This seems like a question for Super User or Unix and Linux – Michael Mrozek Dec 7 '10 at 17:53
You might have to break out the big guns, and script in python and perl to parse the lsof output into a map, and then do the walk operation, excluding opened files, to build up the list of files to move. However I wonder why you can't just move all files in the first place? – Douglas Leeder Dec 7 '10 at 17:54
Another idea might be to move everything, then move anything open back to the original location? Assuming the move preserves enough information of course. – Douglas Leeder Dec 7 '10 at 17:55
@Michael Mrozek: Why? This is a programming question. I'm not asking how to use ls, I'm trying write a bash script. – User1 Dec 7 '10 at 17:56
@User1 That shouldn't be true; you can move a file around and open file handles will still work. You can even erase a file and programs that have it open can keep reading from it – Michael Mrozek Dec 7 '10 at 18:02

I'd do it this way.

find $dir -maxdepth 1 | sort > $other_dir/all_files
lsof $dir/* | awk '(NR>1) {print $9}' | sort > $other_dir/in_use_files
comm -2 -3 $other_dir/all_files $other_dir/in_use_files

From comm(1):

       comm - compare two sorted files line by line

       comm [OPTION]... FILE1 FILE2


       -2     suppress lines unique to FILE2

       -3     suppress lines that appear in both files

Now it's a simple matter of reformatting into a series of mv statements. Perhaps like this:

while IFS= read file ; do
    mv "$file" "$destination/"
done < <(comm -2 -3 all_files in_use_files)

Or using another intermediate file if you prefer.

share|improve this answer
Thanks for the tip about using comm that puts everything together. – User1 Dec 7 '10 at 19:59

Perhaps not the best way, but one can just code it up in a few minutes:

get your list of open files (sort if not already)

get a list of all files (sort if not already)

diff the two lists

xargs whatever

The number of steps here will make obvious something that should have been a concern anyway, which is race conditions - the files that are in use when you select them may not be the files that are in use when you actually move them.

share|improve this answer
Great point about race conditions. – Wesley Rice Dec 7 '10 at 18:36
up vote 2 down vote accepted

Thanks to @Sorpigal's suggestion, I found an easy way to make this work without loops:

comm -2 -3 <(find $dir -maxdepth 1 -type f|sort) <(sudo lsof $dir/* | awk '(NR>1) {print $9}'|sort) | xargs -I {} mv {} $move_dir

I'm not sure of race conditions but it doesn't matter in my case. The files are opened once for writing and then should be closed until moved.

share|improve this answer

It's what fuser is meant for.

$ fuser access.log
/var/log/apache2/access.log: 2132 15456 16414 19555 19622
$ fuser access.log 2>/dev/null
 2132 15456 16414 19555 19622
$ if [ ! -z "$(fuser access.log 2>/dev/null)" ]
> then
>   echo "this file is in use"
> else
>   echo "this file is not in use"
> fi
this file is in use
share|improve this answer

You must log in to answer this question.

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