{"id":506844,"date":"2025-08-08T08:34:02","date_gmt":"2025-08-08T08:34:02","guid":{"rendered":"https:\/\/www.ninjaone.com\/?post_type=script_hub&#038;p=506844"},"modified":"2025-08-08T08:34:51","modified_gmt":"2025-08-08T08:34:51","slug":"monitorare-i-dati-sulle-prestazioni-in-macos-con-uno-script-shell","status":"publish","type":"script_hub","link":"https:\/\/www.ninjaone.com\/it\/script-hub\/monitorare-i-dati-sulle-prestazioni-in-macos-con-uno-script-shell\/","title":{"rendered":"Come monitorare i dati sulle prestazioni in macOS con uno script Shell"},"content":{"rendered":"<p>In questo articolo imparerai a monitorare i dati sulle prestazioni in macOS con uno script Shell. Nel mondo delle operazioni IT e della gestione degli endpoint, la comprensione delle prestazioni di un sistema non \u00e8 facoltativa: \u00e8 fondamentale. Dal garantire che il parco dispositivi Mac in ambiente enterprise rimanga integro fino alla risoluzione dei reclami degli utenti basata su metriche reali, la necessit\u00e0 di dati sulle prestazioni trasparenti e affidabili \u00e8 pi\u00f9 pressante che mai. Per i professionisti IT e i <a href=\"https:\/\/www.ninjaone.com\/it\/cos-e-un-msp\/\">provider di servizi gestiti (MSP)<\/a>, un approccio proattivo al monitoraggio delle prestazioni pu\u00f2 migliorare la qualit\u00e0 del servizio, <a href=\"https:\/\/www.ninjaone.com\/it\/it-hub\/gestione-dei-servizi-it\/cos-e-il-tempo-di-inattivita-di-una-rete\/\">ridurre i tempi di inattivit\u00e0<\/a> e consentire un processo decisionale basato sui dati. \u00c8 qui che entrano in gioco gli script shell automatizzati.<\/p>\n<h2>Contesto<\/h2>\n<p>Sebbene strumenti come Activity Monitor o dashboard delle prestazioni di terze parti forniscano informazioni utili, spesso mancano di <a href=\"https:\/\/www.ninjaone.com\/it\/blog\/tutto-quello-che-bisogna-sapere-sull-automazione-it\/\">automazione<\/a>, coerenza e integrazione con piattaforme di gestione centralizzate come NinjaOne. Lo script che andremo ad esaminare colma questa lacuna raccogliendo un&#8217;ampia gamma di dati sulle prestazioni dei sistemi <a href=\"https:\/\/www.ninjaone.com\/it\/gestione-endpoint\/gestione-dei-mac\/\">macOS<\/a> come utilizzo di CPU, memoria, disco e rete, per poi formattarli in HTML e memorizzarli in un campo WYSIWYG personalizzato di NinjaOne.<\/p>\n<p>Questo approccio elimina la necessit\u00e0 di diagnostica manuale, standardizza i report sulle prestazioni e rende le metriche visibili ai tecnici direttamente nella sezione dell&#8217;endpoint. \u00c8 stato progettato per supportare i flussi di lavoro di monitoraggio zero-touch, la risoluzione dei problemi da remoto e i report di pi\u00f9 alto livello.<\/p>\n<h2>Lo script per monitorare i dati sulle prestazioni in macOS:<\/h2>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\">#!\/usr\/bin\/env bash\r\n#\r\n# Description: Collects system performance data (CPU, memory, disk, and network). The results can optionally be saved to a WYSIWYG custom field.\r\n# By using this script, you indicate your acceptance of the following legal terms as well as our Terms of Use at https:\/\/www.ninjaone.com\/terms-of-use.\r\n# Ownership Rights: NinjaOne owns and will continue to own all right, title, and interest in and to the script (including the copyright). NinjaOne is giving you a limited license to use the script in accordance with these legal terms. \r\n# Use Limitation: You may only use the script for your legitimate personal or internal business purposes, and you may not share the script with another party. \r\n# Republication Prohibition: Under no circumstances are you permitted to re-publish the script in any script library or website belonging to or under the control of any other software provider. \r\n# Warranty Disclaimer: The script is provided \u201cas is\u201d and \u201cas available\u201d, without warranty of any kind. NinjaOne makes no promise or guarantee that the script will be free from defects or that it will meet your specific needs or expectations. \r\n# Assumption of Risk: Your use of the script is at your own risk. You acknowledge that there are certain inherent risks in using the script, and you understand and assume each of those risks. \r\n# Waiver and Release: You will not hold NinjaOne responsible for any adverse or unintended consequences resulting from your use of the script, and you waive any legal or equitable rights or remedies you may have against NinjaOne relating to your use of the script. \r\n# EULA: If you are a NinjaOne customer, your use of the script is subject to the End User License Agreement applicable to you (EULA).\r\n#\r\n# Example: --daysSinceLastReboot \"1\" --durationToPerformTests \"5\" --numberOfEvents \"5\" --wysiwygCustomFieldName \"WYSIWYG\"\r\n#\r\n# [Alert] This computer was last started on 02\/24\/25 08:24:27 AM, which was 7.30 days ago.\r\n# Collecting the last 5 error events from the system log.\r\n#\r\n# Collecting performance metrics for 5 minutes.\r\n# Finished collecting performance metrics.\r\n#\r\n# Formatting total CPU results.\r\n# Formatting CPU process data.\r\n# Formatting the total RAM results.\r\n# Formatting RAM process data.\r\n# Formatting the network usage data.\r\n# Formatting disk results.\r\n# Formatting event log entries.\r\n#\r\n# Collecting system information.\r\n#\r\n# ### Apple M1 ###\r\n# Average CPU % Used  Minimum CPU % Used  Maximum CPU % Used\r\n# 48.07%              40.30%              67.37%\r\n#\r\n# ### Memory Usage ###\r\n# Total Memory Installed: 16.00 GiB\r\n# Average RAM % Used  Minimum RAM % Used  Maximum RAM % Used\r\n# 99.52%              99.44%              99.59%\r\n#\r\n# ### Top 5 CPU Processes ###\r\n# PID    Name              Average CPU % Used  Minimum CPU % Used  Maximum CPU % Used\r\n# 656    WindowServer      21.57%              5.90%               36.90%\r\n# 3503   firefox           18.05%              6.00%               30.50%\r\n# 74524  Storage           8.95%               8.80%               12.70%\r\n# 0      kernel_task       5.78%               5.40%               8.00%\r\n# 74563  ApplicationsStor  5.02%               3.50%               10.40%\r\n#\r\n# ### Top 5 RAM Processes ###\r\n# PID    Name              Average RAM % Used  Minimum RAM % Used  Maximum RAM % Used\r\n# 3503   firefox           8.43%               8.33%               8.58%\r\n# 72329  datagrip          7.96%               7.95%               7.96%\r\n# 656    WindowServer      4.27%               4.26%               4.28%\r\n# 40463  plugin-container  3.87%               3.87%               3.89%\r\n# 828    ninjarmm-macagen  3.28%               3.27%               3.28%\r\n#\r\n# ### Network Usage ###\r\n# Network Adapter  MAC Address        Type   Average Sent &amp; Received  Minimum Sent &amp; Received  Maximum Sent &amp; Received\r\n# en0              00:00:00:00:00:00  Wi-Fi  7.98 Mbps                7.91 Mbps                8.03 Mbps\r\n#\r\n# ### Disk Usage ###\r\n# Volume: disk1s1\r\n# Name: iSCPreboot\r\n# Mount Point: \/System\/Volumes\/iSCPreboot\r\n# Consumed Space: 5.55 MiB (1.11%)\r\n# Container: disk1\r\n# Free Space: 479.16 MiB\r\n# Total Space: 500 MiB\r\n# Physical Disk: APPLE SSD AP0256Q\r\n# Media Type: SSD\r\n#\r\n# Volume: disk1s2\r\n# Name: xART\r\n# Mount Point: \/System\/Volumes\/xarts\r\n# Consumed Space: 6.02 MiB (1.20%)\r\n# Container: disk1\r\n# Free Space: 479.16 MiB\r\n# Total Space: 500 MiB\r\n# Physical Disk: APPLE SSD AP0256Q\r\n# Media Type: SSD\r\n#\r\n# Volume: disk1s3\r\n# Name: Hardware\r\n# Mount Point: \/System\/Volumes\/Hardware\r\n# Consumed Space: 4.52 MiB (0.90%)\r\n# Container: disk1\r\n# Free Space: 479.16 MiB\r\n# Total Space: 500 MiB\r\n# Physical Disk: APPLE SSD AP0256Q\r\n# Media Type: SSD\r\n#\r\n# Volume: disk1s4\r\n# Name: Recovery\r\n# Mount Point: N\/A\r\n# Consumed Space: 20 KiB (0%)\r\n# Container: disk1\r\n# Free Space: 479.16 MiB\r\n# Total Space: 500 MiB\r\n# Physical Disk: APPLE SSD AP0256Q\r\n# Media Type: SSD\r\n#\r\n# Volume: disk2s1\r\n# Name: Recovery\r\n# Mount Point: N\/A\r\n# Consumed Space: 1.84 GiB (36.87%)\r\n# Container: disk2\r\n# Free Space: 3.14 GiB\r\n# Total Space: 5 GiB\r\n# Physical Disk: APPLE SSD AP0256Q\r\n# Media Type: SSD\r\n#\r\n# Volume: disk2s2\r\n# Name: Update\r\n# Mount Point: N\/A\r\n# Consumed Space: 2.84 MiB (0.06%)\r\n# Container: disk2\r\n# Free Space: 3.14 GiB\r\n# Total Space: 5 GiB\r\n# Physical Disk: APPLE SSD AP0256Q\r\n# Media Type: SSD\r\n#\r\n# Volume: disk3s1\r\n# Name: Macintosh HD - Data\r\n# Mount Point: \/System\/Volumes\/Data\r\n# Consumed Space: 156.20 GiB (68.43%)\r\n# Container: disk3\r\n# Free Space: 52.70 GiB\r\n# Total Space: 228.27 GiB\r\n# Physical Disk: APPLE SSD AP0256Q\r\n# Media Type: SSD\r\n#\r\n# Volume: disk3s2\r\n# Name: Update\r\n# Mount Point: \/System\/Volumes\/Update\r\n# Consumed Space: 62.33 MiB (0.03%)\r\n# Container: disk3\r\n# Free Space: 52.70 GiB\r\n# Total Space: 228.27 GiB\r\n# Physical Disk: APPLE SSD AP0256Q\r\n# Media Type: SSD\r\n#\r\n# Volume: disk3s3\r\n# Name: Macintosh HD\r\n# Mount Point: N\/A\r\n# Consumed Space: 10.48 GiB (4.59%)\r\n# Container: disk3\r\n# Free Space: 52.70 GiB\r\n# Total Space: 228.27 GiB\r\n# Physical Disk: APPLE SSD AP0256Q\r\n# Media Type: SSD\r\n#\r\n# Volume: disk3s4\r\n# Name: Preboot\r\n# Mount Point: \/System\/Volumes\/Preboot\r\n# Consumed Space: 6.73 GiB (2.95%)\r\n# Container: disk3\r\n# Free Space: 52.70 GiB\r\n# Total Space: 228.27 GiB\r\n# Physical Disk: APPLE SSD AP0256Q\r\n# Media Type: SSD\r\n#\r\n# Volume: disk3s5\r\n# Name: Recovery\r\n# Mount Point: N\/A\r\n# Consumed Space: 998.96 MiB (0.43%)\r\n# Container: disk3\r\n# Free Space: 52.70 GiB\r\n# Total Space: 228.27 GiB\r\n# Physical Disk: APPLE SSD AP0256Q\r\n# Media Type: SSD\r\n#\r\n# Volume: disk3s6\r\n# Name: VM\r\n# Mount Point: \/System\/Volumes\/VM\r\n# Consumed Space: 1 GiB (0.44%)\r\n# Container: disk3\r\n# Free Space: 52.70 GiB\r\n# Total Space: 228.27 GiB\r\n# Physical Disk: APPLE SSD AP0256Q\r\n# Media Type: SSD\r\n#\r\n# The custom field 'WYSIWYG' has been provided.\r\n# Converting CPU process data into HTML.\r\n# Converting RAM process data into HTML.\r\n# Converting network usage data into HTML.\r\n# Converting disk usage data into HTML.\r\n# Assembling the html data into the html card\r\n# Highlighting last startup date.\r\n# Highlighting overall CPU usage metrics.\r\n# Converting system log data into an HTML format.\r\n#\r\n# Attempting to set the custom field 'WYSIWYG'.\r\n# Successfully set the custom field 'WYSIWYG'.\r\n#\r\n# ### Last 5 warnings and errors in the system log. ###\r\n# Service: trustd\r\n# PID: 1656\r\n# Time Created: 03\/03\/25 03:37:58 PM\r\n# Message: (Security) [com.apple.security:seckey] SecKeyVerifySignature failed: Error Domain=NSOSStatusErrorDomain Code=-67808 \"RSA signature verification failed, no match\" UserInfo={numberOfErrorsDeep=0, NSDescription=RSA signature verification failed, no match}\r\n#\r\n# Service: trustd\r\n# PID: 1656\r\n# Time Created: 03\/03\/25 03:37:58 PM\r\n# Message: (Security) [com.apple.security:seckey] SecKeyVerifySignature failed: Error Domain=NSOSStatusErrorDomain Code=-67808 \"RSA signature verification failed, no match\" UserInfo={numberOfErrorsDeep=0, NSDescription=RSA signature verification failed, no match}\r\n#\r\n# Service: trustd\r\n# PID: 1656\r\n# Time Created: 03\/03\/25 03:37:58 PM\r\n# Message: (Security) [com.apple.security:seckey] SecKeyVerifySignature failed: Error Domain=NSOSStatusErrorDomain Code=-67808 \"RSA signature verification failed, no match\" UserInfo={numberOfErrorsDeep=0, NSDescription=RSA signature verification failed, no match}\r\n#\r\n# Service: trustd\r\n# PID: 1656\r\n# Time Created: 03\/03\/25 03:37:58 PM\r\n# Message: (Security) [com.apple.security:seckey] SecKeyVerifySignature failed: Error Domain=NSOSStatusErrorDomain Code=-67808 \"RSA signature verification failed, no match\" UserInfo={numberOfErrorsDeep=0, NSDescription=RSA signature verification failed, no match}\r\n#\r\n# Service: trustd\r\n# PID: 1656\r\n# Time Created: 03\/03\/25 03:37:58 PM\r\n# Message: (Security) [com.apple.security:seckey] SecKeyVerifySignature failed: Error Domain=NSOSStatusErrorDomain Code=-67808 \"RSA signature verification failed, no match\" UserInfo={numberOfErrorsDeep=0, NSDescription=RSA signature verification failed, no match}\r\n#\r\n# Preset Parameter: --daysSinceLastReboot \"7\"\r\n#\t\tSpecify the number of days by which the system should have been rebooted.\r\n#\r\n# Preset Parameter: --durationToPerformTests \"5\"\r\n#\t\tThe duration (in minutes) for which the performance tests should be executed.\r\n#\r\n# Preset Parameter: --numberOfEvents \"5\"\r\n#\t\tThe number of error events to retrieve from the system log.\r\n#\r\n# Preset Parameter: --wysiwygCustomField \"ReplaceMeWithAnyWYSIWYGCustomField\"\r\n#\t\tOptionally specify the name of a WYSIWYG custom field to store the formatted performance data.\r\n#\r\n# Preset Parameter: --help\r\n#\t\tDisplays some help text.\r\n#\r\n# Minimum Supported OS: macOS Ventura\r\n# Release Notes: Initial Release\r\n\r\n# Script arguments\r\n_arg_daysSinceLastReboot=\r\n_arg_durationToPerformTests=5\r\n_arg_numberOfEvents=5\r\n_arg_wysiwygCustomField=\r\nexitCode=0\r\n\r\n# Converts a string input into an HTML table format.\r\nconvertToHTMLTable() {\r\n  local _arg_delimiter=\" \"\r\n  local _arg_inputObject\r\n\r\n  # Process command-line arguments for the function.\r\n  while test $# -gt 0; do\r\n    _key=\"$1\"\r\n    case \"$_key\" in\r\n      --delimiter | -d)\r\n        test $# -lt 2 &amp;&amp; echo \"[Error] Missing value for the required argument\" &gt;&amp;2 &amp;&amp; return 1\r\n        _arg_delimiter=$2\r\n        shift\r\n        ;;\r\n      --*)\r\n        echo \"[Error] Got an unexpected argument\" &gt;&amp;2\r\n        return 1\r\n        ;;\r\n      *)\r\n        _arg_inputObject=$1\r\n        ;;\r\n    esac\r\n    shift\r\n  done\r\n\r\n  # Handles missing input by checking stdin or returning an error.\r\n  if [[ -z $_arg_inputObject ]]; then\r\n    if [ -p \/dev\/stdin ]; then\r\n      _arg_inputObject=$(cat)\r\n    else\r\n      echo \"[Error] Missing input object to convert to table\" &gt;&amp;2\r\n      return 1\r\n    fi\r\n  fi\r\n\r\n  _arg_inputObject=${_arg_inputObject\/\/\"%\"\/\"%%\"}\r\n\r\n  local htmlTable=\"&lt;table&gt;\\n\"\r\n  htmlTable+=$(printf '%b' \"$_arg_inputObject\" 2&gt; \/dev\/null | head -n1 | awk -F \"$_arg_delimiter\" '{\r\n    printf \"&lt;tr&gt;\"\r\n    for (i=1; i&lt;=NF; i+=1)\r\n      { printf \"&lt;th&gt;\"$i\"&lt;\/th&gt;\" }\r\n    printf \"&lt;\/tr&gt;\"\r\n    }')\r\n  htmlTable+=\"\\n\"\r\n  htmlTable+=$(printf '%b' \"$_arg_inputObject\" | tail -n +2 | awk -F \"$_arg_delimiter\" '{\r\n    printf \"&lt;tr&gt;\"\r\n    for (i=1; i&lt;=NF; i+=1)\r\n      { printf \"&lt;td&gt;\"$i\"&lt;\/td&gt;\" }\r\n    print \"&lt;\/tr&gt;\"\r\n    }')\r\n  htmlTable+=\"\\n&lt;\/table&gt;\"\r\n\r\n  printf '%b' \"$htmlTable\" '\\n'\r\n}\r\n\r\nConvertTo-FriendlySize() {\r\n  local size=$1\r\n  local units=(B KiB MiB GiB TiB PiB)\r\n  local i=0\r\n\r\n  while (($(echo \"$size &gt;= 1024\" | bc -l) &amp;&amp; i &lt; ${#units[@]} - 1)); do\r\n    size=$(echo \"scale=6; $size\/1024\" | bc)\r\n    ((i++))\r\n  done\r\n\r\n  # Round to two decimal places\r\n  size=$(printf \"%.2f\" \"$size\")\r\n\r\n  # Remove trailing .00 if present\r\n  size=${size\/\/.00\/}\r\n\r\n  echo \"${size} ${units[i]}\"\r\n}\r\n\r\n# Help menu\r\nprint_help() {\r\n  printf '\\n\\n%s\\n\\n' 'Usage: [--daysSinceLastReboot|-r &lt;arg&gt;] [--durationToPerformTests|-d &lt;arg&gt;] [--numberOfEvents|-n &lt;arg&gt;]\r\n  [--wysiwygCustomField|-w &lt;arg&gt;] [--help|-h]'\r\n  printf '%s\\n' 'Preset Parameter: --daysSinceLastReboot \"7\"'\r\n  printf '\\t%s\\n' \"Specify the number of days by which the system should have been rebooted.\"\r\n  printf '%s\\n' 'Preset Parameter: --durationToPerformTests \"5\"'\r\n  printf '\\t%s\\n' \"The duration (in minutes) for which the performance tests should be executed.\"\r\n  printf '%s\\n' 'Preset Parameter: --numberOfEvents \"5\"'\r\n  printf '\\t%s\\n' \"The number of error events to retrieve from the system log.\"\r\n  printf '%s\\n' 'Preset Parameter: --wysiwygCustomField \"ReplaceMeWithAnyWYSIWYGCustomField\"'\r\n  printf '\\t%s\\n' \"Optionally specify the name of a WYSIWYG custom field to store the formatted performance data.\"\r\n  printf '%s\\n' 'Preset Parameter: --help'\r\n  printf '\\t%s\\n' \"Displays this help menu.\"\r\n}\r\n\r\ndie() {\r\n  local _ret=\"${2:-1}\"\r\n  echo \"$1\" &gt;&amp;2\r\n  test \"${_PRINT_HELP:-no}\" = yes &amp;&amp; print_help &gt;&amp;2\r\n  exit \"${_ret}\"\r\n}\r\n\r\n# Parses the given command line parameters\r\nparse_commandline() {\r\n  while test $# -gt 0; do\r\n    _key=\"$1\"\r\n    case \"$_key\" in\r\n      --daysSinceLastReboot | --dayssincelastreboot | --days | -r)\r\n        test $# -lt 2 &amp;&amp; die \"[Error] Missing value for the optional argument '$_key'.\" 1\r\n        _arg_daysSinceLastReboot=$2\r\n        shift\r\n        ;;\r\n      --daysSinceLastReboot=*)\r\n        _arg_daysSinceLastReboot=\"${_key##--daysSinceLastReboot=}\"\r\n        ;;\r\n      --durationToPerformTests | --durationtoperformtests | --duration | -d)\r\n        test $# -lt 2 &amp;&amp; die \"[Error] Missing value for the optional argument '$_key'.\" 1\r\n        _arg_durationToPerformTests=$2\r\n        shift\r\n        ;;\r\n      --durationToPerformTests=*)\r\n        _arg_durationToPerformTests=\"${_key##--durationToPerformTests=}\"\r\n        ;;\r\n      --numberOfEvents | --numberofevents | --events | -n)\r\n        test $# -lt 2 &amp;&amp; die \"[Error] Missing value for the optional argument '$_key'.\" 1\r\n        _arg_numberOfEvents=$2\r\n        shift\r\n        ;;\r\n      --numberOfEvents=*)\r\n        _arg_numberOfEvents=\"${_key##--numberOfEvents=}\"\r\n        ;;\r\n      --wysiwygCustomField | --wysiwygcustomfield | --wysiwyg | -w)\r\n        test $# -lt 2 &amp;&amp; die \"[Error] Missing value for the optional argument '$_key'.\" 1\r\n        _arg_wysiwygCustomField=$2\r\n        shift\r\n        ;;\r\n      --wysiwygCustomField=*)\r\n        _arg_wysiwygCustomField=\"${_key##--wysiwygCustomField=}\"\r\n        ;;\r\n      --help | -h)\r\n        _PRINT_HELP=yes die\r\n        ;;\r\n      *)\r\n        _PRINT_HELP=yes die \"[Error] Got an unexpected argument '$1'\" 1\r\n        ;;\r\n    esac\r\n    shift\r\n  done\r\n}\r\n\r\necho \" \"\r\nparse_commandline \"$@\"\r\n\r\n# If script form variables are used, replace the command line parameters with their value.\r\nif [[ -n $daysSinceLastReboot ]]; then\r\n  _arg_daysSinceLastReboot=\"$daysSinceLastReboot\"\r\nfi\r\nif [[ -n $durationToPerformTests ]]; then\r\n  _arg_durationToPerformTests=\"$durationToPerformTests\"\r\nfi\r\nif [[ -n $numberOfEvents ]]; then\r\n  _arg_numberOfEvents=\"$numberOfEvents\"\r\nfi\r\nif [[ -n $wysiwygCustomFieldName ]]; then\r\n  _arg_wysiwygCustomField=\"$wysiwygCustomFieldName\"\r\nfi\r\n\r\n# Ensure the script is being run with root permissions\r\nif [[ $(id -u) -ne 0 ]]; then\r\n  _PRINT_HELP=no die \"[Error] This script must be run with root permissions. Try running it with sudo or as the system\/root user.\" 1\r\nfi\r\n\r\n# Validate and sanitize the 'Days Since Last Reboot' argument\r\nif [[ -n \"$_arg_daysSinceLastReboot\" ]]; then\r\n  _arg_daysSinceLastReboot=$(echo \"$_arg_daysSinceLastReboot\" | xargs)\r\n\r\n  # Check if the argument is empty after trimming\r\n  if [[ -z \"$_arg_daysSinceLastReboot\" ]]; then\r\n    _PRINT_HELP=yes die \"[Error] The 'Days Since Last Reboot' value of '$_arg_daysSinceLastReboot' is invalid. Please provide a positive whole number or 0.\"\r\n  fi\r\nfi\r\n\r\n# Ensure 'Days Since Last Reboot' is a valid numeric value\r\nif [[ -n \"$_arg_daysSinceLastReboot\" &amp;&amp; \"$_arg_daysSinceLastReboot\" =~ [^0-9] ]]; then\r\n  _PRINT_HELP=yes die \"[Error] The 'Days Since Last Reboot' value of '$_arg_daysSinceLastReboot' is invalid. Please provide a positive whole number or 0.\" 1\r\nfi\r\n\r\n# Validate and sanitize the 'Duration To Perform Tests' argument\r\nif [[ -n \"$_arg_durationToPerformTests\" ]]; then\r\n  _arg_durationToPerformTests=$(echo \"$_arg_durationToPerformTests\" | xargs)\r\nfi\r\n\r\n# Ensure the duration is provided\r\nif [[ -z \"$_arg_durationToPerformTests\" ]]; then\r\n  _PRINT_HELP=yes die \"[Error] Please provide a valid duration for performing the tests.\" 1\r\nfi\r\n\r\n# Check if the duration is a valid positive number between 1 and 60\r\nif [[ \"$_arg_durationToPerformTests\" =~ [^0-9] || \"$_arg_durationToPerformTests\" -eq 0 || \"$_arg_durationToPerformTests\" -ge 60 ]]; then\r\n  _PRINT_HELP=yes die \"[Error] The 'Duration To Perform Tests' value of '$_arg_durationToPerformTests' is invalid.\r\n[Error] Please provide a positive whole number that's greater than 0 and less than 60.\" 1\r\nfi\r\n\r\n# Validate and sanitize the 'Number of Events' argument\r\nif [[ -n \"$_arg_numberOfEvents\" ]]; then\r\n  _arg_numberOfEvents=$(echo \"$_arg_numberOfEvents\" | xargs)\r\nfi\r\n\r\n# Ensure the number of events is provided\r\nif [[ -z \"$_arg_numberOfEvents\" ]]; then\r\n  _PRINT_HELP=yes die \"[Error] Please provide a valid number of recent error events to include in the results.\" 1\r\nfi\r\n\r\n# Check if the number of events is a valid numeric value and within the allowed range\r\nif [[ \"$_arg_numberOfEvents\" =~ [^0-9] || \"$_arg_numberOfEvents\" -gt 10000 ]]; then\r\n  _PRINT_HELP=yes die \"[Error] The 'Number of Events' value of '$_arg_numberOfEvents' is invalid. Please provide a positive whole number that is less than or equal to 10000 or greater than or equal to 0.\" 1\r\nfi\r\n\r\n# Validate and sanitize the 'WYSIWYG Custom Field' argument\r\nif [[ -n \"$_arg_wysiwygCustomField\" ]]; then\r\n  _arg_wysiwygCustomField=$(echo \"$_arg_wysiwygCustomField\" | xargs)\r\n\r\n  # Check if the argument is empty\r\n  if [[ -z \"$_arg_wysiwygCustomField\" ]]; then\r\n    _PRINT_HELP=yes die \"[Error] The 'WYSIWYG Custom Field' value of '$_arg_wysiwygCustomField' is invalid. Please provide a valid custom field name.\r\n  https:\/\/ninjarmm.zendesk.com\/hc\/en-us\/articles\/360060920631-Custom-Field-Setup\" 1\r\n  fi\r\nfi\r\n\r\n# Check if the custom field contains invalid characters\r\nif [[ -n \"$_arg_wysiwygCustomField\" &amp;&amp; \"$_arg_wysiwygCustomField\" =~ [^0-9a-zA-Z0-9] ]]; then\r\n  _PRINT_HELP=yes die \"[Error] The 'WYSIWYG Custom Field' value of '$_arg_wysiwygCustomField' is invalid. Please provide a valid custom field name.\r\n  https:\/\/ninjarmm.zendesk.com\/hc\/en-us\/articles\/360060920631-Custom-Field-Setup\" 1\r\nfi\r\n\r\n# Locate the ninjarmm-cli tool, which is required for setting custom fields\r\nif [[ -n \"$_arg_wysiwygCustomField\" ]]; then\r\n  if [[ -n $NINJA_DATA_PATH &amp;&amp; -f \"$NINJA_DATA_PATH\/ninjarmm-cli\" ]]; then\r\n    ninjarmmcliPath=\"$NINJA_DATA_PATH\/ninjarmm-cli\"\r\n  elif [[ -f \"\/Applications\/NinjaRMMAgent\/programdata\/ninjarmm-cli\" ]]; then\r\n    ninjarmmcliPath=\/Applications\/NinjaRMMAgent\/programdata\/ninjarmm-cli\r\n  else\r\n    _PRINT_HELP=no die \"[Error] Unable to locate ninjarmm-cli. Ninjarmm-cli is required to set custom fields.\r\n    https:\/\/ninjarmm.zendesk.com\/hc\/en-us\/articles\/4405408656013-Custom-Fields-and-Documentation-CLI-and-Scripting#h_01FB4NZ6GCG2V53NS4T1RQX8PY\" 1\r\n  fi\r\nfi\r\n\r\n# Ensure the script does not run if another instance is already running\r\nlockFile=\/Applications\/NinjaRMMAgent\/SystemPerformance.lock\r\nif [[ -f \"$lockFile\" ]]; then\r\n  echo \"Process lock file found at '$lockFile'. Checking if the process is still running.\"\r\n\r\n  if ! otherScript=$(cat \"$lockFile\"); then\r\n    _PRINT_HELP=no die \"[Error] Unable to access the lock file at '$lockFile'.\" 1\r\n  fi\r\n\r\n  if ! scriptProcess=$(ps -p \"$otherScript\" | sed '1d'); then\r\n    _PRINT_HELP=no die \"[Error] Unable to verify if another copy of the script is currently running with the process id (PID).\" 1\r\n  fi\r\n\r\n  if [[ -n \"$scriptProcess\" ]]; then\r\n    _PRINT_HELP=no die \"[Error] This script is already running in another process with the process id (PID) '$otherScript'. Please wait for that process to complete.\" 1\r\n  fi\r\nfi\r\n\r\n# Create a lock file to prevent multiple script instances\r\nif ! echo $$ &gt; $lockFile; then\r\n  _PRINT_HELP=no die \"[Error] Failed to create the process lock file at '$lockFile'. This is to prevent multiple instances of the performance script from running simultaneously.\" 1\r\nfi\r\n\r\n# Record the start date and time of the script\r\nreportStartDate=$(date \"+%x %r\")\r\n\r\n# Calculate the system's last boot time and check if it exceeds the allowed days since reboot\r\nlastBoot=$(sysctl -n kern.boottime | awk '{ print $4 }' | sed \"s\/[^0-9]\/\/g\")\r\nlastStartupDate=$(date -r \"$lastBoot\" \"+%x %r\")\r\nif [[ -n \"$_arg_daysSinceLastReboot\" &amp;&amp; $(date -v \"-$_arg_daysSinceLastReboot\"d \"+%s\") -gt \"$lastBoot\" ]]; then\r\n  secondsSinceLastBoot=$(($(date \"+%s\") - lastBoot))\r\n  daysSinceLastBoot=$(echo \"$secondsSinceLastBoot\" | awk '{printf \"%.2f\\n\", $1 \/ 86400}')\r\n  echo \"[Alert] This computer was last started on $lastStartupDate, which was $daysSinceLastBoot days ago.\"\r\n\r\n  exceededLastStartupLimit=\"true\"\r\nfi\r\n\r\n# Collect error events from the system logs\r\nif [[ \"$_arg_numberOfEvents\" -gt 0 ]]; then\r\n  echo \"Collecting the last $_arg_numberOfEvents error events from the system log.\"\r\n\r\n  predicateFilter='(messageType == \"error\")'\r\n  log show --last boot --predicate \"$predicateFilter\" --style \"syslog\" | tail -n \"$_arg_numberOfEvents\" &gt; \/tmp\/syslog.log 2&gt; \/tmp\/syslogErr.log &amp;\r\nfi\r\n\r\n# Collect CPU, RAM, network, and disk performance metrics\r\necho \"\"\r\necho \"Collecting performance metrics for $_arg_durationToPerformTests minutes.\"\r\ndiskutil apfs list -plist &gt; \/tmp\/diskData.log 2&gt; \/tmp\/diskDataErr.log\r\nmemory_pressure &gt; \/tmp\/totalMemData.log 2&gt; \/tmp\/totalMemDataErr.log\r\nCOLUMNS=10000 top -l \"$((_arg_durationToPerformTests + 1))\" -s 60 -o cpu -stats pid,cpu,command &gt; \/tmp\/cpuData.log 2&gt; \/tmp\/cpuDataErr.log &amp;\r\nCOLUMNS=10000 top -l \"$((_arg_durationToPerformTests + 1))\" -s 60 -o mem -stats pid,mem,command &gt; \/tmp\/memData.log 2&gt; \/tmp\/memDataErr.log &amp;\r\nnettop -t external -x -L \"$((_arg_durationToPerformTests + 1))\" -s 60 &gt; \/tmp\/netData.log 2&gt; \/tmp\/netDataErr.log &amp;\r\n\r\ni=1\r\nwhile ((i &lt; \"$_arg_durationToPerformTests\")); do\r\n  memory_pressure &gt;&gt; \/tmp\/totalMemData.log 2&gt;&gt; \/tmp\/totalMemDataErr.log\r\n\r\n  sleep 60\r\n  ((i++))\r\ndone\r\n\r\n# Wait for background tasks to complete\r\nwait\r\n\r\n# Validate collected event logs and handle errors\r\nif [[ \"$_arg_numberOfEvents\" -gt 0 ]]; then\r\n  if [[ -s \"\/tmp\/syslogErr.log\" ]]; then\r\n    cat \"\/tmp\/syslogErr.log\" &gt;&amp;2\r\n    echo \"[Error] Errors were detected while attempting to retrieve the last $_arg_numberOfEvents events.\" &gt;&amp;2\r\n    exitCode=1\r\n  fi\r\n\r\n  if [[ -f \"\/tmp\/syslog.log\" ]]; then\r\n    syslogLines=$(wc -l \/tmp\/syslog.log | grep -o \"[0-9]*\" | xargs)\r\n  fi\r\n\r\n  if [[ -z \"$syslogLines\" || \"$syslogLines\" =~ [^0-9] || \"$syslogLines\" -le 1 ]]; then\r\n    echo \"[Warning] No errors were found in the system log since the last boot. Is the system log service enabled?\"\r\n  fi\r\nfi\r\n\r\n# Validate collected performance data and handle errors\r\nif [[ -s \"\/tmp\/diskDataErr.log\" ]]; then\r\n  cat \"\/tmp\/diskDataErr.log\"\r\n  echo \"[Warning] Errors were detected while attempting to gather disk performance metrics.\"\r\nfi\r\nif [[ ! -s \"\/tmp\/diskData.log\" ]]; then\r\n  _PRINT_HELP=no die \"[Error] No disk performance metrics were collected.\" 1\r\nfi\r\nif [[ -s \"\/tmp\/cpuDataErr.log\" ]]; then\r\n  cat \"\/tmp\/cpuDataErr.log\" &gt;&amp;2\r\n  _PRINT_HELP=no die \"[Error] Errors were detected while attempting to gather CPU performance metrics.\" 1\r\nfi\r\nif [[ ! -s \"\/tmp\/cpuData.log\" ]]; then\r\n  _PRINT_HELP=no die \"[Error] No cpu performance metrics were collected.\" 1\r\nfi\r\nif [[ -s \"\/tmp\/memDataErr.log\" ]]; then\r\n  cat \"\/tmp\/memDataErr.log\" &gt;&amp;2\r\n  _PRINT_HELP=no die \"[Error] Errors were detected while attempting to gather RAM performance metrics.\" 1\r\nfi\r\nif [[ ! -s \"\/tmp\/memData.log\" ]]; then\r\n  _PRINT_HELP=no die \"[Error] No RAM performance metrics were collected.\" 1\r\nfi\r\nif [[ -s \"\/tmp\/totalMemDataErr.log\" ]]; then\r\n  cat \"\/tmp\/totalMemDataErr.log\" &gt;&amp;2\r\n  _PRINT_HELP=no die \"[Error] Errors were detected while attempting to gather RAM performance metrics.\" 1\r\nfi\r\nif [[ ! -s \"\/tmp\/totalMemData.log\" ]]; then\r\n  _PRINT_HELP=no die \"[Error] No RAM performance metrics were collected.\" 1\r\nfi\r\nif [[ -s \"\/tmp\/netDataErr.log\" ]]; then\r\n  cat \"\/tmp\/netDataErr.log\" &gt;&amp;2\r\n  _PRINT_HELP=no die \"[Error] Errors were detected while attempting to gather network performance metrics.\" 1\r\nfi\r\nif [[ ! -s \"\/tmp\/netData.log\" ]]; then\r\n  _PRINT_HELP=no die \"[Error] No network performance metrics were collected.\" 1\r\nfi\r\n\r\n# Indicate the completion of performance metric collection\r\necho \"Finished collecting performance metrics.\"\r\necho \"\"\r\n\r\n# Process and format the total CPU usage metrics\r\necho \"Formatting total CPU results.\"\r\ncpuTotalData=$(\r\n  awk '\r\n    BEGIN {\r\n      # Define the table header for CPU results\r\n      header = \"Average CPU % Used;|;Minimum CPU % Used;|;Maximum CPU % Used\"\r\n    }\r\n    \/^[[:space:]]*CPU usage:\/{\r\n\r\n      # Extract CPU idle percentage and calculate the used percentage\r\n      if ($7 ~ \/[0-9]\/) {\r\n        split($7, cpuIdlePerc,\"%\")\r\n        cpuPerc = 100 - $7\r\n      }\r\n\r\n      # Aggregate metrics\r\n      countCpu += 1\r\n      cpuSum += cpuPerc\r\n\r\n      # Determine minimum and maximum CPU usage\r\n      if(! cpuMin ){ cpuMin = cpuPerc }\r\n      if(cpuPerc &lt; cpuMin){ cpuMin = cpuPerc }\r\n      if(! cpuMax){ cpuMax = cpuPerc }\r\n      if(cpuPerc &gt; cpuMax){ cpuMax = cpuPerc }\r\n    }\r\n    END {\r\n      # Print the formatted CPU usage data\r\n      print header\r\n      avgCpu = cpuSum \/ countCpu\r\n      printf \"%.2f%%;|;%.2f%%;|;%.2f%%\\n\", avgCpu, cpuMin, cpuMax\r\n    }\r\n  ' '\/tmp\/cpuData.log'\r\n)\r\n\r\n# Extract specific CPU metrics (average, minimum, and maximum) from the formatted data\r\nCPUAverage=$(echo \"$cpuTotalData\" | column -t -s \";|;\" | grep \"[0-9]\" | awk '{ print $1 }')\r\nCPUMinimum=$(echo \"$cpuTotalData\" | column -t -s \";|;\" | grep \"[0-9]\" | awk '{ print $2 }')\r\nCPUMaximum=$(echo \"$cpuTotalData\" | column -t -s \";|;\" | grep \"[0-9]\" | awk '{ print $3 }')\r\n\r\n# Process and format CPU usage by process\r\necho \"Formatting CPU process data.\"\r\ncpuProcessData=$(\r\n  awk '\r\n    BEGIN {\r\n      # Define the table header for process-level CPU metrics\r\n      header = \"PID;|;Name;|;Average CPU % Used;|;Minimum CPU % Used;|;Maximum CPU % Used\"\r\n    }\r\n    \/^[[:space:]]*[0-9]\/ &amp;&amp; NF &gt;= 3{\r\n      # Extract process information (PID, name, and CPU usage)\r\n      procId = $1\r\n      gsub(\/[^0-9]\/, \"\", procId)\r\n\r\n      cpuPerc = $2\r\n\r\n      procName = \"\"\r\n      for (i = 3; i &lt;= NF; i++) {\r\n        procName = procName $i \" \"\r\n      }\r\n      gsub(\/^[ \\t\\r\\n]+\/, \"\", procName)\r\n      gsub(\/[ \\t\\r\\n]+$\/, \"\", procName)\r\n\r\n      if (procName == \"nettop\" || procName == \"top\" || procName == \"log\") {\r\n        next\r\n      }\r\n\r\n      # Aggregate CPU metrics for each process\r\n      sumCpu[procId] += cpuPerc\r\n      countCpu[procId] += 1\r\n      pidName[procId] = procName\r\n\r\n      # Determine minimum and maximum CPU usage for each process\r\n      if(! cpuMin[procId] ){ cpuMin[procId] = cpuPerc }\r\n      if(cpuPerc &lt; cpuMin[procId]){ cpuMin[procId] = cpuPerc }\r\n      if(! cpuMax[procId]){ cpuMax[procId] = cpuPerc }\r\n      if(cpuPerc &gt; cpuMax[procId]){ cpuMax[procId] = cpuPerc }\r\n    }\r\n    END {\r\n      # Print the formatted process-level CPU data\r\n      print header\r\n      for (pid in sumCpu) {\r\n        avgCpu = sumCpu[pid] \/ countCpu[pid]\r\n        printf \"%s;|;%s;|;%.2f%%;|;%.2f%%;|;%.2f%%\\n\", pid, pidName[pid], avgCpu, cpuMin[pid], cpuMax[pid]\r\n      }\r\n    }\r\n  ' '\/tmp\/cpuData.log' | {\r\n    # Sort and display the top 6 processes by average CPU usage\r\n    read -r header\r\n    echo \"$header\"\r\n    sed 's\/;|;\/\\\/\/g' |\r\n    sort -t '\/' -k3,3nr |\r\n    sed 's\/\\\/\/;|;\/g'\r\n  } | head -n 6\r\n)\r\n\r\n# Process and format the total RAM usage metrics\r\necho \"Formatting the total RAM results.\"\r\nmemTotalData=$(\r\n  awk '\r\n    BEGIN {\r\n      # Define the table header for RAM results\r\n      header = \"Average RAM % Used;|;Minimum RAM % Used;|;Maximum RAM % Used\"\r\n    }\r\n    \/^[[:space:]]*System-wide\/ {\r\n      # Compute the percentage of RAM used\r\n      gsub(\/%\/, \"\", $5)\r\n      memPerc = 100 - $5\r\n\r\n      # Aggregate metrics\r\n      memSum += memPerc\r\n      memCount += 1\r\n\r\n      # Determine minimum and maximum RAM usage\r\n      if(! memMin ){ memMin = memPerc }\r\n      if(memPerc &lt; memMin){ memMin = memPerc }\r\n      if(! memMax){ memMax = memPerc }\r\n      if(memPerc &gt; memMax){ memMax = memPerc }\r\n    }\r\n    END {\r\n      # Print the formatted RAM usage data\r\n      print header\r\n      avgMem = memSum \/ memCount\r\n      printf \"%.2f%%;|;%.2f%%;|;%.2f%%\\n\", avgMem, memMin, memMax\r\n    }\r\n  ' '\/tmp\/totalMemData.log'\r\n)\r\n\r\n# Extract specific RAM metrics (average, minimum, and maximum) from the formatted data\r\nMEMAverage=$(echo \"$memTotalData\" | column -t -s \";|;\" | grep \"[0-9]\" | awk '{ print $1 }')\r\nMEMMinimum=$(echo \"$memTotalData\" | column -t -s \";|;\" | grep \"[0-9]\" | awk '{ print $2 }')\r\nMEMMaximum=$(echo \"$memTotalData\" | column -t -s \";|;\" | grep \"[0-9]\" | awk '{ print $3 }')\r\n\r\n# Process and format RAM usage by process\r\necho \"Formatting RAM process data.\"\r\nmemProcessData=$(\r\n  awk '\r\n    BEGIN {\r\n      # Define the table header for process-level RAM metrics\r\n      header = \"PID;|;Name;|;Average RAM % Used;|;Minimum RAM % Used;|;Maximum RAM % Used\"\r\n    }\r\n    \/^[[:space:]]*PhysMem:\/ {\r\n      # Extract total and free memory and calculate used percentage\r\n      memFreeHumanFriendly = $8\r\n      memUsedHumanFriendly = $2\r\n\r\n      # Split the free memory string to isolate the numeric part (ignoring non-digits).\r\n      split(memFreeHumanFriendly, memFreeNumber,\/[^0-9]\/)\r\n\r\n      # Similarly, split the used memory string.\r\n      split(memUsedHumanFriendly, memUsedNumber,\/[^0-9]\/)\r\n\r\n      # Convert the free memory value to bytes based on the unit (T, G, M, or K).\r\n      if(memFreeHumanFriendly ~ \/T\/){\r\n        memFreeBytes = memFreeNumber[1] * 1000000000000\r\n      }\r\n      if(memFreeHumanFriendly ~ \/G\/){\r\n        memFreeBytes = memFreeNumber[1] * 1000000000\r\n      }\r\n      if(memFreeHumanFriendly ~ \/M\/){\r\n        memFreeBytes = memFreeNumber[1] * 1000000\r\n      }\r\n      if(memFreeHumanFriendly ~ \/K\/){\r\n        memFreeBytes = memFreeNumber[1] * 1000\r\n      }\r\n\r\n      # Convert the used memory value to bytes based on the unit (T, G, M, or K).\r\n      if(memUsedHumanFriendly ~ \/T\/){\r\n        memUsedBytes = memUsedNumber[1] * 1000000000000\r\n      }\r\n      if(memUsedHumanFriendly ~ \/G\/){\r\n        memUsedBytes = memUsedNumber[1] * 1000000000\r\n      }\r\n      if(memUsedHumanFriendly ~ \/M\/){\r\n        memUsedBytes = memUsedNumber[1] * 1000000\r\n      }\r\n      if(memUsedHumanFriendly ~ \/K\/){\r\n        memUsedBytes = memUsedNumber[1] * 1000\r\n      }\r\n\r\n      # Calculate the total system memory in bytes.\r\n      memTotal = memUsedBytes + memFreeBytes\r\n    }\r\n    \/^[[:space:]]*[0-9]\/ &amp;&amp; NF &gt;= 3 {\r\n      # Extract the process ID (PID) from the first field and remove any non-digit characters.\r\n      procId = $1\r\n      gsub(\/[^0-9]\/, \"\", procId)\r\n\r\n      # Concatenate fields 3 to NF to build the process name.\r\n      procName = \"\"\r\n      for (i = 3; i &lt;= NF; i++) {\r\n        procName = procName $i \" \"\r\n      }\r\n\r\n      # Trim leading and trailing whitespace from the process name.\r\n      gsub(\/^[ \\t\\r\\n]+\/, \"\", procName)\r\n      gsub(\/[ \\t\\r\\n]+$\/, \"\", procName)\r\n\r\n      # Skip processing for specific process names that are not relevant.\r\n      if (procName == \"nettop\" || procName == \"top\" || procName == \"log\") {\r\n        next\r\n      }\r\n\r\n      # Extract the process-specific used memory (in a human-friendly format) from the second field.\r\n      memProcessUsedHumanFriendly = $2\r\n\r\n      # Split the process used memory string to extract the numeric part.\r\n      split(memProcessUsedHumanFriendly, memProcessUsedNumber,\/[^0-9]\/)\r\n\r\n      # Convert the process used memory value to bytes based on its unit.\r\n      if(memProcessUsedHumanFriendly ~ \/T\/){\r\n        memProcessUsedBytes = memProcessUsedNumber[1] * 1000000000000\r\n      }\r\n      if(memProcessUsedHumanFriendly ~ \/G\/){\r\n        memProcessUsedBytes = memProcessUsedNumber[1] * 1000000000\r\n      }\r\n      if(memProcessUsedHumanFriendly ~ \/M\/){\r\n        memProcessUsedBytes = memProcessUsedNumber[1] * 1000000\r\n      }\r\n      if(memProcessUsedHumanFriendly ~ \/K\/){\r\n        memProcessUsedBytes = memProcessUsedNumber[1] * 1000\r\n      }\r\n\r\n      # Calculate the percentage of system RAM used by this process.\r\n      ramPerc = ( memProcessUsedBytes \/ memTotal ) * 100\r\n\r\n      # Aggregate RAM metrics for each process\r\n      sumRam[procId] += ramPerc\r\n      countRam[procId] += 1\r\n      pidName[procId] = procName\r\n\r\n      # Determine minimum and maximum RAM usage for each process\r\n      if(! ramMin[procId] ){ ramMin[procId] = ramPerc }\r\n      if(ramPerc &lt; ramMin[procId]){ ramMin[procId] = ramPerc }\r\n      if(! ramMax[procId]){ ramMax[procId] = ramPerc }\r\n      if(ramPerc &gt; ramMax[procId]){ ramMax[procId] = ramPerc }\r\n    }\r\n    END {\r\n      # Print the formatted process-level RAM data\r\n      print header\r\n      for (pid in sumRam) {\r\n        avgRam = sumRam[pid] \/ countRam[pid]\r\n        printf \"%s;|;%s;|;%.2f%%;|;%.2f%%;|;%.2f%%\\n\", pid, pidName[pid], avgRam, ramMin[pid], ramMax[pid]\r\n      }\r\n    }\r\n  ' '\/tmp\/memData.log' | {\r\n    # Sort and display the top 6 processes by average RAM usage\r\n    read -r header\r\n    echo \"$header\"\r\n    sed 's\/;|;\/\\\/\/g' |\r\n    sort -t '\/' -k3,3nr |\r\n    sed 's\/\\\/\/;|;\/g'\r\n  } | head -n 6\r\n)\r\n\r\n# Process and format network usage metrics\r\necho \"Formatting the network usage data.\"\r\nnetData=$(\r\n  awk -F \",\" '\r\n    BEGIN {\r\n      # Define the table header for network usage metrics\r\n      header = \"Network Adapter;|;MAC Address;|;Type;|;Average Sent &amp; Received;|;Minimum Sent &amp; Received;|;Maximum Sent &amp; Received\"\r\n\r\n      # Set SUBSEP to \"|\" for multidimensional array keys.\r\n      SUBSEP = \"|\"\r\n    }\r\n    # Process only lines that have a non-empty 3rd field and start with a digit (ignoring leading spaces)\r\n    length($3) &gt; 0 &amp;&amp; \/^[[:space:]]*[0-9]\/{\r\n      netAdapter = $3\r\n      receivedBytes = $5\r\n      transmittedBytes = $6\r\n\r\n      # Split the first field (time) using \":\" as a delimiter.\r\n      split($1, time, \":\")\r\n\r\n      # Skip the loopback interface \"lo0\".\r\n      if (netAdapter == \"lo0\"){\r\n        next\r\n      }\r\n\r\n      # Calculate total bits transferred: add received and transmitted bytes, then multiply by 8.\r\n      totalBits = (receivedBytes + transmittedBytes) * 8\r\n\r\n      # Convert total bits to megabits.\r\n      totalMegabits = totalBits \/ 1000000\r\n\r\n      # Aggregate metrics\r\n      total[netAdapter,time[2]] += totalMegabits\r\n      adapterName[netAdapter,time[2]] = netAdapter\r\n    }\r\n    END {\r\n      # Print the formatted network usage data\r\n      print header\r\n\r\n      # Loop over each unique composite key from the adapterName array.\r\n      for (adapter in adapterName) {\r\n        # Map the composite key to the adapter name for aggregation.\r\n        name[adapterName[adapter]] = adapterName[adapter]\r\n\r\n        # Calculate the per-second average usage for this time slice by dividing by 60 seconds.\r\n        perSecondAvg = (total[adapter] \/ 60)\r\n\r\n        # Sum up the per-second averages for each adapter.\r\n        netSum[adapterName[adapter]] += perSecondAvg\r\n        countAdapter[adapterName[adapter]] +=1\r\n\r\n        # Get the minimum and maximum\r\n        if(! netMin[adapterName[adapter]]){ netMin[adapterName[adapter]] = perSecondAvg }\r\n        if(perSecondAvg &lt; netMin[adapterName[adapter]]){ netMin[adapterName[adapter]] = perSecondAvg }\r\n        if(! netMax[adapterName[adapter]]){ netMax[adapterName[adapter]] = perSecondAvg }\r\n        if(perSecondAvg &gt; netMax[adapterName[adapter]]){ netMax[adapterName[adapter]] = perSecondAvg }\r\n      }\r\n\r\n      # Loop over each aggregated adapter.\r\n      for (combinedAdapter in netSum) {\r\n        # Run ifconfig command to fetch the MAC address for the adapter.\r\n        ifconfig = \"ifconfig \" name[combinedAdapter]\r\n\r\n        while ((ifconfig | getline line) &gt; 0) {\r\n          # Look for the line containing the \"ether\" keyword.\r\n          if (line ~ \/ether\/) {\r\n            numberOfFields = split(line, field, \" \")\r\n\r\n            # Loop through the fields to find the MAC address following \"ether\".\r\n            for (i = 1; i &lt;= numberOfFields; i++) {\r\n              if (field[i] ~ \/ether\/) {\r\n                adapterMac = field[i+1]\r\n              }\r\n            }\r\n          }\r\n        }\r\n        close(ifconfig)\r\n\r\n        # Run networksetup command to determine the adapter type.\r\n        # It searches for the hardware port corresponding to the adapter name.\r\n        networkSetup = \"networksetup -listallhardwareports | grep -B 1 \\\"\" name[combinedAdapter] \"\\\" | head -n 1 | sed \\\"s\/Hardware Port: \/\/\\\"\"\r\n\r\n        while ((networkSetup | getline line) &gt; 0) {\r\n          # If the line contains \"Wi-Fi\", set the adapter type accordingly.\r\n          if (line ~ \/Wi-Fi\/){\r\n            adapterType = \"Wi-Fi\"\r\n          } else if (line ~ \/Ethernet Adapter\/) {\r\n            adapterType = \"Wired\"\r\n          }else {\r\n            adapterType = \"Other\"\r\n          }\r\n        }\r\n        close(networkSetup)\r\n\r\n        # Calculate the average network usage for this adapter.\r\n        avgNet = netSum[combinedAdapter] \/ countAdapter[combinedAdapter]\r\n\r\n        # Print the final results in CSV format:\r\n        # Adapter name, MAC address, adapter type, average, minimum, and maximum usage (appended with \"Mbps\").\r\n        printf \"%s;|;%s;|;%s;|;%.2f Mbps;|;%.2f Mbps;|;%.2f Mbps\\n\", name[combinedAdapter], adapterMac, adapterType, avgNet, netMin[combinedAdapter], netMax[combinedAdapter]\r\n      }\r\n    }\r\n  ' '\/tmp\/netData.log'\r\n)\r\n\r\necho \"Formatting disk results.\"\r\ni=0\r\napfsContainers=$(\/usr\/libexec\/PlistBuddy -c 'print' \/tmp\/diskData.log | grep \"ContainerReference\" | sed \"s\/ContainerReference =\/\/\" | xargs)\r\ndiskData=\"Volume;|;Name;|;Mount Point;|;Consumed Space;|;Container;|;Free Space;|;Total Space;|;Physical Disk;|;Media Type\"\r\nfor container in $apfsContainers; do\r\n  apfsVolumes=$(\/usr\/libexec\/PlistBuddy -c \"Print ':Containers:$i:Volumes'\" \/tmp\/diskData.log | grep \"DeviceIdentifier\" | sed \"s\/DeviceIdentifier =\/\/\" | xargs)\r\n  containerFreeSpace=$(\/usr\/libexec\/PlistBuddy -c \"Print :Containers:$i:CapacityFree\" \/tmp\/diskData.log)\r\n  containerTotalSize=$(\/usr\/libexec\/PlistBuddy -c \"Print :Containers:$i:CapacityCeiling\" \/tmp\/diskData.log)\r\n\r\n  diskInfo=$(diskutil info \"$container\")\r\n  physicalDisk=$(echo \"$diskInfo\" | grep \"Device \/ Media Name:\" | sed \"s\/Device \\\/ Media Name:\/\/g\" | xargs)\r\n  if echo \"$diskInfo\" | grep \"Removable Media:\" | sed \"s\/Removable Media:\/\/g\" | grep \"Removable\"; then\r\n    continue\r\n  fi\r\n\r\n  if echo \"$diskInfo\" | grep \"Solid State\" | grep \"Yes\" &gt; \/dev\/null 2&gt; \/dev\/null; then\r\n    deviceType=\"SSD\"\r\n  elif echo \"$diskInfo\" | grep \"Solid State\" | grep \"No\" &gt; \/dev\/null 2&gt; \/dev\/null; then\r\n    deviceType=\"HDD\"\r\n  else\r\n    deviceType=\"Unspecified\"\r\n  fi\r\n\r\n  v=0\r\n  for volume in $apfsVolumes; do\r\n    volumeInfo=$(diskutil info \"$volume\")\r\n    mountPoint=$(echo \"$volumeInfo\" | grep \"Mount Point:\" | sed \"s\/Mount Point:\/\/g\" | xargs)\r\n    volumeName=$(\/usr\/libexec\/PlistBuddy -c \"Print ':Containers:$i:Volumes:$v:Name'\" \/tmp\/diskData.log)\r\n    consumedSpace=$(\/usr\/libexec\/PlistBuddy -c \"Print ':Containers:$i:Volumes:$v:CapacityInUse'\" \/tmp\/diskData.log)\r\n    capacityQuota=$(\/usr\/libexec\/PlistBuddy -c \"Print ':Containers:$i:Volumes:$v:CapacityQuota'\" \/tmp\/diskData.log)\r\n\r\n    if [[ \"$capacityQuota\" == 0 ]]; then\r\n      freeSpace=\"$containerFreeSpace\"\r\n      totalSpace=$containerTotalSize\r\n    else\r\n      freeSpace=$((capacityQuota - consumedSpace))\r\n      totalSpace=$capacityQuota\r\n    fi\r\n\r\n    if [[ -n \"$totalSpace\" ]]; then\r\n      consumedSpacePercentage=$(echo \"scale=6; $consumedSpace \/ $totalSpace\" | bc)\r\n      consumedSpacePercentage=$(echo \"scale=6; $consumedSpacePercentage * 100\" | bc)\r\n      consumedSpacePercentage=$(printf \"%.2f\" \"$consumedSpacePercentage\")\r\n      consumedSpacePercentage=${consumedSpacePercentage\/\/.00\/}\r\n\r\n      totalSpaceInGB=$(ConvertTo-FriendlySize \"$totalSpace\")\r\n    fi\r\n\r\n    consumedSpaceInGB=$(ConvertTo-FriendlySize \"$consumedSpace\")\r\n    freeSpaceInGB=$(ConvertTo-FriendlySize \"$freeSpace\")\r\n\r\n    if [[ -z \"$mountPoint\" ]]; then\r\n      mountPoint=\"N\/A\"\r\n    fi\r\n\r\n    diskData+=$'\\n'\"$volume;|;$volumeName;|;$mountPoint;|;$consumedSpaceInGB ($consumedSpacePercentage%);|;$container;|;$freeSpaceInGB;|;$totalSpaceInGB;|;$physicalDisk;|;$deviceType\"\r\n    ((v++))\r\n  done\r\n  ((i++))\r\ndone\r\n\r\n# Check if the journal log file exists and is not empty\r\nif [[ -f \"\/tmp\/syslog.log\" &amp;&amp; -s \"\/tmp\/syslog.log\" ]]; then\r\n  # Count the number of lines in the journal log file\r\n  journalLines=$(wc -l \/tmp\/syslog.log | grep -o \"[0-9]*\" | xargs)\r\n\r\n  # If there are more than one line in the journal log, process the log entries\r\n  if [[ \"$journalLines\" -ge 1 ]]; then\r\n    echo \"Formatting event log entries.\"\r\n\r\n    # Use `awk` to process the journal log and extract relevant fields\r\n    errorEvents=$(\r\n      awk '\r\n      BEGIN {\r\n        # Define the header for the formatted output\r\n        header = \"Service;|;PID;|;Time Created;|;Message\"\r\n        print header\r\n      }\r\n      \/^[0-9]\/ {\r\n        time = $2\r\n        gsub(\/\\.[0-9]+\/, \"\", time)\r\n\r\n        if ( $1 !~ \/^[0-9]{4}-[0-9]{2}-[0-9]{2}\/ ){\r\n          next\r\n        }\r\n\r\n        # Convert the timestamp to epoch format\r\n        dateCmd = \"date -j -f \\\"%Y-%m-%d %H:%M:%S%z\\\" \\\"\" $1 \" \" time \"\\\" \\\"+%x %r\\\"\"\r\n        dateCmd | getline date\r\n        close(dateCmd)\r\n\r\n        # Extract the service, PID, and message from the log entry\r\n        if ($4 ~ \/^.*\\[[0-9]*\\]:\/) {\r\n          split($4, servicePart, \"[\")\r\n          service = servicePart[1]\r\n\r\n          pid = substr(servicePart[2], 1, length(servicePart[2])-2)\r\n          split($0, messagePart, \"]: \")\r\n\r\n          message = messagePart[2]\r\n        }else{\r\n          split($4, servicePart, \":\")\r\n          service = servicePart[1]\r\n          pid = \"N\/A\"\r\n\r\n          split($0, messagePart, service\": \")\r\n\r\n          message = messagePart[2]\r\n        }\r\n\r\n        # Output the formatted data as a semicolon-separated string\r\n        print service \";|;\" pid \";|;\" date \";|;\" message\r\n      }\r\n    ' '\/tmp\/syslog.log'\r\n    )\r\n  fi\r\nfi\r\n\r\necho \"\"\r\necho \"Collecting system information.\"\r\n\r\n# Retrieve the CPU model name\r\nif ! CPU=$(sysctl -n machdep.cpu.brand_string); then\r\n  echo \"[Error] Failed to retrieve the cpu information.\" &gt;&amp;2\r\n  exitCode=1\r\nfi\r\n\r\n# Retrieve the total memory installed on the system in GiB\r\nif ! TotalMemory=$(sysctl -n hw.memsize | awk '{printf \"%.2f GiB\\n\", $1\/1024^3}'); then\r\n  echo \"[Error] Failed to retrieve the RAM information.\" &gt;&amp;2\r\n  exitCode=1\r\nfi\r\n\r\n# Define the list of log files to clean up\r\nlogPaths=(\"\/tmp\/cpuData.log\" \"\/tmp\/cpuDataErr.log\" \"\/tmp\/diskData.log\" \"\/tmp\/diskDataErr.log\" \"\/tmp\/syslog.log\" \"syslogErr.log\" \"\/tmp\/memData.log\" \"\/tmp\/memDataErr.log\" \"\/tmp\/totalMemData.log\" \"\/tmp\/totalMemDataErr.log\" \"\/tmp\/netData.log\" \"\/tmp\/netDataErr.log\")\r\n\r\n# Iterate over the log files and remove each if it exists\r\nfor logFile in \"${logPaths[@]}\"; do\r\n  if [[ -f \"$logFile\" ]]; then\r\n    # Attempt to remove the log file and handle errors if it fails\r\n    if ! rm \"$logFile\"; then\r\n      echo \"[Error] Failed to remove the log file at '$logFile'.\" &gt;&amp;2\r\n      exitCode=1\r\n    fi\r\n  fi\r\ndone\r\n\r\n# Display the CPU information and formatted total CPU usage data\r\necho \"\"\r\necho \"### $CPU ###\"\r\necho \"$cpuTotalData\" | column -t -s \";|;\"\r\n\r\n# Display the total memory installed and the formatted total memory usage data\r\necho \"\"\r\necho \"### Memory Usage ###\"\r\necho \"Total Memory Installed: $TotalMemory\"\r\necho \"$memTotalData\" | column -t -s \";|;\"\r\n\r\n# Display the top 5 CPU-consuming processes\r\necho \"\"\r\necho \"### Top 5 CPU Processes ###\"\r\necho \"$cpuProcessData\" | column -t -s \";|;\"\r\n\r\n# Display the top 5 RAM-consuming processes\r\necho \"\"\r\necho \"### Top 5 RAM Processes ###\"\r\necho \"$memProcessData\" | column -t -s \";|;\"\r\n\r\n# Display the formatted network usage data\r\necho \"\"\r\necho \"### Network Usage ###\"\r\necho \"$netData\" | column -t -s \";|;\"\r\n\r\n# Display the formatted disk usage data\r\necho \"\"\r\necho \"### Disk Usage ###\"\r\n# Output disk data as a list instead of a table\r\necho \"$diskData\" | tail -n +2 | awk -F';[|];' '\r\n{\r\n  print \"Volume: \" $1\r\n  print \"Name: \" $2\r\n  print \"Mount Point: \" $3\r\n  print \"Consumed Space: \" $4\r\n  print \"Container: \" $5\r\n  print \"Free Space: \" $6\r\n  print \"Total Space: \" $7\r\n  print \"Physical Disk: \" $8\r\n  print \"Media Type: \" $9\r\n  print \"\"\r\n}'\r\n\r\n# Check if a custom field has been provided\r\nif [[ -n \"$_arg_wysiwygCustomField\" ]]; then\r\n  echo \"The custom field '$_arg_wysiwygCustomField' has been provided.\"\r\n\r\n  # Convert CPU process data to HTML format\r\n  echo \"Converting CPU process data into HTML.\"\r\n  cpuProcessMetricTable=$(echo \"$cpuProcessData\" | convertToHTMLTable -d \";[|];\")\r\n  cpuProcessMetricTable=${cpuProcessMetricTable\/\/\"&lt;th&gt;\"\/\"&lt;th&gt;&lt;b&gt;\"}\r\n  cpuProcessMetricTable=\"${cpuProcessMetricTable\/\/&lt;\\\/th&gt;\/&lt;\/b&gt;&lt;\/th&gt;}\"\r\n  cpuProcessMetricTable=\"${cpuProcessMetricTable\/\/&lt;table&gt;\/&lt;table&gt;&lt;caption style='border-top: 1px; border-left: 1px; border-right: 1px; border-style: solid; border-color: #CAD0D6'&gt;&lt;b&gt;Top 5 CPU Processes&lt;\/b&gt;&lt;\/caption&gt;}\"\r\n  cpuProcessMetricTable=${cpuProcessMetricTable\/\/\"Average CPU % Used\"\/\"&lt;i class='fa-solid fa-arrow-down-up-across-line'&gt;&lt;\/i&gt;&amp;nbsp;&amp;nbsp;Average CPU % Used\"}\r\n  cpuProcessMetricTable=${cpuProcessMetricTable\/\/\"Minimum CPU % Used\"\/\"&lt;i class='fa-solid fa-arrows-down-to-line'&gt;&lt;\/i&gt;&amp;nbsp;&amp;nbsp;Minimum CPU % Used\"}\r\n  cpuProcessMetricTable=${cpuProcessMetricTable\/\/\"Maximum CPU % Used\"\/\"&lt;i class='fa-solid fa-arrows-up-to-line'&gt;&lt;\/i&gt;&amp;nbsp;&amp;nbsp;Maximum CPU % Used\"}\r\n\r\n  # Apply thresholds for CPU metrics to highlight warnings and danger levels\r\n  cpuProcessMetricTable=$(\r\n    echo \"$cpuProcessMetricTable\" | awk '\r\n      BEGIN {\r\n        dangerThreshold = 50.00\r\n        warningThreshold = 20.00\r\n      }\r\n      {\r\n        if ($0 ~ \/&lt;tr&gt;&lt;td&gt;\/) {\r\n          split($0, tableData, \"&lt;td&gt;\")\r\n\r\n          avg = tableData[4]\r\n          min = tableData[5]\r\n          max = tableData[6]\r\n\r\n          gsub(\/[^0-9.]\/, \"\", avg)\r\n          gsub(\/[^0-9.]\/, \"\", min)\r\n          gsub(\/[^0-9.]\/, \"\", max)\r\n\r\n          avg += 0\r\n          min += 0\r\n          max += 0\r\n\r\n          # Highlight rows based on thresholds\r\n          if (avg &gt; dangerThreshold || min &gt; dangerThreshold || max &gt; dangerThreshold) {\r\n            sub(\/&lt;tr&gt;\/, \"&lt;tr class='\\''danger'\\''&gt;\");\r\n          } else if (avg &gt; warningThreshold || min &gt; warningThreshold || max &gt; warningThreshold) {\r\n            sub(\/&lt;tr&gt;\/, \"&lt;tr class='\\''warning'\\''&gt;\");\r\n          }\r\n        }\r\n\r\n        print $0\r\n      }\r\n    '\r\n  )\r\n\r\n  # Convert RAM process data to HTML format\r\n  echo \"Converting RAM process data into HTML.\"\r\n  ramProcessMetricTable=$(echo \"$memProcessData\" | convertToHTMLTable -d \";[|];\")\r\n  ramProcessMetricTable=${ramProcessMetricTable\/\/\"&lt;th&gt;\"\/\"&lt;th&gt;&lt;b&gt;\"}\r\n  ramProcessMetricTable=\"${ramProcessMetricTable\/\/&lt;\\\/th&gt;\/&lt;\/b&gt;&lt;\/th&gt;}\"\r\n  ramProcessMetricTable=\"${ramProcessMetricTable\/\/&lt;table&gt;\/&lt;table&gt;&lt;caption style='border-top: 1px; border-left: 1px; border-right: 1px; border-style: solid; border-color: #CAD0D6'&gt;&lt;b&gt;Top 5 RAM Processes&lt;\/b&gt;&lt;\/caption&gt;}\"\r\n  ramProcessMetricTable=${ramProcessMetricTable\/\/\"Average RAM % Used\"\/\"&lt;i class='fa-solid fa-arrow-down-up-across-line'&gt;&lt;\/i&gt;&amp;nbsp;&amp;nbsp;Average RAM % Used\"}\r\n  ramProcessMetricTable=${ramProcessMetricTable\/\/\"Minimum RAM % Used\"\/\"&lt;i class='fa-solid fa-arrows-down-to-line'&gt;&lt;\/i&gt;&amp;nbsp;&amp;nbsp;Minimum RAM % Used\"}\r\n  ramProcessMetricTable=${ramProcessMetricTable\/\/\"Maximum RAM % Used\"\/\"&lt;i class='fa-solid fa-arrows-up-to-line'&gt;&lt;\/i&gt;&amp;nbsp;&amp;nbsp;Maximum RAM % Used\"}\r\n\r\n  # Apply thresholds for RAM metrics to highlight warnings and danger levels\r\n  ramProcessMetricTable=$(\r\n    echo \"$ramProcessMetricTable\" | awk '\r\n      BEGIN {\r\n        dangerThreshold = 30.00\r\n        warningThreshold = 10.00\r\n      }\r\n      {\r\n        if ($0 ~ \/&lt;tr&gt;&lt;td&gt;\/) {\r\n          split($0, tableData, \"&lt;td&gt;\")\r\n\r\n          avg = tableData[4]\r\n          min = tableData[5]\r\n          max = tableData[6]\r\n\r\n          gsub(\/[^0-9.]\/, \"\", avg)\r\n          gsub(\/[^0-9.]\/, \"\", min)\r\n          gsub(\/[^0-9.]\/, \"\", max)\r\n\r\n          avg += 0\r\n          min += 0\r\n          max += 0\r\n\r\n          # Highlight rows based on thresholds\r\n          if (avg &gt; dangerThreshold || min &gt; dangerThreshold || max &gt; dangerThreshold) {\r\n            sub(\/&lt;tr&gt;\/, \"&lt;tr class='\\''danger'\\''&gt;\");\r\n          } else if (avg &gt; warningThreshold || min &gt; warningThreshold || max &gt; warningThreshold) {\r\n            sub(\/&lt;tr&gt;\/, \"&lt;tr class='\\''warning'\\''&gt;\");\r\n          }\r\n        }\r\n\r\n        print $0\r\n      }\r\n    '\r\n  )\r\n\r\n  echo \"Converting network usage data into HTML.\"\r\n\r\n  # Apply formatting to the network usage table\r\n  networkInterfaceUsage=$(echo \"$netData\" | convertToHTMLTable -d \";[|];\")\r\n  networkInterfaceUsage=${networkInterfaceUsage\/\/\"&lt;th&gt;\"\/\"&lt;th&gt;&lt;b&gt;\"}\r\n  networkInterfaceUsage=\"${networkInterfaceUsage\/\/&lt;\\\/th&gt;\/&lt;\/b&gt;&lt;\/th&gt;}\"\r\n  networkInterfaceUsage=\"${networkInterfaceUsage\/\/&lt;table&gt;\/&lt;table&gt;&lt;caption style='border-top: 1px; border-left: 1px; border-right: 1px; border-style: solid; border-color: #CAD0D6'&gt;&lt;b&gt;Network Usage&lt;\/b&gt;&lt;\/caption&gt;}\"\r\n  networkInterfaceUsage=${networkInterfaceUsage\/\/\"Average Sent &amp; Received\"\/\"&lt;i class='fa-solid fa-arrow-down-up-across-line'&gt;&lt;\/i&gt;&amp;nbsp;&amp;nbsp;Average Sent &amp; Received\"}\r\n  networkInterfaceUsage=${networkInterfaceUsage\/\/\"Minimum Sent &amp; Received\"\/\"&lt;i class='fa-solid fa-arrows-down-to-line'&gt;&lt;\/i&gt;&amp;nbsp;&amp;nbsp;Minimum Sent &amp; Received\"}\r\n  networkInterfaceUsage=${networkInterfaceUsage\/\/\"Maximum Sent &amp; Received\"\/\"&lt;i class='fa-solid fa-arrows-up-to-line'&gt;&lt;\/i&gt;&amp;nbsp;&amp;nbsp;Maximum Sent &amp; Received\"}\r\n  networkInterfaceUsage=\"${networkInterfaceUsage\/\/&lt;th&gt;&lt;b&gt;Type&lt;\\\/b&gt;&lt;\\\/th&gt;\/&lt;th&gt;&lt;b&gt;&lt;i class='fa-solid fa-network-wired'&gt;&lt;\/i&gt;&amp;nbsp;&amp;nbsp;Type&lt;\/b&gt;&lt;\/th&gt;}\"\r\n  networkInterfaceUsage=\"${networkInterfaceUsage\/\/&lt;td&gt;Wired&lt;\\\/td&gt;\/&lt;td&gt;&lt;i class='fa-solid fa-ethernet'&gt;&lt;\/i&gt;&amp;nbsp;&amp;nbsp;Wired&lt;\/td&gt;}\"\r\n  networkInterfaceUsage=\"${networkInterfaceUsage\/\/&lt;td&gt;Wi-Fi&lt;\\\/td&gt;\/&lt;td&gt;&lt;i class='fa-solid fa-wifi'&gt;&lt;\/i&gt;&amp;nbsp;&amp;nbsp;Wi-Fi&lt;\/td&gt;}\"\r\n  networkInterfaceUsage=\"${networkInterfaceUsage\/\/&lt;td&gt;Other&lt;\\\/td&gt;\/&lt;td&gt;&lt;i class='fa-solid fa-circle-question'&gt;&lt;\/i&gt;&amp;nbsp;&amp;nbsp;Other&lt;\/td&gt;}\"\r\n\r\n  # Apply threshold-based row highlighting for network usage data\r\n  networkInterfaceUsage=$(\r\n    echo \"$networkInterfaceUsage\" | awk '\r\n      BEGIN {\r\n        dangerThreshold = 100.00\r\n        warningThreshold = 10.00\r\n      }\r\n      {\r\n        if ($0 ~ \/&lt;tr&gt;&lt;td&gt;\/) {\r\n          split($0, tableData, \"&lt;td&gt;\")\r\n\r\n          avg = tableData[5]\r\n          min = tableData[6]\r\n          max = tableData[7]\r\n          networkType = tableData[4]\r\n\r\n          gsub(\/&amp;nbsp;\/, \"\", networkType)\r\n          gsub(\/&lt;i.*&gt;&lt;\\\/i&gt;\/, \"\", networkType)\r\n          gsub(\/&lt;\\\/td&gt;\/, \"\", networkType)\r\n\r\n          gsub(\/[^0-9.]\/, \"\", avg)\r\n          gsub(\/[^0-9.]\/, \"\", min)\r\n          gsub(\/[^0-9.]\/, \"\", max)\r\n\r\n          avg += 0\r\n          min += 0\r\n          max += 0\r\n\r\n          # Highlight rows based on thresholds\r\n          if (avg &gt; dangerThreshold || min &gt; dangerThreshold || max &gt; dangerThreshold) {\r\n            sub(\/&lt;tr&gt;\/, \"&lt;tr class='\\''danger'\\''&gt;\");\r\n          } else if (avg &gt; warningThreshold || min &gt; warningThreshold || max &gt; warningThreshold || networkType != \"Wired\" ) {\r\n            sub(\/&lt;tr&gt;\/, \"&lt;tr class='\\''warning'\\''&gt;\");\r\n          }\r\n        }\r\n\r\n        print $0\r\n      }\r\n    '\r\n  )\r\n\r\n  echo \"Converting disk usage data into HTML.\"\r\n  diskTable=$(echo \"$diskData\" | convertToHTMLTable -d \";[|];\")\r\n\r\n  # Apply formatting to the disk usage table\r\n  diskTable=${diskTable\/\/\"&lt;th&gt;\"\/\"&lt;th&gt;&lt;b&gt;\"}\r\n  diskTable=\"${diskTable\/\/&lt;\\\/th&gt;\/&lt;\/b&gt;&lt;\/th&gt;}\"\r\n  diskTable=\"${diskTable\/\/&lt;table&gt;\/&lt;table&gt;&lt;caption style='border-top: 1px; border-left: 1px; border-right: 1px; border-style: solid; border-color: #CAD0D6'&gt;&lt;b&gt;Disk Usage&lt;\/b&gt;&lt;\/caption&gt;}\"\r\n\r\n  # Apply threshold-based row highlighting for disk usage data\r\n  diskTable=$(\r\n    echo \"$diskTable\" | awk '\r\n      BEGIN {\r\n        dangerThreshold = 90.00\r\n        warningThreshold = 75.00\r\n      }\r\n      {\r\n        if ($0 ~ \/&lt;tr&gt;&lt;td&gt;\/) {\r\n          split($0, tableData, \"&lt;td&gt;\")\r\n\r\n          consumedSpace = tableData[5]\r\n          mediaType = tableData[10]\r\n\r\n          gsub(\/^.*\\(\/, \"\", consumedSpace)\r\n          gsub(\/\\).*$\/, \"\", consumedSpace)\r\n          gsub(\/%\/, \"\", consumedSpace)\r\n          consumedSpace += 0.00\r\n\r\n          gsub(\/&lt;\\\/td&gt;.*\/, \"\", mediaType)\r\n\r\n          # Highlight rows based on thresholds and media type\r\n          if (consumedSpace &gt; dangerThreshold || (mediaType != \"SSD\" &amp;&amp; mediaType != \"Unspecified\" )) {\r\n            sub(\/&lt;tr&gt;\/, \"&lt;tr class='\\''danger'\\''&gt;\");\r\n          } else if (consumedSpace &gt; warningThreshold) {\r\n            sub(\/&lt;tr&gt;\/, \"&lt;tr class='\\''warning'\\''&gt;\");\r\n          }\r\n        }\r\n\r\n        print $0\r\n      }\r\n    '\r\n  )\r\n\r\n  # Record the completion date and time\r\n  reportCompleteDate=$(date \"+%x %r\")\r\n\r\n  # Assemble the HTML card for the report\r\n  echo \"Assembling the html data into the html card\"\r\n  htmlCard=\"&lt;div class='card flex-grow-1'&gt;\r\n  &lt;div class='card-title-box'&gt;\r\n    &lt;div class='card-title'&gt;&lt;i class='fa-solid fa-gauge-high'&gt;&lt;\/i&gt;&amp;nbsp;&amp;nbsp;System Performance Metrics&lt;\/div&gt;\r\n  &lt;\/div&gt;\r\n  &lt;div class='card-body' style='white-space: nowrap'&gt;\r\n    &lt;div class='container' style='padding-left: 0px'&gt;\r\n      &lt;div class='row'&gt;\r\n        &lt;div class='col-sm'&gt;\r\n          &lt;p class='card-text'&gt;&lt;b&gt;Start Date and Time&lt;\/b&gt;&lt;br&gt;$reportStartDate&lt;\/p&gt;\r\n        &lt;\/div&gt;\r\n        &lt;div class='col-sm'&gt;\r\n          &lt;p class='card-text'&gt;&lt;b&gt;Completed Date and Time&lt;\/b&gt;&lt;br&gt;$reportCompleteDate&lt;\/p&gt;\r\n        &lt;\/div&gt;\r\n      &lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n    &lt;p id='lastStartup' class='card-text'&gt;&lt;b&gt;Last Startup Time&lt;\/b&gt;&lt;br&gt;$lastStartupDate&lt;\/p&gt;\r\n    &lt;p&gt;&lt;b&gt;$CPU&lt;\/b&gt;&lt;\/p&gt;\r\n    &lt;div class='container'&gt;\r\n      &lt;div class='row' style='padding-left: 0px'&gt;\r\n        &lt;div class='col-md'&gt;\r\n          &lt;div class='stat-card' style='display: flex;'&gt;\r\n            &lt;div class='stat-value' id='cpuOverallAvg' style='color: #008001;'&gt;$CPUAverage&lt;\/div&gt;\r\n            &lt;div class='stat-desc'&gt;&lt;i class='fa-solid fa-arrow-down-up-across-line'&gt;&lt;\/i&gt;&amp;nbsp;&amp;nbsp;Average CPU % Used&lt;\/div&gt;\r\n          &lt;\/div&gt;\r\n        &lt;\/div&gt;\r\n        &lt;div class='col-md'&gt;\r\n          &lt;div class='stat-card' style='display: flex;'&gt;\r\n            &lt;div class='stat-value' id='cpuOverallMin' style='color: #008001;'&gt;$CPUMinimum&lt;\/div&gt;\r\n            &lt;div class='stat-desc'&gt;&lt;i class='fa-solid fa-arrows-down-to-line'&gt;&lt;\/i&gt;&amp;nbsp;&amp;nbsp;Minimum CPU % Used&lt;\/div&gt;\r\n          &lt;\/div&gt;\r\n        &lt;\/div&gt;\r\n        &lt;div class='col-md'&gt;\r\n          &lt;div class='stat-card' style='display: flex;'&gt;\r\n            &lt;div class='stat-value' id='cpuOverallMax' style='color: #008001;'&gt;$CPUMaximum&lt;\/div&gt;\r\n            &lt;div class='stat-desc'&gt;&lt;i class='fa-solid fa-arrows-up-to-line'&gt;&lt;\/i&gt;&amp;nbsp;&amp;nbsp;Maximum CPU % Used&lt;\/div&gt;\r\n          &lt;\/div&gt;\r\n        &lt;\/div&gt;\r\n      &lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n    &lt;p&gt;&lt;b&gt;Total Memory: $TotalMemory&lt;\/b&gt;&lt;\/p&gt;\r\n    &lt;div class='container'&gt;\r\n      &lt;div class='row' style='padding-left: 0px'&gt;\r\n        &lt;div class='col-md'&gt;\r\n          &lt;div class='stat-card' style='display: flex;'&gt;\r\n            &lt;div class='stat-value' id='ramOverallAvg' style='color: #008001;'&gt;$MEMAverage&lt;\/div&gt;\r\n            &lt;div class='stat-desc'&gt;&lt;i class='fa-solid fa-arrow-down-up-across-line'&gt;&lt;\/i&gt;&amp;nbsp;&amp;nbsp;Average RAM % Used&lt;\/div&gt;\r\n          &lt;\/div&gt;\r\n        &lt;\/div&gt;\r\n        &lt;div class='col-md'&gt;\r\n          &lt;div class='stat-card' style='display: flex;'&gt;\r\n            &lt;div class='stat-value' id='ramOverallMin' style='color: #008001;'&gt;$MEMMinimum&lt;\/div&gt;\r\n            &lt;div class='stat-desc'&gt;&lt;i class='fa-solid fa-arrows-down-to-line'&gt;&lt;\/i&gt;&amp;nbsp;&amp;nbsp;Minimum RAM % Used&lt;\/div&gt;\r\n          &lt;\/div&gt;\r\n        &lt;\/div&gt;\r\n        &lt;div class='col-md'&gt;\r\n          &lt;div class='stat-card' style='display: flex;'&gt;\r\n            &lt;div class='stat-value' id='ramOverallMax' style='color: #008001;'&gt;$MEMMaximum&lt;\/div&gt;\r\n            &lt;div class='stat-desc'&gt;&lt;i class='fa-solid fa-arrows-up-to-line'&gt;&lt;\/i&gt;&amp;nbsp;&amp;nbsp;Maximum RAM % Used&lt;\/div&gt;\r\n          &lt;\/div&gt;\r\n        &lt;\/div&gt;\r\n      &lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n    $cpuProcessMetricTable\r\n    &lt;br&gt;\r\n    $ramProcessMetricTable\r\n    &lt;br&gt;\r\n    $networkInterfaceUsage\r\n    &lt;br&gt;\r\n    $diskTable\r\n    &lt;br&gt;\r\n  &lt;\/div&gt;\r\n&lt;\/div&gt;\"\r\n  if [[ \"$_arg_daysSinceLastReboot\" -ge 0 ]]; then\r\n    echo \"Highlighting last startup date.\"\r\n  fi\r\n\r\n  # Check if the last startup exceeded the specified limit and update the HTML card accordingly\r\n  escapedLastStartupDate=\"${lastStartupDate\/\/\\\/\/\\\\\/}\"\r\n  if [[ \"$exceededLastStartupLimit\" == \"true\" ]]; then\r\n    searchString=\"id='lastStartup' class='card-text'&gt;&lt;b&gt;Last Startup Time&lt;\\\/b&gt;&lt;br&gt;$escapedLastStartupDate\"\r\n    replacementString=\"id='lastStartup' class='card-text'&gt;&lt;b&gt;Last Startup Time&lt;\/b&gt;&lt;br&gt;$lastStartupDate&amp;nbsp;&amp;nbsp;&lt;i class='fa-solid fa-circle-exclamation' style='color: #D53948;'&gt;&lt;\/i&gt;\"\r\n\r\n    htmlCard=\"${htmlCard\/\/$searchString\/$replacementString}\"\r\n  elif [[ \"$_arg_daysSinceLastReboot\" -ge 0 ]]; then\r\n    searchString=\"id='lastStartup' class='card-text'&gt;&lt;b&gt;Last Startup Time&lt;\\\/b&gt;&lt;br&gt;$escapedLastStartupDate\"\r\n    replacementString=\"id='lastStartup' class='card-text'&gt;&lt;b&gt;Last Startup Time&lt;\/b&gt;&lt;br&gt;$lastStartupDate&amp;nbsp;&amp;nbsp;&lt;i class='fa-solid fa-circle-check' style='color: #008001;'&gt;&lt;\/i&gt;\"\r\n\r\n    htmlCard=\"${htmlCard\/\/$searchString\/$replacementString}\"\r\n  fi\r\n\r\n  # Highlight overall CPU usage metrics based on thresholds\r\n  echo \"Highlighting overall CPU usage metrics.\"\r\n  CPUNumericAverage=$(echo \"$CPUAverage\" | awk '{ gsub(\/[^0-9.]\/, \"\"); printf \"%.0f\", $1 }')\r\n  CPUNumericMaximum=$(echo \"$CPUMaximum\" | awk '{ gsub(\/[^0-9.]\/, \"\"); printf \"%.0f\", $1 }')\r\n  CPUNumericMinimum=$(echo \"$CPUMinimum\" | awk '{ gsub(\/[^0-9.]\/, \"\"); printf \"%.0f\", $1 }')\r\n\r\n  # Update HTML card with warning or danger thresholds for CPU usage\r\n  if [[ \"$CPUNumericAverage\" -ge 60 &amp;&amp; \"$CPUNumericAverage\" -lt 90 ]]; then\r\n    htmlCard=${htmlCard\/\/\"id='cpuOverallAvg' style='color: #008001;'\"\/\"id='cpuOverallAvg' style='color: #FAC905;'\"}\r\n  fi\r\n  if [[ \"$CPUNumericMaximum\" -ge 60 &amp;&amp; \"$CPUNumericMaximum\" -lt 90 ]]; then\r\n    htmlCard=${htmlCard\/\/\"id='cpuOverallMax' style='color: #008001;'\"\/\"id='cpuOverallMax' style='color: #FAC905;'\"}\r\n  fi\r\n  if [[ \"$CPUNumericMinimum\" -ge 60 &amp;&amp; \"$CPUNumericMinimum\" -lt 90 ]]; then\r\n    htmlCard=${htmlCard\/\/\"id='cpuOverallMin' style='color: #008001;'\"\/\"id='cpuOverallMin' style='color: #FAC905;'\"}\r\n  fi\r\n  if [[ \"$CPUNumericAverage\" -ge 90 ]]; then\r\n    htmlCard=${htmlCard\/\/\"id='cpuOverallAvg' style='color: #008001;'\"\/\"id='cpuOverallAvg' style='color: #D53948;'\"}\r\n  fi\r\n  if [[ \"$CPUNumericMaximum\" -ge 90 ]]; then\r\n    htmlCard=${htmlCard\/\/\"id='cpuOverallMax' style='color: #008001;'\"\/\"id='cpuOverallMax' style='color: #D53948;'\"}\r\n  fi\r\n  if [[ \"$CPUNumericMinimum\" -ge 90 ]]; then\r\n    htmlCard=${htmlCard\/\/\"id='cpuOverallMin' style='color: #008001;'\"\/\"id='cpuOverallMin' style='color: #D53948;'\"}\r\n  fi\r\n\r\n  # Highlight overall RAM usage metrics based on thresholds\r\n  echo \"Highlighting overall RAM usage metrics.\"\r\n  MEMNumericAverage=$(echo \"$MEMAverage\" | awk '{ gsub(\/[^0-9.]\/, \"\"); printf \"%.0f\", $1 }')\r\n  MEMNumericMaximum=$(echo \"$MEMMaximum\" | awk '{ gsub(\/[^0-9.]\/, \"\"); printf \"%.0f\", $1 }')\r\n  MEMNumericMinimum=$(echo \"$MEMMinimum\" | awk '{ gsub(\/[^0-9.]\/, \"\"); printf \"%.0f\", $1 }')\r\n\r\n  # Update HTML card with warning or danger thresholds for RAM usage\r\n  if [[ \"$MEMNumericAverage\" -ge 60 &amp;&amp; \"$MEMNumericAverage\" -lt 90 ]]; then\r\n    htmlCard=${htmlCard\/\/\"id='ramOverallAvg' style='color: #008001;'\"\/\"id='ramOverallAvg' style='color: #FAC905;'\"}\r\n  fi\r\n  if [[ \"$MEMNumericMaximum\" -ge 60 &amp;&amp; \"$MEMNumericMaximum\" -lt 90 ]]; then\r\n    htmlCard=${htmlCard\/\/\"id='ramOverallMax' style='color: #008001;'\"\/\"id='ramOverallMax' style='color: #FAC905;'\"}\r\n  fi\r\n  if [[ \"$MEMNumericMinimum\" -ge 60 &amp;&amp; \"$MEMNumericMinimum\" -lt 90 ]]; then\r\n    htmlCard=${htmlCard\/\/\"id='ramOverallMin' style='color: #008001;'\"\/\"id='ramOverallMin' style='color: #FAC905;'\"}\r\n  fi\r\n  if [[ \"$MEMNumericAverage\" -ge 90 ]]; then\r\n    htmlCard=${htmlCard\/\/\"id='ramOverallAvg' style='color: #008001;'\"\/\"id='ramOverallAvg' style='color: #D53948;'\"}\r\n  fi\r\n  if [[ \"$MEMNumericMaximum\" -ge 90 ]]; then\r\n    htmlCard=${htmlCard\/\/\"id='ramOverallMax' style='color: #008001;'\"\/\"id='ramOverallMax' style='color: #D53948;'\"}\r\n  fi\r\n  if [[ \"$MEMNumericMinimum\" -ge 90 ]]; then\r\n    htmlCard=${htmlCard\/\/\"id='ramOverallMin' style='color: #008001;'\"\/\"id='ramOverallMin' style='color: #D53948;'\"}\r\n  fi\r\n\r\n  # Store the assembled HTML card as the WYSIWYG value\r\n  wysiwygValue=\"$htmlCard\"\r\n\r\n  # Check if event data needs to be included in the WYSIWYG field\r\n  if [[ \"$_arg_numberOfEvents\" -gt 0 ]]; then\r\n    echo \"Converting system log data into an HTML format.\"\r\n  fi\r\n\r\n  # Convert system log error events to HTML format or handle missing events\r\n  if [[ \"$_arg_numberOfEvents\" -gt 0 &amp;&amp; -n \"$errorEvents\" ]]; then\r\n    eventLogTableMetrics=$(echo \"$errorEvents\" | convertToHTMLTable -d \";[|];\")\r\n    eventLogTableMetrics=$(sed -e 's\/&lt;th&gt;\/&lt;th&gt;&lt;b&gt;\/g' \\\r\n      -e 's\/&lt;\\\/th&gt;\/&lt;\\\/b&gt;&lt;\\\/th&gt;\/g' \\\r\n      -e 's\/&lt;th&gt;&lt;b&gt;Service\/&lt;th style=\"width: 250px\"&gt;&lt;b&gt;Service\/g' \\\r\n      -e 's\/&lt;th&gt;&lt;b&gt;PID\/&lt;th style=\"width: 75px\"&gt;&lt;b&gt;PID\/g' \\\r\n      -e 's\/&lt;th&gt;&lt;b&gt;Time Created\/&lt;th style=\"width: 200px\"&gt;&lt;b&gt;Time Created\/g' \\\r\n      &lt;&lt;&lt; \"$eventLogTableMetrics\")\r\n  elif [[ \"$_arg_numberOfEvents\" -gt 0 ]]; then\r\n    eventLogTableMetrics=\"&lt;p style='margin-top: 0px'&gt;No error events were found in the system log since the last boot.&lt;\/p&gt;\"\r\n  fi\r\n\r\n  # Append event log data to the WYSIWYG value\r\n  if [[ \"$_arg_numberOfEvents\" -gt 0 ]]; then\r\n    eventLogCard=\"&lt;div class='card flex-grow-1'&gt;\r\n    &lt;div class='card-title-box'&gt;\r\n        &lt;div class='card-title'&gt;&lt;i class='fa-solid fa-book'&gt;&lt;\/i&gt;&amp;nbsp;&amp;nbsp;Recent Error Events&lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n    &lt;div class='card-body' style='white-space: nowrap'&gt;\r\n        $eventLogTableMetrics\r\n    &lt;\/div&gt;\r\n&lt;\/div&gt;\"\r\n    wysiwygValue+=\"$eventLogCard\"\r\n\r\n    # Check and handle the 40,000-character limit for the WYSIWYG field\r\n    characterCount=${#wysiwygValue}\r\n    if [[ \"$characterCount\" -gt 40000 ]]; then\r\n      echo \"The current character count is '$characterCount'.\"\r\n      echo \"[Warning] The 40,000-character limit has been reached! Trimming output to fit within the allowed limit...\"\r\n    fi\r\n\r\n    while [[ \"$characterCount\" -gt 40000 ]]; do\r\n      totalLines=$(printf \"%s\" \"$eventLogTableMetrics\" | wc -l | xargs)\r\n\r\n      exceededAmount=$((characterCount - 40000))\r\n      if [[ \"$exceededAmount\" -lt 500 ]]; then\r\n        batchSize=3\r\n      else\r\n        batchSize=$(((exceededAmount + 499) \/ 500))\r\n        [[ $batchSize -lt 3 ]] &amp;&amp; batchSize=3\r\n      fi\r\n\r\n      # Keep only the remaining lines within the limit\r\n      linesToKeep=$((totalLines - batchSize))\r\n      eventLogTableMetrics=$(echo \"$eventLogTableMetrics\" | head -n \"$linesToKeep\")\r\n      eventLogTableMetrics+=$'\\n'\"&lt;\/tbody&gt;\"\r\n      eventLogTableMetrics+=$'\\n'\"&lt;\/table&gt;\"\r\n\r\n      wysiwygValue=\"$htmlCard\"\r\n\r\n      eventLogCard=\"&lt;div class='card flex-grow-1'&gt;\r\n    &lt;div class='card-title-box'&gt;\r\n        &lt;div class='card-title'&gt;&lt;i class='fa-solid fa-book'&gt;&lt;\/i&gt;&amp;nbsp;&amp;nbsp;Recent Error Events&lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n    &lt;div class='card-body' style='white-space: nowrap'&gt;\r\n        $eventLogTableMetrics\r\n    &lt;\/div&gt;\r\n&lt;\/div&gt;\"\r\n      wysiwygValue+=\"&lt;h1&gt;This info has been truncated to accommodate the 40,000 character limit.&lt;\/h1&gt;\"\r\n      wysiwygValue+=\"$eventLogCard\"\r\n\r\n      characterCount=${#wysiwygValue}\r\n    done\r\n  fi\r\n\r\n  # Attempt to set the custom field using ninjarmm-cli\r\n  echo \"\"\r\n  echo \"Attempting to set the custom field '$_arg_wysiwygCustomField'.\"\r\n  # Try to set the multiline custom field using ninjarmm-cli and capture the output\r\n  if ! output=$(\"$ninjarmmcliPath\" set \"$_arg_wysiwygCustomField\" \"$wysiwygValue\" 2&gt;&amp;1); then\r\n    echo \"[Error] $output\" &gt;&amp;2\r\n    exitCode=1\r\n  else\r\n    echo \"Successfully set the custom field '$_arg_wysiwygCustomField'.\"\r\n  fi\r\nfi\r\n\r\n# Display the last number of events in the system log\r\nif [[ -n \"$_arg_numberOfEvents\" ]]; then\r\n  echo \"\"\r\n  echo \"### Last $_arg_numberOfEvents warnings and errors in the system log. ###\"\r\n\r\n  if [[ -n \"$errorEvents\" ]]; then\r\n    echo \"$errorEvents\" | sed '1d' | awk -F \";[|];\" '{\r\n      print \"Service: \" $1\r\n      print \"PID: \" $2\r\n      print \"Time Created: \" $3\r\n      print \"Message: \" $4\r\n      print \"\"\r\n    }'\r\n  else\r\n    echo \"No errors or warnings were found in the system log. Ensure the system log service is enabled and configured with an adequate file size limit.\"\r\n  fi\r\nfi\r\n\r\n# Attempt to remove the lock file\r\nif [[ -f \"$lockFile\" ]]; then\r\n  if ! rm \"$lockFile\"; then\r\n    echo \"[Error] Failed to remove the lock file at '$lockFile'. This file prevents multiple instances of the performance script from running simultaneously.\" &gt;&amp;2\r\n    exitCode=1\r\n  fi\r\nfi\r\n\r\nexit \"$exitCode\"\r\n<\/pre>\n<p>&nbsp;<\/p>\n\n<h2>Analisi dettagliata dello script per monitorare i dati sulle prestazioni in macOS<\/h2>\n<p>Questo script shell per monitorare i dati sulle prestazioni in macOS \u00e8 un&#8217;utility di diagnostica completa progettata per macOS Ventura e versioni successive. Esegue le seguenti operazioni di alto livello:<\/p>\n<ol>\n<li><strong>Convalida dell&#8217;ingresso e parsing degli argomenti<\/strong><strong><br \/>\n<\/strong>Accetta argomenti come &#8211;daysSinceLastReboot, &#8211;durationToPerformTests, &#8211;numberOfEvents e &#8211;wysiwygCustomFieldName. Questi controllano l&#8217;estensione e la profondit\u00e0 dell&#8217;esame diagnostico.<\/li>\n<li><strong>Controlli di integrit\u00e0 per le startup<\/strong><strong><br \/>\n<\/strong>Controlla che ci siano i permessi di root, impedisce l&#8217;esecuzione simultanea di script (usando un lockfile) e convalida il percorso dello strumento ninjarmm-cli di NinjaOne se \u00e8 necessario l&#8217;aggiornamento del campo WYSIWYG.<\/li>\n<li><strong>Fase di raccolta dati<\/strong>\n<ul>\n<li><strong>Metriche della CPU<\/strong>: Utilizza top per controllare l&#8217;utilizzo della CPU a livello di processo e a livello di sistema.<\/li>\n<li><strong>Metriche di memoria<\/strong>: Utilizza memory_pressure e top per determinare l&#8217;uso della RAM per ogni processo.<\/li>\n<li><strong>Metriche del disco<\/strong>: Raccoglie le statistiche a livello di volume con diskutil apfs list -plist.<\/li>\n<li><strong>Metriche di rete<\/strong>: Utilizza nettop e ifconfig per analizzare il traffico a livello di scheda di rete.<\/li>\n<li><strong>Log eventi<\/strong>: Estrae i messaggi di errore recenti dal sistema di logging unificato di macOS utilizzando log show.<\/li>\n<\/ul>\n<\/li>\n<li><strong>Aggregazione e formattazione dei dati<\/strong><strong><br \/>\n<\/strong>Dopo aver raccolto i log grezzi, lo script elabora i dati in tabelle strutturate. Per esempio:<\/p>\n<ul>\n<li>I 5 principali processi che consumano CPU e RAM (media\/min\/max)<\/li>\n<li>Utilizzo del disco tra i volumi<\/li>\n<li>Throughput di rete per interfaccia<\/li>\n<li>Un riepilogo delle informazioni sul sistema (modello di CPU, memoria installata ecc.)<\/li>\n<\/ul>\n<\/li>\n<li><strong>Aggiornamento opzionale dei campi WYSIWYG personalizzati<\/strong><strong><br \/>\n<\/strong>Se viene specificato un campo WYSIWYG, lo script crea una scheda HTML gi\u00e0 formattata correttamente utilizzando icone Font Awesome e indicatori di colore basati sulla soglia per evidenziare le metriche anomale.<\/li>\n<li><strong>Pulizia e output finale<\/strong><strong><br \/>\n<\/strong>Tutti i log temporanei vengono cancellati e i risultati delle prestazioni vengono stampati sul terminale. Se configurato, i risultati vengono inviati a NinjaOne.<\/li>\n<\/ol>\n<p>\ud83d\uddbc\ufe0f\u00a0<em>Aiuto visivo<\/em>: Un diagramma di flusso che mostra le fasi dello script: validazione \u2192 raccolta dei dati \u2192 formattazione \u2192 caricamento opzionale.<\/p>\n<h2>Casi d&#8217;uso potenziali<\/h2>\n<p>Immagina un MSP che gestisce un gran numero di MacBook per dirigenti d&#8217;azienda. Un utente segnala che il suo computer \u00e8 lento, ma i problemi intermittenti sono difficili da individuare in tempo reale. Con questo script per monitorare i dati sulle prestazioni in macOS:<\/p>\n<ul>\n<li>L&#8217;MSP lo gestisce in remoto tramite NinjaOne.<\/li>\n<li>Riceve un rapporto WYSIWYG che mostra un utilizzo del 99% della RAM a causa di un&#8217;applicazione che non si comporta come dovrebbe.<\/li>\n<li>Un tecnico risolve rapidamente il problema e documenta la risoluzione, il tutto senza dover condividere lo schermo o trasferire file di log.<\/li>\n<\/ul>\n<p>Questo migliora la conformit\u00e0 agli SLA, riduce l&#8217;MTTR (tempo medio di risoluzione) e migliora la soddisfazione degli utenti.<\/p>\n<h2>Confronti<\/h2>\n<p>Gli approcci tradizionali prevedono:<\/p>\n<ul>\n<li>Avvio manuale di Monitoraggio attivit\u00e0 o della Console.<\/li>\n<li>Utilizzo di applicazioni di terze parti come iStat Menus o CleanMyMac.<\/li>\n<li>Distribuzione di agenti di monitoraggio enterprise (con costi di licenza e prestazioni).<\/li>\n<\/ul>\n<p>Rispetto a questi metodi, lo script per monitorare i dati sulle prestazioni in macOS qui analizzato:<\/p>\n<ul>\n<li>Non richiede alcuna interazione con l&#8217;interfaccia grafica.<\/li>\n<li>Funziona in modo nativo su macOS.<\/li>\n<li>Si integra perfettamente con il motore di scripting e i campi personalizzati di NinjaOne.<\/li>\n<\/ul>\n<p>\u00c8 leggero, ripetibile e completamente verificabile.<\/p>\n<h2>Domande frequenti<\/h2>\n<h3>D: Questo script per monitorare i dati sulle prestazioni in macOS supporta i Mac basati su Intel?<\/h3>\n<p>R: S\u00ec, anche se le prestazioni possono variare. \u00c8 stato testato su Apple Silicon e si aspetta gli output di sysctl e top di conseguenza.<\/p>\n<h3>D: Posso raccogliere pi\u00f9 di 5 eventi di errore?<\/h3>\n<p>R: S\u00ec, modificando l&#8217;argomento &#8211;numberOfEvents. Lo script per monitorare i dati sulle prestazioni in macOS supporta fino a 10.000 eventi, ma tronca l&#8217;output HTML se supera i 40.000 caratteri.<\/p>\n<h3>D: Questo script rallenter\u00e0 la mia macchina?<\/h3>\n<p>R: Lo script per monitorare i dati sulle prestazioni in macOS utilizza metodi di verifiche a campione leggeri e viene eseguito per un periodo definito (per esempio 5 minuti), riducendo al minimo l&#8217;impatto sui sistemi di produzione.<\/p>\n<h3>D: Cosa succede se il campo WYSIWYG non \u00e8 impostato?<\/h3>\n<p>R: Lo script per monitorare i dati sulle prestazioni in macOS stampa comunque i risultati sul terminale per la revisione manuale.<\/p>\n<h2>Implicazioni<\/h2>\n<p>I problemi di prestazioni del sistema, soprattutto su endpoint macOS, spesso non vengono diagnosticati finch\u00e9 gli utenti non si lamentano. Questo script per monitorare i dati sulle prestazioni in macOS consente ai team IT di:<\/p>\n<ul>\n<li>Rilevare e agire preventivamente in caso di utilizzo elevato delle risorse.<\/li>\n<li>Documentare l&#8217;integrit\u00e0 del sistema nel tempo per eventuali revisioni forensi.<\/li>\n<li>Identificare il software che riduce le prestazioni (per esempio i servizi in background o le schede del browser che consumano troppa RAM).<\/li>\n<\/ul>\n<p>Dal punto di vista della sicurezza, segnala anche le anomalie nel traffico di rete o i log di servizi inaspettati.<\/p>\n<h2>Raccomandazioni<\/h2>\n<ul>\n<li><strong>Pianifica esecuzioni regolari<\/strong>: Distribuisci questo script per monitorare i dati sulle prestazioni in macOS con frequenza settimanale o dopo cicli di patch.<\/li>\n<li><strong>Utilizza l&#8217;automazione dei campi personalizzati<\/strong>: Utilizza lo script per monitorare i dati sulle prestazioni in macOS insieme ai flussi di lavoro di NinjaOne per interventi basati su soglie di utilizzo CPU o RAM.<\/li>\n<li><strong>Regola attentamente le soglie di utilizzo<\/strong>: Regola i livelli di avviso\/pericolo in base a quella che \u00e8 la norma dell&#8217;ambiente.<\/li>\n<li><strong>Tendenze a lungo termine<\/strong>: Esporta i risultati in una dashboard centrale per l&#8217;analisi delle tendenze.<\/li>\n<\/ul>\n<h2>Considerazioni finali<\/h2>\n<p>Per i team IT che gestiscono dispositivi macOS su vasta scala, la visibilit\u00e0 \u00e8 fondamentale. Questo script shell per monitorare i dati sulle prestazioni in macOS colma il divario tra la diagnostica del sistema e le informazioni utili, fornendo istantanee delle prestazioni direttamente nei campi personalizzati di NinjaOne. Questo esemplifica come l&#8217;automazione e l&#8217;integrazione possano snellire i flussi di lavoro IT, migliorare l&#8217;esperienza degli utenti finali e mantenere l&#8217;integrit\u00e0 del sistema.<\/p>\n<p>Se utilizzi <a href=\"https:\/\/www.ninjaone.com\/it\/\">NinjaOne<\/a> e non hai ancora sperimentato la potenza degli script shell sugli endpoint macOS, questo script pu\u00f2 essere un buon punto di partenza.<\/p>\n","protected":false},"author":35,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_acf_changed":false,"_relevanssi_hide_post":"","_relevanssi_hide_content":"","_relevanssi_pin_for_all":"","_relevanssi_pin_keywords":"","_relevanssi_unpin_keywords":"","_relevanssi_related_keywords":"","_relevanssi_related_include_ids":"","_relevanssi_related_exclude_ids":"","_relevanssi_related_no_append":"","_relevanssi_related_not_related":"","_relevanssi_related_posts":"","_relevanssi_noindex_reason":"","_lmt_disableupdate":"no","_lmt_disable":""},"operating_system":[4210],"use_cases":[4269],"class_list":["post-506844","script_hub","type-script_hub","status-publish","hentry","script_hub_category-macos"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.ninjaone.com\/it\/wp-json\/wp\/v2\/script_hub\/506844","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.ninjaone.com\/it\/wp-json\/wp\/v2\/script_hub"}],"about":[{"href":"https:\/\/www.ninjaone.com\/it\/wp-json\/wp\/v2\/types\/script_hub"}],"author":[{"embeddable":true,"href":"https:\/\/www.ninjaone.com\/it\/wp-json\/wp\/v2\/users\/35"}],"replies":[{"embeddable":true,"href":"https:\/\/www.ninjaone.com\/it\/wp-json\/wp\/v2\/comments?post=506844"}],"wp:attachment":[{"href":"https:\/\/www.ninjaone.com\/it\/wp-json\/wp\/v2\/media?parent=506844"}],"wp:term":[{"taxonomy":"script_hub_category","embeddable":true,"href":"https:\/\/www.ninjaone.com\/it\/wp-json\/wp\/v2\/operating_system?post=506844"},{"taxonomy":"use_cases","embeddable":true,"href":"https:\/\/www.ninjaone.com\/it\/wp-json\/wp\/v2\/use_cases?post=506844"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}