我有一堆被覆盖的日志文件(file.log.1
等file.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;
:如果是,则打印当前行($_
) 。这将打印第一个非空白行。$k
1
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
。它将打印文件的内容,但从最后一行到第一行。因此,该命令将打印最后一个非空白行。