【问题标题】:bash tail the newest file in folder without variablebash尾文件夹中的最新文件没有变量
【发布时间】:2015-12-06 14:12:49
【问题描述】:

我在一个文件夹中有一堆日志文件。当我 cd 进入文件夹并查看文件时,它看起来像这样。

$ ls -lhat
-rw-r--r--   1 root root 5.3K Sep 10 12:22 some_log_c48b72e8.log
-rw-r--r--   1 root root 5.1M Sep 10 02:51 some_log_cebb6a28.log
-rw-r--r--   1 root root 1.1K Aug 25 14:21 some_log_edc96130.log
-rw-r--r--   1 root root 406K Aug 25 14:18 some_log_595c9c50.log
-rw-r--r--   1 root root  65K Aug 24 16:00 some_log_36d179b3.log
-rw-r--r--   1 root root  87K Aug 24 13:48 some_log_b29eb255.log
-rw-r--r--   1 root root  13M Aug 22 11:55 some_log_eae54d84.log
-rw-r--r--   1 root root 1.8M Aug 12 12:21 some_log_1aef4137.log

我想查看最新日志文件中的最新消息。我现在可以手动复制最新日志的名称,然后对其执行尾部操作,这样就可以了。

$ tail -n 100 some_log_c48b72e8.log 

这确实涉及体力劳动,所以我想使用 bash-fu 来做这件事。

我目前找到了这种方法;

filename="$(ls -lat | sed -n 2p |  tail -c 30)"; tail -n 100 $filename

它有效,但我很沮丧,我需要将数据保存到一个变量中才能做到这一点。是否可以在 bash 中执行此操作而不将中间结果保存到变量中?

【问题讨论】:

  • 最好的解决方案是让您的记录器为日志文件使用基于时间戳的名称,并为最近的文件使用固定名称,如current。 (或具有固定名称的符号链接到最新的。)

标签: linux bash tail


【解决方案1】:
tail -n 100 "$(ls -at | head -n 1)"

您不需要ls 来实际打印 时间戳,您只需要按它们排序 (ls -t)。我添加了 -a 选项,因为它在您的原始代码中,但请注意,除非您的日志文件是“点文件”,即以 . 开头(它们不应该),否则这不是必需的。

以这种方式使用ls 可以避免使用sedtail -c 解析输出。 (还有you should not try to parse the output of ls。)只需选择列表中的第一个文件(head -n 1),它是最新的。将其放在引号中应该可以使您避免更常见的“问题”,例如文件名中的空格。 (如果您的文件名中有换行符或类似内容,修正您的文件名。:-D)

您可以就地使用命令替换,而不是保存到变量中。

【讨论】:

  • 一般来说,依赖ls的输出不是很推荐,但在这种情况下,这似乎是一个不错的选择。你也可以选择find . -type f -printf '%T@ %p\n' | sort -n | tail -1
  • @fedorqui:您可以随时依赖ls 的输出。令人沮丧的是解析ls -l 的输出。是的,find 是整体上更强大的工具,我只能推荐学习它,但在这种情况下,简单的ls 提供了更清洁的解决方案。
  • Why you shouldn't parse the output of ls(1)Unix 允许文件名中的几乎任何字符,包括空格、换行符、逗号、管道符号,以及几乎任何你曾经尝试用作分隔符的其他字符,除了NUL 然后 _ 在其默认模式下,如果标准输出不是终端,则 ls 用换行符分隔文件名_。所以一般是ls,而不仅仅是ls -l
  • @fedorqui:啊……看,保持你的文件系统没有“有趣”的名字,显然随着时间的推移会产生一些盲点。 :-D
  • 哈哈,我也从未创建过名称中包含新行的文件 :) 但是最好提及这些情况,以防止命令停止工作时产生奇怪的感觉。
【解决方案2】:

你也可以试试这个方法

ls -1t  | head -n 1  | xargs tail -c 50

说明:

ls   -1rht     -- list the files based on modified time in reverse order.
tail -n 1      -- get the last one file 
tail -c 50     -- show the last 50 character from the file.

【讨论】:

  • ls -1rht... 删除-1ls 仅在打印到终端时将输出列化,输出到管道无论如何都是一行)。删除-r 并使用head 而不是tail。删除-h,因为您没有打印 时间戳(-l),因此您不需要告诉ls 以人类可读的方式打印它们。 ;-)
【解决方案3】:

如果不解析ls,您将使用stat

tail -n 100 "$(stat -c "%Y %n" * | sort -nk1,1 | tail -1 | cut -d" " -f 2-)"

如果您的文件名包含换行符,将会中断。


版本 2:换行符没问题

tail -n 100 "$(
    stat --printf "%Y:%n\0" * | 
    sort -z -t: -k1,1nr | 
    { IFS=: read -d '' time filename; echo "$filename"; }
)"

【讨论】:

  • 如果它与包含换行符的文件名中断,那真的比只使用ls更好。
【解决方案4】:

真正的ls-free 解决方案:

tail -n 100 < <(
  for f in *; do
    [[ $f -nt $newest ]] && newest=$f
  done
  cat "$newest"
  )

不需要初始化newest,因为任何文件都会比空字符串命名的空文件更新。

这有点冗长,但保证可以使用任何合法的文件名。将其保存到 shell 函数中以便于使用:

tail_latest () {
  dir=${1:-.}
  size=${2:-100}
  for f in "$dir"/*; do
      [[ $f -nt $newest ]] && newest=$f
  done
  tail -f "$size" "$newest"
}

一些例子:

# Default of 100 lines from newest file in the current directory
tail_latest
# 200 lines from the newest file in another directory
tail_latest /some/log/dir 200

zsh 的插件:glob 限定符让您可以直接对 glob 的结果进行排序,从而更容易获得最新文件。

tail -n 100 *(om[1,1])

om 按修改时间(最新的优先)对结果进行排序。 [1,1] 限制匹配第一个文件的范围。 (我认为Y1 应该这样做,但它一直给我一个“未知文件属性”错误。)

【讨论】:

    猜你喜欢
    • 2014-10-05
    • 2012-08-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-08
    • 2018-10-17
    • 1970-01-01
    • 2015-02-08
    相关资源
    最近更新 更多