如何确定文件是否在 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。
我似乎可以使用 lsof
或 fstat
来获取此信息,但必须对输出进行一些复杂的解析,这会使我的脚本更加复杂。我想知道是否有人可以向我指出一个简单的 '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 用户。
我正在尝试将 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。
我似乎可以使用 lsof
或 fstat
来获取此信息,但必须对输出进行一些复杂的解析,这会使我的脚本更加复杂。我想知道是否有人可以向我指出一个简单的 '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 用户。