【问题标题】:Linux Shell - String manipulation then calculating age of file in minutesLinux Shell - 字符串操作,然后在几分钟内计算文件的年龄
【发布时间】:2013-05-21 04:03:01
【问题描述】:

我正在编写一个脚本来计算目录中最旧文件的年龄。运行的第一个命令是:

OLDFILE=`ls -lt $DIR | grep "^-" | tail -1 `    
echo $OLDFILE

输出包含的不仅仅是文件名。例如

-rwxrwxr-- 1 abc abc 334 May 10 2011 ABCD_xyz20110510113817046.abc.bak

第一季度/。如何在上述行的最后一个空格之后获得输出?这会给我文件名。我意识到需要进行某种字符串操作,但对 shell 脚本还是陌生的。

第二季度/。如何以分钟为单位获取此文件的年龄?

【问题讨论】:

  • How do I obtain the age of this file in minutes 是什么意思?
  • 补充@Bill 的评论 - 在大多数 Unix/Linux 文件系统上,不存储文件的创建时间。因此,您可以找到上次更改文件数据的时间(其mtime)或上次更改有关文件的元数据(权限、大小、所有者等)(其ctime),并且有时,最后一次读取文件(其 atime) - 尽管出于性能原因,atime 通常要么完全禁用,要么仅近似。

标签: linux string shell time


【解决方案1】:

要获取最早的文件名,

ls -lt | awk '/^-/{file=$NF}END{print file}'

但是,如果您的文件名称中包含空格等,这并不可靠。通常,您应该尝试avoid parsing the output from ls

使用stat,您可以获得机器可读格式的文件创建日期,以自 1970 年 1 月 1 日以来的秒数表示;使用date +%s,您可以获得相同格式的当前时间。减除以 60。(更多的 Awk 技能会在算术中派上用场。)

最后,对于替代解决方案,请查看find 的选项;特别是,它的printf 格式字符串允许您提取文件的年龄。以下将直接为您获取最旧文件的年龄(以秒为单位)和 inode 号:

find . -maxdepth 1 -type f -printf '%T@ %i\n' |
sort -n | head -n 1

使用 inode 号可以避免文件名搞笑的问题;一旦你有一个 inode,将它转换为文件名是很容易的:

find . -maxdepth 1 -inum "$number"

将两者结合在一起,你可能想要这样的东西:

# set -- Replace $@ with output from command
set -- $(find . -maxdepth 1 -type f -printf '%T@ %i\n' |
         sort -n | head -n 1)
# now $1 is the timestamp and $2 is the inode

oldest_filename=$(find . -maxdepth 1 -inum "$2")
age_in_minutes=$(date +%s | awk -v d="$1" '{ print ($1 - d) / 60 }')

【讨论】:

  • 运行find 两次可能会占用一个大目录。
  • ls -lt 会优先选择按字母顺序排列的最后一个文件,如果有多个最旧的文件具有相同的日期戳,而 sort 会倾向于选择最低(最旧?)的 inode 编号,因此它们不是完全等价,而且很难做到。
  • 另外find 将包含点文件,而ls 将不包含。你可以添加-name '[!.]*' 来解决这个问题。
  • 请注意 (Li|U)nix ctime 不是创建时间。它代表“更改时间”,表示上次更改 inode 本身(权限等)的时间,而不是 inode 首次分配给该文件的时间。一些文件系统可能会存储创建时间,但大多数不会。
  • 我的目标是修改时间,但我分心并输入了错误的格式代码。感谢您的评论;更新的答案。
【解决方案2】:

awk 解决方案,以分钟为单位为您提供文件的年龄(因为您的 ls 输出不包含创建的最小值,因此默认情况下假定为 00)。同样正如triplee 指出的那样,ls 输出本身就存在被解析的风险。

[[bash_prompt$]]$ echo $l; echo "##############";echo $l | awk -f test.sh ; echo "#############"; cat test.sh
-rwxrwxr-- 1 abc abc 334 May 20 2013 ABCD_xyz20110510113817046.abc.bak
##############
File ABCD_xyz20110510113817046.abc.bak is 2074.67 min old
#############
BEGIN{
   m=split("Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec",d,"|")
   for(o=1;o<=m;o++){
      date[d[o]]=sprintf("%02d",o)
    }
}
{
month=date[$6];
old_time=mktime($8" "month" "$7" "00" "00" "00);
curr_time=systime();
print "File " $NF " is " (curr_time-old_time)/60 " min old";
}

【讨论】:

  • 也许这是一个练习或 awk 作业?我想不出解析ls数据来找出文件年龄的实际用途
【解决方案3】:

对于第二季度,bash 单线可能是:

let mtime_minutes=\(`date +%s`-`stat -c%Y "$file_to_inspect"`\)/60

【讨论】:

    【解决方案4】:

    你可能想要ls -t。包含“-l”选项明确要求 ls 为您提供您真正想要摆脱的所有其他信息。 但是,您可能真正想要的是首先找到:

    NEWEST=$(find . -maxdepth 1 -type f | cut -c3- |xargs ls -t| head -1)
    

    这将为您提供最新项目的纯文件名,无需其他处理。 目录将被正确排除。 (感谢 Tripleee 指出您的目标。)

    第二个问题可以用stat和bc:

    TIME_MINUTES=$(stat --format %Y/60 "$NEWEST" |bc -l)
    

    如果您只想要整数分钟,请从 bc 中删除“-l”选项。

    【讨论】:

    • 这对于带有换行符的文件名仍然会中断,但我承认这是一个边缘场景。
    • 我想你可以用stat --format '%Y/60'避免不雅的echo
    • @triplee:是的,就我而言,包含特殊字符的文件名太难了。否则我会花另一个步骤来转义任何双引号和 $。我对 bash 半生不熟的引用的耐心是有限度的。感谢您的建议,我已经编辑了。
    猜你喜欢
    • 2021-03-27
    • 1970-01-01
    • 2011-06-10
    • 2014-06-08
    • 1970-01-01
    • 2022-08-11
    • 2012-03-26
    • 2021-02-24
    • 1970-01-01
    相关资源
    最近更新 更多