6

The script is:

#!/bin/bash

# Dynamic Menu Function
createmenu () {
    select selected_option; do # in "$@" is the default
        if [ 1 -le "$REPLY" ] && [ "$REPLY" -le $(($#)) ]; then
            break;
        else
            echo "Please make a vaild selection (1-$#)."
        fi
    done
}

declare -a drives=();
# Load Menu by Line of Returned Command
mapfile -t drives < <(lsblk --nodeps -o name,serial,size | grep "sd");
# Display Menu and Prompt for Input
echo "Available Drives (Please select one):";
createmenu "${drives[@]}"
# Split Selected Option into Array and Display
drive=($(echo "${selected_option}"));
echo "Drive Id: ${drive[0]}";
echo "Serial Number: ${drive[1]}";

The older system doesn't have mapfile or readarray so I need to convert that line to some alternative that can read each line of the lsblk output into an array.

The line in question that creates the array is:

mapfile -t drives < <(lsblk --nodeps -o name,serial,size | grep "sd");
codeforester
  • 34,080
  • 14
  • 96
  • 122
Joshua Jarman
  • 387
  • 5
  • 10
  • Is this possible to use **older command scripts** such as the answer accepted below on **latest version of Bash**? – Bando Sep 13 '21 at 13:33

2 Answers2

13

You can loop over your input and append to the array:

$ while IFS= read -r line; do arr+=("$line"); done < <(printf '%d\n' {0..5})
$ declare -p arr
declare -a arr='([0]="0" [1]="1" [2]="2" [3]="3" [4]="4" [5]="5")'

Or, for your specific case:

while IFS= read -r line; do
    drives+=("$line")
done < <(lsblk --nodeps -o name,serial,size | grep "sd")

See the BashFAQ/001 for an excellent explanation why IFS= read -r is a good idea: it makes sure that whitespace is conserved and backslash sequences not interpreted.

Benjamin W.
  • 38,596
  • 16
  • 96
  • 104
2

Here's the solution I came up with a while back. This is better because it provides a substitute function for older versions of Bash that don't support mapfile/readarray.

if ! type -t readarray >/dev/null; then
  # Very minimal readarray implementation using read. Does NOT work with lines that contain double-quotes due to eval()
  readarray() {
    local cmd opt t v=MAPFILE
    while [ -n "$1" ]; do
      case "$1" in
      -h|--help) echo "minimal substitute readarray for older bash"; exit; ;;
      -r) shift; opt="$opt -r"; ;;
      -t) shift; t=1; ;;
      -u) 
          shift; 
          if [ -n "$1" ]; then
            opt="$opt -u $1"; 
            shift
          fi
          ;;
      *)
          if [[ "$1" =~ ^[A-Za-z_]+$ ]]; then
            v="$1"
            shift
          else
            echo -en "${C_BOLD}${C_RED}Error: ${C_RESET}Unknown option: '$1'\n" 1>&2
            exit
          fi
          ;;
      esac
    done
    cmd="read $opt"
    eval "$v=()"
    while IFS= eval "$cmd line"; do      
      line=$(echo "$line" | sed -e "s#\([\"\`]\)#\\\\\1#g" )
      eval "${v}+=(\"$line\")"
    done
  }
fi

You don't have to change your code one bit. It just works!

readarray -t services -u < <(lsblk --nodeps -o name,serial,size | grep "sd")
parleer
  • 1,008
  • 2
  • 11
  • 20
  • 1
    Hey do you have this up on a github gist somewheres? If so, point me at it and consider it followed ! I haven't tested it yet, but if I get a chance to, I'll vote accordingly -- Looks good in general tho! – David Farrell Jan 18 '21 at 00:57