如何确定文件是否在 FreeBSD 上的 bash 脚本中打开

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

我正在尝试将 Linux bash 脚本移植到 FreeBSD。在决定是否采取某些操作之前,脚本需要检查文件是否打开(用于写入)。

在 Linux 上,使用 fuser 命令很容易:

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

然而,在 FreeBSD 上,fuser 命令似乎完全被破坏了(由 this 证实)并且 return 没有任何正确的退出代码或者实际上没有任何有用的输出到标准输出。例如,在一个正在积极写入的文件上:

# fuser file 
file:

编辑:


Vladimir Botka 的评论:

"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

在我的 FreeBSD 盒子(也是 FreeBSD 12)上,相同的测试产生以下结果:

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

Vladimir Botka 的评论:

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.

这是我对编译后的 C 代码的测试:

# ./a.out
Enter num:

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

因此,fuser似乎确实被破坏了。然而,它似乎只在我的系统上坏了,在 Vladimir Botka 的系统上没有,这很奇怪,因为它们都是 FreeBSD 12。


我似乎可以使用 lsoffstat 来获取此信息,但必须对输出进行一些复杂的解析,这会使我的脚本更加复杂。我想知道是否有人可以向我指出一个简单的 'yes/no' 命令来确定是否正在使用像 fuser 这样的文件,它可以在 FreeBSD 上运行?

您的主张

"on FreeBSD the fuser command seems totally broken"

指的是 Port details: fuser POSIX fuser utility for FreeBSD,已弃用并过期于:2012-11-26

让我们在 FreeBSD 12.0 中尝试这个简单的测试。打开终端并 运行

# touch /scratch/test-file
# tail -f /scratch/test-file

保持 tail 等待 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

让我们尝试使用一个简单的 C 程序测试写入文件,该程序打开文件、等待输入并将输入写入文件。

#include <stdio.h>
#include <stdlib.h>
int main()
{
   int num;
   FILE *fptr;
   fptr = fopen("/scratch/test-file","w");
   printf("Enter num: ");
   scanf("%d",&num);
   fprintf(fptr,"%d",num);
   fclose(fptr);
   return 0;
}

# ./a.out 
Enter num: 

保持程序等待输入。打开第三个终端和 运行

# fuser /scratch/test-file
/scratch/test-file: 45448w 45042
# echo $?
0
# ps ax | grep 45448
45448  0  I+       0:00.00 ./a.out
45464  1  S+       0:00.00 grep 45448

如果打开文件进行写入 fuser 将 "w" 附加到 PID 并报告

w ... The file is open for writing.

简单bash脚本

#!/usr/local/bin/bash
my_file=/scratch/test-file
result=`fuser $my_file 2>&1`
pid=`echo $result | cut -d ':' -f 2-`
if [ -z "$pid" ]; then
    echo "$my_file is not opened by any process"
else
    echo "$my_file is opened by process PID(s): $pid"
fi

显示

# ./test.bash
/scratch/test-file is opened by process PID(s): 45448w 45042

分别

# ./test.bash
/scratch/test-file is not opened by any process

如果这对您不起作用,请参阅 How to create a Minimal, Complete, and Verifiable example 以提供详细信息。

如果 fuser 在您的系统中不工作,并且可以进行一些解析,请尝试 fstat,例如,在打开文件时:

$ cat > /tmp/test

这是fstat /tmp/test的输出:

# fstat /tmp/text
USER     CMD          PID   FD MOUNT      INUM MODE         SZ|DV R/W NAME
root     cat        39590    1 [restricted]   3812 -rw-r--r--       0  w  /tmp/text

如果文件未被使用,您将只会得到 headers:

# fstat /tmp/text
USER     CMD          PID   FD MOUNT      INUM MODE         SZ|DV R/W NAME

那么你可以使用类似的东西:

[ `fstat /tmp/test | wc -l` -gt 1 ] && echo "in use"

test `fstat /tmp/test | wc -l` -gt 1 && echo "in use"

我的结论是 fuser 在 FreeBSD 中有问题。最后,我对 fstat 输出进行了解析以解决问题。 fstat 在正在写入的文件上产生以下输出(还有另一个进程读取它,因此有两行):

# 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

以下 bash 代码使用 fstat 测试文件,删除 header 行 (sed),然后使用 awk 获取第 9 个列,与文件是否打开以供读取或写入有关。然后 grep 在任何行中查找 w 写标志。如果找到一个,它将 return 为真。整个条件被否定 (!) 以确保仅在文件未使用时才执行操作:

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

您所描述的 'broken' 也可能是正确的行为,具体取决于进程所有者的权限。

您只能通过 fuser 查看您自己的进程,除非您拥有 root 或同等权限并且可以查看其他用户的进程。确保您 运行 两个会话都是同一个 / root 用户。