Created
November 1, 2025 23:06
-
-
Save job-gordon/8d708d195adc2f7ee7f3688db20b44a1 to your computer and use it in GitHub Desktop.
vm format convert
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # --- Bash Version Check --- | |
| if [[ -z "${BASH_VERSINFO[0]}" || "${BASH_VERSINFO[0]}" -lt 4 ]]; then | |
| echo "Error: This script requires Bash version 4.0 or higher." >&2 | |
| echo "You are running: $BASH_VERSION" >&2 | |
| exit 1 | |
| fi | |
| # --- Default Variables --- | |
| input_file="" | |
| output_type="" | |
| output_file="" | |
| # --- Dynamic Data Arrays --- | |
| # 1. Associative array to store the key-value data | |
| declare -A vm_data | |
| # 2. Standard array to store the *order* of the keys as they appear | |
| declare -a headers_ordered | |
| # --- Functions --- | |
| # Function to show usage | |
| usage() { | |
| echo "Usage: $0 -t <csv|markdown|html> [-i inputfile] [-o outputfile]" | |
| echo " -t Output type (csv, markdown, or html)" | |
| echo " -i Optional input file (default: read from standard input)" | |
| echo " -o Optional output file (default: print to console)" | |
| exit 1 | |
| } | |
| # Function to parse input (from file or stdin) | |
| parse_input() { | |
| while IFS= read -r line; do | |
| # Skip empty lines or lines without a colon | |
| if [[ -z "$line" || "$line" != *":"* ]]; then | |
| continue | |
| fi | |
| # Extract key: everything before the first colon | |
| key="${line%%:*}" | |
| # Extract value: everything after the first colon | |
| value="${line#*:}" | |
| # --- Strip leading/trailing whitespace (Bash 4+ method) --- | |
| key="${key#"${key%%[![:space:]]*}"}" | |
| key="${key%"${key##*[![:space:]]}"}" | |
| value="${value#"${value%%[![:space:]]*}"}" | |
| value="${value%"${value##*[![:space:]]}"}" | |
| if [ -n "$key" ]; then | |
| # --- New Dynamic Header Logic --- | |
| # Check if this key has been set before. | |
| # We use ${vm_data[$key]+_} which is a portable way to check if a key is set, | |
| # even if its value is empty. | |
| if [[ -z ${vm_data[$key]+_} ]]; then | |
| # This is a new key, add it to our ordered list | |
| headers_ordered+=("$key") | |
| fi | |
| # Store or overwrite the value | |
| vm_data["$key"]="$value" | |
| fi | |
| done | |
| } | |
| # Function to generate CSV output | |
| generate_csv() { | |
| # 1. Print Header (from our dynamic array) | |
| (IFS=,; echo "${headers_ordered[*]}") | |
| # 2. Build data row | |
| local data_row=() | |
| for header in "${headers_ordered[@]}"; do | |
| data_row+=("${vm_data[$header]}") | |
| done | |
| # 3. Print data row | |
| (IFS=,; echo "${data_row[*]}") | |
| } | |
| # Function to generate Markdown table output | |
| generate_markdown() { | |
| # 1. Print Header (from our dynamic array) | |
| printf "|" | |
| for header in "${headers_ordered[@]}"; do | |
| printf " %s |" "$header" | |
| done | |
| printf "\n" | |
| # 2. Print Separator | |
| printf "|" | |
| for header in "${headers_ordered[@]}"; do | |
| printf " %s |" "$(seq -s'-' 1 ${#header} | tr -d '0-9')" | |
| done | |
| printf "\n" | |
| # 3. Print Data | |
| printf "|" | |
| for header in "${headers_ordered[@]}"; do | |
| local value="${vm_data[$header]}" | |
| # Backslash-escape colons to prevent Markdown emoji rendering | |
| local escaped_value="${value//:/\\:}" | |
| printf " %s |" "$escaped_value" | |
| done | |
| printf "\n" | |
| } | |
| # Function to generate HTML table output | |
| generate_html() { | |
| echo "<table>" | |
| # 1. Header Row (from our dynamic array) | |
| echo " <thead>" | |
| echo " <tr>" | |
| for header in "${headers_ordered[@]}"; do | |
| echo " <th>$header</th>" | |
| done | |
| echo " </tr>" | |
| echo " </thead>" | |
| # 2. Body Row | |
| echo " <tbody>" | |
| echo " <tr>" | |
| for header in "${headers_ordered[@]}"; do | |
| local value="${vm_data[$header]}" | |
| if [ -z "$value" ]; then | |
| echo " <td> </td>" | |
| else | |
| echo " <td>$value</td>" | |
| fi | |
| done | |
| echo " </tr>" | |
| echo " </tbody>" | |
| echo "</table>" | |
| } | |
| # --- Argument Parsing --- | |
| while getopts "i:t:o:" opt; do | |
| case $opt in | |
| i) input_file="$OPTARG" ;; | |
| t) output_type="$OPTARG" ;; | |
| o) output_file="$OPTARG" ;; | |
| \?) usage ;; | |
| esac | |
| done | |
| # --- Validation --- | |
| if [ -n "$input_file" ] && [ ! -r "$input_file" ]; then | |
| echo "Error: Input file (-i) '$input_file' is not readable." >&2 | |
| usage | |
| fi | |
| if [[ "$output_type" != "csv" && "$output_type" != "markdown" && "$output_type" != "html" ]]; then | |
| echo "Error: Invalid output type (-t). Must be 'csv', 'markdown', or 'html'." >&2 | |
| usage | |
| fi | |
| # --- Main Logic --- | |
| # 1. Read and parse the input. This will populate | |
| # both 'vm_data' and 'headers_ordered' arrays. | |
| if [ -n "$input_file" ]; then | |
| parse_input < "$input_file" | |
| else | |
| parse_input | |
| fi | |
| # 2. Set up output redirection | |
| if [ -n "$output_file" ]; then | |
| exec > "$output_file" | |
| fi | |
| # 3. Call the correct generation function | |
| case "$output_type" in | |
| csv) | |
| generate_csv | |
| ;; | |
| markdown) | |
| generate_markdown | |
| ;; | |
| html) | |
| generate_html | |
| ;; | |
| esac |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment