我有一个文件,其中包含一些关于日常存储利用率的信息。每天有两列-DD.MM日期和使用量(KB(。
我使用awk来显示随着存储使用量的增加,第二行与前一行之间的差异(以GB为单位(。
示例文件:
20.09 10485760
21.09 20971520
22.09 26214400
23.09 27262976
我的awk命令:
awk 'NR > 1 {a=($2-prev)/1024^2" GB"} {prev=$2} {print $1,$2,a}' file
该输出:
20.09 10485760
21.09 20971520 10 GB
22.09 26214400 5 GB
23.09 27262976 1 GB
我还想在第一列之前添加工作日名称。文件中的日期格式始终是DD.MM,因此,为了使GNU日期接受它作为有效输入并返回工作日名称,我编写了以下管道:
echo '20.09.2022' | awk -v FS=. -v OFS=- '{print $3,$2,$1}' | date -f - +%a
它是有效的,但我想从第一个awk开始为每个处理过的行调用它,第一列日期作为参数.2022";附加到它以便工作,并将此外部管道的输出(它将是工作日名称(放在第一列的日期之前。
示例输出:
Tue 20.09 10485760
Wed 21.09 20971520 10 GB
Thu 22.09 26214400 5 GB
Fri 23.09 27262976 1 GB
我查看了awk中的system()
选项,但我无法使用我的管道和我的第一个awk命令。
第一个解决方案:
使用awk
中的getline
,请尝试以下解决方案。
awk '
NR>1{
a=($2-prev)/1024^2" GB"
}
{
split($1,arr,".")
value="2022-"arr[2]"-"arr[1]
dateVal="date -d "" value "" +%a"
newVal = ( (dateVal | getline line) > 0 ? line : "N/A" )
close(dateVal)
print newVal,$0,a
prev=$2
}
' Input_file
第二个解决方案:使用您显示的样本,请尝试以下awk
代码。system
命令在awk
中的作用是:它在一个单独的shell中运行上述命令,所以基本上您调用的是awk
->system
->shell
->commands
,所以尽管如此,我们只需在所有日子里用1个awk
获取所有值(基于Input_file的第一个字段(,我们可以将其作为输入传递给另一个awk
,在那里我们正在进行实际空间计算,我们可以合并这两个值(因为system
命令通过shell命令打印输出,所以我们不能将该输出与awk
的输出合并(。我们也可以使用while循环,但IMHO使用awk
可能会更快。
awk '
FNR==NR{
arr[FNR]=$0
next
}
NR>1{
a=($2-prev)/1024^2" GB"
}
{
print arr[FNR],$1,$2,a
prev=$2
}
' <(awk '{split($1,arr,".");system("d="2022-" arr[2]"-"arr[1]"";date -d "$d" +%a")}' Input_file) Input_file
所示样品的输出如下:
Tue 20.09 10485760
Wed 21.09 20971520 10 GB
Thu 22.09 26214400 5 GB
Fri 23.09 27262976 1 GB
既然你有GNU日期,你也应该有GNU awk,它有内置的时间函数,比awk快几个数量级,为每个输入行生成一个调用date
的子shell:
$ cat tst.sh
#!/usr/bin/env bash
awk '
BEGIN {
year = strftime("%Y")
}
NR > 1 {
diff = ( ($2 - prev) / (1024 ^ 2) ) " GB"
}
{
split($1,dayMth,/[.]/)
secs = mktime(year " " dayMth[2] " " dayMth[1] " 12 0 0")
day = strftime("%a",secs)
print day, $0, diff
prev = $2
}
' "${@:--}"
$ ./tst.sh file
Tue 20.09 10485760
Wed 21.09 20971520 10 GB
Thu 22.09 26214400 5 GB
Fri 23.09 27262976 1 GB
如果由于某种原因,您没有GNU awk并且无法获得它,那么使用GNU日期和任何awk:,这种两步方法将相当有效
$ cat tst.sh
#!/usr/bin/env bash
awk -v year="$(date +'%Y')" -v OFS='-' '{
split($1,dayMth,/[.]/)
print year, dayMth[2], dayMth[1]
}' "$@" |
date -f- +'%a' |
awk '
NR == FNR {
days[NR] = $1
next
}
FNR > 1 {
diff = ( ($2 - prev) / (1024 ^ 2) ) " GB"
}
{
print days[FNR], $0, diff
prev = $2
}
' - "$@"
$ ./tst.sh file
Tue 20.09 10485760
Wed 21.09 20971520 10 GB
Thu 22.09 26214400 5 GB
Fri 23.09 27262976 1 GB
第二个脚本的缺点是它不能从流中读取输入,只能从文件中读取,因为它必须读取两次。如果这是一个问题,并且您的输入量不太大,无法在磁盘上放置副本,那么您可以始终使用临时文件,例如:
$ cat tst.sh
#!/usr/bin/env bash
tmp=$(mktemp) &&
trap 'rm -f "$tmp"; exit' 0 &&
cat "${@:--}" > "$tmp" || exit 1
awk -v year="$(date +'%Y')" -v OFS='-' '{
split($1,dayMth,/[.]/)
print year, dayMth[2], dayMth[1]
}' "$tmp" |
date -f- +'%a' |
awk '
NR == FNR {
days[NR] = $1
next
}
FNR > 1 {
diff = ( ($2 - prev) / (1024 ^ 2) ) " GB"
}
{
print days[FNR], $0, diff
prev = $2
}
' - "$tmp"
$ ./tst.sh file
Tue 20.09 10485760
Wed 21.09 20971520 10 GB
Thu 22.09 26214400 5 GB
Fri 23.09 27262976 1 GB
date
可以处理多个换行剪切日期,因此我提出以下解决方案,让file.txt
的内容为
20.09 10485760
21.09 20971520 10 GB
22.09 26214400 5 GB
23.09 27262976 1 GB
然后
awk 'BEGIN{FS="[[:space:].]";OFS="-"}{print "2022",$2,$1}' file.txt | date -f - +%a | paste -d ' ' - file.txt
给出输出
Tue 20.09 10485760
Wed 21.09 20971520 10 GB
Thu 22.09 26214400 5 GB
Fri 23.09 27262976 1 GB
说明:我使用GNUAWK
提取并准备日期供date
使用,因此20.09
变为2022-09-20
等等,然后date
用于计算星期几的代号,然后CCD26用于获得按空格字符并排剪切的列,第一列为-
,表示使用标准输入,第二列为不变的file.txt
(在GNU Awk 5.0.1中测试并粘贴(GNU coreutils(8.30(
谁说你不能使用system()
来获得工作日?
-
此功能还配有自动
gnu-date
与bsd-date
检测,(由于
gnu-date
能够返回纳秒精度,这是bsd-date
所缺乏的(,并相应地调整其调用语法
jot -w '2022-09-%d' 30 | gtail -n 12 | mawk 'function ____(_) { return substr("SunMonTueWedThuFriSat",(_= system("exit 140 date -" ( system("exit 140date +"%s%6N" "" |grep -cF N140") ? "j -f " ""%Y-%m-%d"":"d") " ""(_) "" +%w 140")) +_+_+(_^=_<_),_+_+_) } ($++NF=____($!_))^_'
2022-09-19 Mon
2022-09-20 Tue
2022-09-21 Wed
2022-09-22 Thu
2022-09-23 Fri
2022-09-24 Sat
2022-09-25 Sun
2022-09-26 Mon
2022-09-27 Tue
2022-09-28 Wed
2022-09-29 Thu
2022-09-30 Fri
如果您明确地将system()
的退出代码设置为您想要的任何值,则CCD_34通常可以返回一个从0
到255
的无符号整数,
因此,只要所需的值的范围在256
内(或可以合并到其中(,则可以利用system()
并比完整的getline
例程更快地获得结果。
但是,由于此解决方法需要返回数值,因此无法直接使用内置格式代码date +'%a'
。