打印文件夹中所有文件的第一行和最后一行

打印文件夹中所有文件的第一行和最后一行

我有一堆被覆盖的日志文件(file.log.1file.log.2)。当我将它们从设备复制到本地计算机上时,我丢失了原始时间戳。所以我想把它们按时间顺序排列。问题是我不一定知道哪个是最新的,哪个是最旧的。

我想要做的是,如果所有日志都在一个目录中,则打印如下内容:

file: file.log.1
first line: [first line that isn't whitespace]
last line: [last line that isn't whitespace]

我可以编写一个 Python 脚本来执行此操作,但如果可能的话,我更愿意使用 Linux 内置脚本来执行此操作。这是 awk/sed 的工作吗?或者这真的更适合脚本语言吗?如果答案是肯定的,那么 awk/sed 会怎么做?

我通过搜索找到了这个 awk 命令,但它只接受一个文件名,并且会打印最后一行的内容(并且末尾可能有可变数量的空行)

awk 'NR == 1 { print }END{ print }' filename

答案1

所以我喜欢sed这个答案

for file in file.log.*
do
   echo "file: $file"
   echo -n "first line: "
   cat "$file" | sed -n '/^\s*$/!{p;q}'
   echo -n "last line: "
   tac "$file" | sed -n '/^\s*$/!{p;q}'
done

答案2

awk 命令:

awk -v OFS=: '
    FNR==1 {
        # the last non-blank line from the previous file
        if (line) {print filename, fnr, line}
        filename=FILENAME
        line=""
        p=0
    } 
    /^[[:blank:]]*$/ {next} 
    !p {
        # the first non-blank line
        print FILENAME, FNR, $0; p=1
    }
    {fnr=FNR; line=$0} 
    END {print filename, fnr, line}
' *

对于每个文件,打印文件名、行号和行,以冒号分隔。

GNU awk v4 有 BEGINFILE 和 ENDFILE 模式,这大大简化了事情:

gawk -v OFS=: '
    BEGINFILE {p=0} 
    /^[[:blank:]]*$/ {next} 
    !p {print FILENAME, FNR, $0; p=1}
    {fnr=FNR; line=$0} 
    ENDFILE {print FILENAME, fnr, line}
' *

答案3

尝试:

awk -F'\n' -vRS="" '
  {
    print "file: " FILENAME;
    gsub(/\n[[:blank:]]+|[[:blank:]]+\n/,"");
    print "first line: " $1;
    print "last line: " $NF;
  }
' file.log.*

答案4

什么?没有 Perl 吗?

for file in file.log.*; do 
    echo "FILE: $file"; 
    perl -ne 'if(/\S/){$k++; $l=$_}; 
              print "First line: $_" if $k==1; 
              END{print "Last line: $l\n"}' "$file";  
done

解释

  • for file in file.log.*:遍历file.log.当前目录中所有名称以 开头的文件,并将每个文件保存为$file.
  • echo "FILE: $file";:打印文件名。
  • perl -ne:逐行读取当前输入文件(-n),将每一行保存为特殊的 Perl 变量,并在其上$_运行 给定的脚本。-e
  • if(/\S/){$k++; $l=$_}:如果当前行匹配非空白字符 ( \S),则将该行保存为$l并将计数器加$k一。
  • print "First line: $_" if $k==1;:如果是,则打印当前行( $_) 。这将打印第一个非空白行。$k1
  • END{print "Last line: $l\n"}:读取所有输入行后执行。由于我们将每个非空白行保存为$l,因此在文件末尾,$l将是最后一个非空白行。因此,这将打印最后一行。

另一种方法:

for file in file.log.*; do 
    printf "FILE: %s\nFirst line: %s\nLast line: %s\n\n" \
        "$file" \
        "$(grep -Em 1 '\S' "$file")" \
        "$(tac "$file" | grep -Em1 '\S' )"; 
done

解释

for与我们仅在此处用于printf打印三个字符串的循环相同。文件名以及这两个命令的输出:

  • grep -Pm 1 '\S' "$file"-E激活扩展正则表达式,让我们可以使用\S“非空白”。意思-m1是“找到第一个匹配项后退出”。
  • tac "$file" | grep -Em1 '\S':tac是 的倒数cat。它将打印文件的内容,但从最后一行到第一行。因此,该命令将打印最后一个非空白行。

相关内容