Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Need alternative to readarray/mapfile for script on older version of Bash

Tags:

bash

shell

unix

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");
like image 311
Joshua Jarman Avatar asked Jan 05 '17 00:01

Joshua Jarman


People also ask

What is the difference between the Readarray and the Mapfile commands?

mapfile also called (read array) is a command of the Bash shell used to read arrays. It reads lines from the standard input into an array variable. Also, mapfile must read from substitution (< <) and not from a pipe. Also, mapfile is faster and more convenient when compared to a read loop.

How do I read an array file in bash?

The read command has the -a option that allows us to populate the given array with a list of whitespace-delimited values. (The IFS value determines the delimiter, which is whitespace by default.) The array is populated starting at index 0. The last value provided becomes the last element in the array.

What is Linux Readarray command?

The readarray reads lines from the standard input into an array variable: my_array. The -t option will remove the trailing newlines from each line. We used the < <(COMMAND) trick to redirect the COMMAND output to the standard input.


2 Answers

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.

like image 144
Benjamin W. Avatar answered Oct 24 '22 11:10

Benjamin W.


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")
like image 43
parleer Avatar answered Oct 24 '22 11:10

parleer