Search code examples
bashfreebsdfuser

How to determine whether a file is open in a bash script on FreeBSD


I'm trying to port a Linux bash script to FreeBSD. The script needs to check if a file is open (for writing) before deciding whether or not to take some action.

On Linux, this was easy using the fuser command:

if [[ ! `/usr/bin/fuser "$FILE"` ]]
     then
     ...
fi

However, on FreeBSD the fuser command seems totally broken (borne out by this) and doesn't return any proper exit codes or indeed any useful output to stdout. For example, on a file which is actively being written to:

# fuser file 
file:

Edit:


Vladimir Botka's comment:

"Simple test in FreeBSD 12.0 shows":

# tail -f /scratch/test-file`
# fuser /scratch/test-file
/scratch/test-file: 45042
# echo $?
0
# ps ax | grep 45042
45042  0  I+       0:00.00 tail -f /scratch/test-file
45232  1  R+       0:00.00 grep 45042

On my FreeBSD box (also FreeBSD 12), the same test yields the following:

# tail -f /scratch/test-file
# fuser /scratch/test-file
/scratch/test-file:
# echo $?
0

Vladimir Botka's comment:

Let's try and test the writing to a file with a simple C program which opens the file, waits for an input and writes the input to the file.

Here is my test on the compiled C code:

# ./a.out
Enter num:

# fuser /scratch/test-file
/scratch/test-file:
# echo $?
0

Therefore, fuser does seem to be broken. However, it only seems broken on my system, not on Vladimir Botka's system, which is strange as they're both FreeBSD 12.


It seems I could use lsof or fstat to get this information but not without some complex parsing of the output which is going to make my script more complicated. I wondered if anyone can point me towards a simple 'yes/no' command to determine whether a file is in use like fuser that will work on FreeBSD?


Solution

  • I conclude that fuser is broken in FreeBSD. In the end I went with a parse of the fstat output to solve the problem. fstat produces the following output on a file which is being actively written (and there is also another process reading it, hence the two lines):

    # fstat file 
    USER     CMD          PID   FD MOUNT      INUM MODE         SZ|DV R/W NAME
    root     smbd       36299   41 /data    1282756 -rwxr--r--  7407140864 rw  file
    root     smbd       36295   30 /data    1282756 -rwxr--r--  7407140864  r  file
    

    The following bash code tests the file using fstat, removes the header line (sed), then uses awk to grab the 9th column, that which is concerned with whether the file is open for reading or writing. A grep then follows to look for the w write flag in any of the lines. If it finds one, it will return true. The whole condition is negated (!) to ensure that the action is only carried out if the file is NOT in use:

     if [[ ! `/usr/bin/fstat "$FILE" | sed 1d | awk '{print $9}' | grep "w"` ]]
     then
        ... do something ...
     fi