2 # Desc: Displays `uptime` information but with ISO-8601 time period 
   5 yell
() { echo "$0: $*" >&2; } # Yell, Die, Try Three-Fingered Claw technique; # Ref/Attrib: https://stackoverflow.com/a/25515370 
   6 die
() { yell 
"$*"; exit 111; } 
   7 try
() { "$@" || die 
"cannot $*"; } 
   9     # Desc: Given seconds, output ISO-8601 duration string 
  10     # Ref/Attrib: ISO-8601:2004(E), §4.4.4.2 Representations of time intervals by duration and context information 
  11     # Note: "1 month" ("P1M") is assumed to be "30 days" (see ISO-8601:2004(E), §2.2.1.2) 
  12     # Usage: timeDuration [1:seconds] ([2:precision]) 
  14     # Input: arg1: seconds as base 10 integer >= 0  (ex: 3601) 
  15     #        arg2: precision level (optional; default=2) 
  16     # Output: stdout: ISO-8601 duration string (ex: "P1H1S", "P2Y10M15DT10H30M20S") 
  17     #         exit code 0: success 
  18     #         exit code 1: error_input 
  19     #         exit code 2: error_unknown 
  20     # Example: 'timeDuration 111111 3' yields 'P1DT6H51M' 
  21     # Depends: date 8, bash 5, yell, 
  22     local argSeconds argPrecision precision returnState remainder
 
  23     local fullYears fullMonths fullDays fullHours fullMinutes fullSeconds
 
  24     local hasYears hasMonths hasDays hasHours hasMinutes hasSeconds
 
  25     local witherPrecision output
 
  26     local displayYears displayMonths displayDays displayHours displayMinutes displaySeconds
 
  28     argSeconds
="$1"; # read arg1 (seconds) 
  29     argPrecision
="$2"; # read arg2 (precision) 
  30     precision
=2; # set default precision 
  32     # Check that between one and two arguments is supplied 
  33     if ! { [[ $# -ge 1 ]] && [[ $# -le 2 ]]; }; then 
  34         yell 
"ERROR:Invalid number of arguments:$# . Exiting."; 
  35         returnState
="error_input"; fi 
  37     # Check that argSeconds provided 
  38     if [[ $# -ge 1 ]]; then 
  39         ## Check that argSeconds is a positive integer 
  40         if [[ "$argSeconds" =~ ^
[[:digit
:]]+$ 
]]; then 
  43             yell 
"ERROR:argSeconds not a digit."; 
  44             returnState
="error_input"; 
  47         yell 
"ERROR:No argument provided. Exiting."; 
  51     # Consider whether argPrecision was provided 
  52     if  [[ $# -eq 2 ]]; then 
  53         # Check that argPrecision is a positive integer 
  54         if [[ "$argPrecision" =~ ^
[[:digit
:]]+$ 
]] && [[ "$argPrecision" -gt 0 ]]; then 
  55         precision
="$argPrecision"; 
  57             yell 
"ERROR:argPrecision not a positive integer. (is $argPrecision ). Leaving early."; 
  58             returnState
="error_input"; 
  64     remainder
="$argSeconds" ; # seconds 
  65     ## Calculate full years Y, update remainder 
  66     fullYears
=$
(( remainder 
/ (365*24*60*60) )); 
  67     remainder
=$
(( remainder 
- (fullYears
*365*24*60*60) )); 
  68     ## Calculate full months M, update remainder 
  69     fullMonths
=$
(( remainder 
/ (30*24*60*60) )); 
  70     remainder
=$
(( remainder 
- (fullMonths
*30*24*60*60) )); 
  71     ## Calculate full days D, update remainder 
  72     fullDays
=$
(( remainder 
/ (24*60*60) )); 
  73     remainder
=$
(( remainder 
- (fullDays
*24*60*60) )); 
  74     ## Calculate full hours H, update remainder 
  75     fullHours
=$
(( remainder 
/ (60*60) )); 
  76     remainder
=$
(( remainder 
- (fullHours
*60*60) )); 
  77     ## Calculate full minutes M, update remainder 
  78     fullMinutes
=$
(( remainder 
/ (60) )); 
  79     remainder
=$
(( remainder 
- (fullMinutes
*60) )); 
  80     ## Calculate full seconds S, update remainder 
  81     fullSeconds
=$
(( remainder 
/ (1) )); 
  82     remainder
=$
(( remainder 
- (remainder
*1) )); 
  83     ## Check which fields filled 
  84     if [[ $fullYears -gt 0 ]]; then hasYears
="true"; else hasYears
="false"; fi 
  85     if [[ $fullMonths -gt 0 ]]; then hasMonths
="true"; else hasMonths
="false"; fi 
  86     if [[ $fullDays -gt 0 ]]; then hasDays
="true"; else hasDays
="false"; fi 
  87     if [[ $fullHours -gt 0 ]]; then hasHours
="true"; else hasHours
="false"; fi 
  88     if [[ $fullMinutes -gt 0 ]]; then hasMinutes
="true"; else hasMinutes
="false"; fi 
  89     if [[ $fullSeconds -ge 0 ]]; then hasSeconds
="true"; else hasSeconds
="false"; fi 
  91     ## Determine which fields to display (see ISO-8601:2004 §4.4.3.2) 
  92     witherPrecision
="false" 
  95     if $hasYears && [[ $precision -gt 0 ]]; then 
  97         witherPrecision
="true"; 
 101     if $witherPrecision; then ((precision--
)); fi; 
 104     if $hasMonths && [[ $precision -gt 0 ]]; then 
 105         displayMonths
="true"; 
 106         witherPrecision
="true"; 
 108         displayMonths
="false"; 
 110     if $witherPrecision && [[ $precision -gt 0 ]]; then 
 111         displayMonths
="true"; 
 113     if $witherPrecision; then ((precision--
)); fi; 
 116     if $hasDays && [[ $precision -gt 0 ]]; then 
 118         witherPrecision
="true"; 
 122     if $witherPrecision && [[ $precision -gt 0 ]]; then 
 125     if $witherPrecision; then ((precision--
)); fi; 
 128     if $hasHours && [[ $precision -gt 0 ]]; then 
 130         witherPrecision
="true"; 
 132         displayHours
="false"; 
 134     if $witherPrecision && [[ $precision -gt 0 ]]; then 
 137     if $witherPrecision; then ((precision--
)); fi; 
 140     if $hasMinutes && [[ $precision -gt 0 ]]; then 
 141         displayMinutes
="true"; 
 142         witherPrecision
="true"; 
 144         displayMinutes
="false"; 
 146     if $witherPrecision && [[ $precision -gt 0 ]]; then 
 147         displayMinutes
="true"; 
 149     if $witherPrecision; then ((precision--
)); fi; 
 153     if $hasSeconds && [[ $precision -gt 0 ]]; then 
 154         displaySeconds
="true"; 
 155         witherPrecision
="true"; 
 157         displaySeconds
="false"; 
 159     if $witherPrecision && [[ $precision -gt 0 ]]; then 
 160         displaySeconds
="true"; 
 162     if $witherPrecision; then ((precision--
)); fi; 
 164     ## Determine whether or not the "T" separator is needed to separate date and time elements 
 165     if ( $displayHours || 
$displayMinutes || 
$displaySeconds); then 
 166         displayDateTime
="true"; else displayDateTime
="false"; fi 
 168     ## Construct duration output string 
 170     if $displayYears; then 
 171         output
=$output$fullYears"Y"; fi 
 172     if $displayMonths; then 
 173         output
=$output$fullMonths"M"; fi 
 174     if $displayDays; then 
 175         output
=$output$fullDays"D"; fi 
 176     if $displayDateTime; then 
 177         output
=$output"T"; fi 
 178     if $displayHours; then 
 179         output
=$output$fullHours"H"; fi 
 180     if $displayMinutes; then 
 181         output
=$output$fullMinutes"M"; fi 
 182     if $displaySeconds; then 
 183         output
=$output$fullSeconds"S"; fi 
 185     ## Output duration string to stdout 
 186     echo "$output" && returnState
="true"; 
 188     #===Determine function return code=== 
 189     if [ "$returnState" = "true" ]; then 
 191     elif [ "$returnState" = "error_input" ]; then 
 195         yell 
"ERROR:Unknown"; 
 199 } # Get duration (ex: PT10M4S ) 
 201     # Desc: Get system uptime with ISO_8601-formatted time period 
 202     # Usage: uptimeIso8601     
 203     # Output: A string similar to `uptime` but with date and uptime 
 204     #   time period replaced with a period/endtime ISO-8601-formatted 
 206     #     ex: `P3DT23H36M46S/2021-03-28T14:47:48+0000,  5 users,  load average: 0.09, 0.10, 0.13` 
 207     # Note: No arguments used. 
 208     # Depends: timeDuration() v1.0.5, uptime 
 210     # Ref/Attrib: get uptime in seconds https://leo.leung.xyz/wiki/Linux_Uptime_in_Seconds 
 211     sysUptime
=$
(</proc
/uptime
); 
 212     sysUptime
=${sysUptime%%.*}; #  
 213     sysUptimeIso
="$(timeDuration $sysUptime 4)"; 
 214     timeNow
="$(date +%Y-%m-%dT%H:%M:%S%z)"; 
 215     users_load
="$(uptime | cut -d',' -f3-)"; 
 216     output
="$sysUptimeIso"/"$timeNow","$users_load "; 
 218 } # Get uptime with ISO-8601 time period 
 224 # Author: Steven Baltakatei Sandoval