使用 bash 解析“uptime”的输出

使用 bash 解析“uptime”的输出

我想将uptime命令的输出保存到 Bash 脚本中的 csv 文件中。由于该uptime命令根据自上次重启以来的时间具有不同的输出格式,因此我想出了一个基于的相当繁琐的解决方案case,但肯定有更优雅的方法来实现这一点。

正常运行时间输出:

 8:58AM  up 15:12, 1 user, load averages: 0.01, 0.02, 0.00

期望结果:

15:12,1 user,0.00 0.02 0.00,

当前代码:

case "`uptime | wc -w | awk '{print $1}'`" in
#Count the number of words in the uptime output

10)
    #e.g.:  8:16PM  up  2:30, 1 user, load averages: 0.09, 0.05, 0.02
    echo -n `uptime | awk '{ print $3 }' | awk '{gsub ( ",","" ) ; print $0 }'`","`uptime | awk '{ print $4,$5 }' | awk '{gsub ( ",","" ) ; print $0 }'`","`uptime | awk '{ print $8,$9,$10 }' | awk '{gsub ( ",","" ) ; print $0 }'`","
    ;;

12)
    #e.g.: 1:41pm  up 105 days, 21:46,  2 users,  load average: 0.28, 0.28, 0.27
    echo -n `uptime | awk '{ print $3,$4,$5 }' | awk '{gsub ( ",","" ) ; print $0 }'`","`uptime | awk '{ print $6,$7 }' | awk '{gsub ( ",","" ) ; print $0 }'`","`uptime | awk '{ print $10,$11,$12 }' | awk '{gsub ( ",","" ) ; print $0 }'`","
    ;;

13)
    #e.g.: 12:55pm  up 105 days, 21 hrs,  2 users,  load average: 0.26, 0.26, 0.26
    echo -n `uptime | awk '{ print $3,$4,$5,$6 }' | awk '{gsub ( ",","" ) ; print $0 }'`","`uptime | awk '{ print $7,$8 }' | awk '{gsub ( ",","" ) ; print $0 }'`","`uptime | awk '{ print $11,$12,$13 }' | awk '{gsub ( ",","" ) ; print $0 }'`","
    ;;
esac

答案1

我建议您直接转到源代码而不是解析 uptime(1) 输出。

  • 正常运行时间/proc/uptime
  • 平均负载/proc/loadavg
  • 用户数量稍微复杂一些,请参见维基百科:utmp,但w(1)who(1)命令会帮助您。

以下并不完全是您所期望的输出,但您可以明白我的意思:

$ echo $(cut -d ' ' -f 1 </proc/uptime),$(w -h | wc -l),$(cut -d ' ' -f 1-3 </proc/loadavg),
8545883.49,4,0.00 0.01 0.05

这意味着 8.55e6 秒(将近 99 天)、4 个用户、平均负载。

答案2

以下代码适用于所有类型的正常运行时间输出。希望这对您有所帮助,

uptime=`uptime`
upt=`echo $uptime | grep -ohe 'up .*user*' | awk '{gsub ( "user*","" ); print $0 }' | sed 's/,//g' | sed -r 's/(\S+\s+){1}//' | awk '{$NF=""}1'`
usrs=`echo $uptime | grep -ohe '[0-9.*] user[s,]'| sed 's/,//g'`
ldt=`echo $uptime | grep -ohe 'load average[s:][: ].*' | sed 's/,//g' | awk '{ print $3" "$4" "$5"," }'`
echo $upt, $usrs, $ldt

答案3

我将添加一种 Awk 方法。首先,我将直接回答您的问题:

uptime | 
awk 'BEGIN {FS=","}                                                 # Use commas as the field separator
     function trim(s,d) {gsub("(^ +| +$|"d")", "", s); return s}    # Function to trim spaces and delete pattern d in input s
     {
        days=trim($1, ".* up ")     # Trim spaces and remove everything before and including " up " on 1st field
        h_m=trim($2)                # Trim spaces on 2nd field
        users=trim($3)              # . . .
        l1=trim($4, ".*: ")
        l2=trim($5)
        l3=trim($6)
        printf "%s %s,%s,%s %s %s,\n", days, h_m, users, l1, l2, l3
     }'

因此,删除大多数不必要的空格、注释和变量、trim重命名为t;并且不剥离(不存在的)尾随空格的单行代码将是:

uptime | awk 'BEGIN{FS=","}function t(s,d){gsub("(^ +|"d")","",s);return s}{printf "%s %s,%s,%s %s %s,\n",t($1,".* up "),t($2),t($3),t($4,".*: "),t($5),t($6)}'

另外还有 sed-only 方法:

uptime | sed -E -e 's/(.* up|[^,]*: )//g' -e 's/, +/,/g' -e 's/(([0-9]+ days),)?([^,]*),/\2 \3,/' -e 's/^ +//' -e 's/([^,]+),([^,]+),([^,]+)$/\1 \2 \3,/'

仅限正常运行时间

对于像我这样只关心此例中正常运行时间的人来说,以下是获取输出的方法,如下所示uptime: 32d 7h 9m (at 13:38:22)。时间、天数、小时和分钟的值存储在变量中,因此您可以printf在最后调整语句以使其按您想要的方式打印。
笔记: 这或多或少是一个示例,说明如何在 Awk 中解析 uptime 的输出以获取要使用的天、小时、分钟和时间变量。这可以扩展以包括 uptime 输出的其余部分。您会发现它有点复杂,因为 uptime 很花哨,它会输出7 min而不是00:07是否检测到等等。如果您只想在较短的命令中使用 uptime,请跳过以下内容。

uptime | 
awk 'BEGIN {FS=","}                             # Use commas as the field separator
     #   Function to trim spaces and delete pattern d in input s
     function trim(s,d) {gsub("(^ +| +$|"d")", "", s); return s}
     #   Function to set hours and mins variables from time in x:xx format (split on : then trim leading 0 from mins)
     function CoSp(s) {split(trim(s), B, /:/); sub(/^0/, "", B[2]); if(B[2]==""){B[2]=0} hours=B[1]; mins=B[2]}
     {
        up=" up ";                              # Set variable for long pattern that is needed frequently
        days=0; hours=0; mins=0                 # Set variables so that printf is more readable
        if ($1 ~ /.*days.*/) {                  # If it has days
            split(trim($1, " days"), A, up)     # Trim spaces and remove " days" on 1st field and split on " up "
            days=A[2]
            if ($2 ~ /hrs/) {                   # If only hours
                hours=trim($2, " hrs")
            } else if ($2 ~ /min/) {            # If only minutes
                mins=trim($2, " min")
            } else {                            # If has hours and minutes
                CoSp($2)
            }
        } else if ($1 ~ /hrs/) {                # If only hours
            split(trim($1, " hrs"), A, up)
            hours=A[2]
        } else if ($1 ~ /min/) {                # If only minutes
            split(trim($1, " min"), A, up)
            mins=A[2]
        } else {
            split(trim($1), A, up)
            CoSp(A[2])
        }
        time=A[1];
        printf "uptime: %sd %sh %sm (at %s)\n", days, hours, mins, time
     }'

如果您不关心捕获时间,则可以将其缩小,但此时最好仅基于以下内容进行计算/proc/uptime(如另一个答案中所述)。这将以以下形式获取正常运行时间uptime: 27d 9h 13m 13s 320ms

awk 'FNR==1{                       # Only process 1st line of file
        seconds=$1
        ms=seconds*1000%1000
        secs=int(seconds%60)
        mins=int(seconds/60)
        hours=int(mins/60)
        days=int(hours/24)
        hours=hours%24
        mins=mins%60
        printf "uptime: %sd %sh %sm %ss %sms\n", days, hours, mins, secs, ms
     }' /proc/uptime

这是实现该目的的一行代码,没有不必要的空格和注释:

awk 'FNR==1{ms=$1*1000%1000;secs=int($1%60);mins=int($1/60);hours=int(mins/60);days=int(hours/24);hours=hours%24;mins=mins%60;printf "uptime: %sd %sh %sm %ss %sms\n", days, hours, mins, secs, ms}' /proc/uptime

如果你可以安全地假设/proc/uptime只有 1 行,你将变量减少到只有它们的第一个字母,并且你稍微修改数学,你会得到这一行:

awk '{ms=$1*1000%1000;s=int($1%60);m=int($1/60%60);h=int($1/3600%24);d=int($1/86400);printf "uptime: %sd %sh %sm %ss %sms\n", d, h, m, s, ms}' /proc/uptime

而且,您始终可以删除任何您不关心的计算。例如,如果您想删除毫秒和四舍五入的秒数(而不是仅从int()四舍五入的秒数中删除并将其保留为浮点数):

awk '{s=int($1%60+.5);m=int($1/60%60);h=int($1/3600%24);d=int($1/86400);printf "uptime: %sd %sh %sm %ss %sms\n", d, h, m, s}' /proc/uptime

答案4

仅供参考:

$ echo -e "\n$(正常运行时间)\n" ; 正常运行时间 \
| sed -nre 's/.+up +([\:0-9]+), +([^ ] [^,]+), +.*: +([^ ]+), +([^ ]+), +([^ ]+)$/\1, \2, \3 \4 \5/p'

 12:58:47 启动 5:53,2 个用户,平均负载:0,21、0,09、0,10

5:53,2 位用户,0.21 0.09 0.10

请注意,数字的小数分隔符取决于您的区域设置。

相关内容