如何通过 linux 脚本识别文件是否仍在写入或完成
How to identify if file is still written or completed through linux script
我们有一个正在生成文件的系统,我想通过脚本检查许多文件中哪些文件已完成并且在过去两分钟内没有被修改,最后重命名这些文件。
这是我试过的,但结果不正确。有人可以帮忙吗?
for file in /home/test/*abc_YYYYMMDDhhmmss*
do
f1=`basename $file`
if [ lsof | grep "$f1" = "" ];then
if [ `stat --format=%Y $file` -le $(( `date +%s` - 300 )) ]; then
mv "$f1" "${f1}_Complete"
else
echo "no files to collect"
fi
done
这样怎么样?
#!/bin/bash
find /home/test/* -type f -mmin +2 -print0 |
while IFS= read -r -d '' line; do
echo $line
fuser -s "$line"
mv "$line" "${line}_Completed"
done
-mmin +2
表示最近2分钟内没有修改过的文件。
编辑:应要求,我已将其更改为check if the file is currently open。 fuser -s "$line"
如果文件当前打开,行将退出,否则它将继续移动文件。我还将变量用引号括起来,感谢您的提醒
您犯了一个常见的错误,即假设 [
是 if
命令语法的一部分;但它不是:[
只是另一个命令。 if
语句的语法是
if commands; then
: what to do if the exit code from commands was 0
else
: what to do if not
fi
其中 commands
可以是任意复杂的命令序列,序列中最后一个命令的退出代码决定采用哪个分支; else
分支是可选的。
作为最小修复,更改为
# use modern $(command substitution) syntax
# instead of obsolescent `command substitution`;
# always quote variables with file names
f1=$(basename "$file")
# Remove [ and switch to grep -q;
# add -F to grep flags for literal matching
if ! lsof | grep -Fq "$f1"; then
无论如何,像这样的东西怎么样?
find $(lsof |
awk 'NR==FNR { if ( ~ /^\/home\/test\//) a[]++; next }
FNR == 1 {
if (! (FILENAME in a)) print FILENAME;
next }' - /home/test/*abc_YYYYMMDDhhmmss*) \
-type f -mmin +2 -exec sh -c '
for file; do
mv "$file" "${file}_Complete"
done' _ {} +
这很复杂,但这里有一个纲要。
lsof | awk ...
从通配符匹配中打印出未打开的文件。
- 这假定文件是常规文本文件 - 一些 Awk 变体在处理二进制输入文件时遇到问题。如果它有问题,重构它以避免这种约束可能不会太难。
- 更详细地说,Awk 的第一个参数是
-
即标准输入,它从 lsof
读取管道。对于第一个输入文件,条件 NR==FNR
为真;我们只是将打开的文件收集到关联数组 a
中。然后第二个条件打印当前输入文件的名称,如果它不在数组中;这是对剩余的输入文件执行的,即与通配符匹配的文件。
- 这是作为
find
检查的路径传递的;它将查找最近两分钟内修改过的所有文件,并将结果传递给 -exec
. 中的命令
-exec
中简单的shell脚本应该很容易理解。 find
将找到的文件作为命令行参数传递,但 sh -c
从 [=29=]
填充它们,因此我们传入一个虚拟 _
以将文件名推入 </code>, <code>
等等,如果你不给它一个参数列表,for
就会循环。
如果您的文件名包含换行符,这可能不起作用;那么你还需要更复杂的东西。
在 Bourne-family shells 中遍历任意文件名令人失望地复杂,并且在列表中查找元素 not 在 [=69= 中总是有点讨厌] 脚本。 Ksh 和 Bash 提供了一些缓解,因为它们有数组,但这不能移植到 POSIX sh
/ ash
/ dash
.
我们有一个正在生成文件的系统,我想通过脚本检查许多文件中哪些文件已完成并且在过去两分钟内没有被修改,最后重命名这些文件。
这是我试过的,但结果不正确。有人可以帮忙吗?
for file in /home/test/*abc_YYYYMMDDhhmmss*
do
f1=`basename $file`
if [ lsof | grep "$f1" = "" ];then
if [ `stat --format=%Y $file` -le $(( `date +%s` - 300 )) ]; then
mv "$f1" "${f1}_Complete"
else
echo "no files to collect"
fi
done
这样怎么样?
#!/bin/bash
find /home/test/* -type f -mmin +2 -print0 |
while IFS= read -r -d '' line; do
echo $line
fuser -s "$line"
mv "$line" "${line}_Completed"
done
-mmin +2
表示最近2分钟内没有修改过的文件。
编辑:应要求,我已将其更改为check if the file is currently open。 fuser -s "$line"
如果文件当前打开,行将退出,否则它将继续移动文件。我还将变量用引号括起来,感谢您的提醒
您犯了一个常见的错误,即假设 [
是 if
命令语法的一部分;但它不是:[
只是另一个命令。 if
语句的语法是
if commands; then
: what to do if the exit code from commands was 0
else
: what to do if not
fi
其中 commands
可以是任意复杂的命令序列,序列中最后一个命令的退出代码决定采用哪个分支; else
分支是可选的。
作为最小修复,更改为
# use modern $(command substitution) syntax
# instead of obsolescent `command substitution`;
# always quote variables with file names
f1=$(basename "$file")
# Remove [ and switch to grep -q;
# add -F to grep flags for literal matching
if ! lsof | grep -Fq "$f1"; then
无论如何,像这样的东西怎么样?
find $(lsof |
awk 'NR==FNR { if ( ~ /^\/home\/test\//) a[]++; next }
FNR == 1 {
if (! (FILENAME in a)) print FILENAME;
next }' - /home/test/*abc_YYYYMMDDhhmmss*) \
-type f -mmin +2 -exec sh -c '
for file; do
mv "$file" "${file}_Complete"
done' _ {} +
这很复杂,但这里有一个纲要。
lsof | awk ...
从通配符匹配中打印出未打开的文件。- 这假定文件是常规文本文件 - 一些 Awk 变体在处理二进制输入文件时遇到问题。如果它有问题,重构它以避免这种约束可能不会太难。
- 更详细地说,Awk 的第一个参数是
-
即标准输入,它从lsof
读取管道。对于第一个输入文件,条件NR==FNR
为真;我们只是将打开的文件收集到关联数组a
中。然后第二个条件打印当前输入文件的名称,如果它不在数组中;这是对剩余的输入文件执行的,即与通配符匹配的文件。
- 这是作为
find
检查的路径传递的;它将查找最近两分钟内修改过的所有文件,并将结果传递给-exec
. 中的命令
-exec
中简单的shell脚本应该很容易理解。find
将找到的文件作为命令行参数传递,但sh -c
从[=29=]
填充它们,因此我们传入一个虚拟_
以将文件名推入</code>, <code>
等等,如果你不给它一个参数列表,for
就会循环。
如果您的文件名包含换行符,这可能不起作用;那么你还需要更复杂的东西。
在 Bourne-family shells 中遍历任意文件名令人失望地复杂,并且在列表中查找元素 not 在 [=69= 中总是有点讨厌] 脚本。 Ksh 和 Bash 提供了一些缓解,因为它们有数组,但这不能移植到 POSIX sh
/ ash
/ dash
.