需要替代读取阵列/Map文件以在旧版本的Bash上执行脚本

oo7oh9g9  于 2022-10-17  发布在  Unix
关注(0)|答案(3)|浏览(165)

脚本是:


# !/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]}";

较旧的系统没有mapfilereadarray,所以我需要将该行转换为可以将lsblk输出的每一行读取到数组中的替代行。
创建该数组的行是:

mapfile -t drives < <(lsblk --nodeps -o name,serial,size | grep "sd");
lnlaulya

lnlaulya1#

您可以循环您的输入并将其追加到数组中:

$ 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")'

或者,对于您的特定情况:

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

参见BashFAQ/001可以很好地解释为什么IFS= read -r是个好主意:它确保保留空格并且不解释反斜杠序列。

h22fl7wq

h22fl7wq2#

这是我前段时间想出的解决方案。这更好,因为它为不支持Map文件/读数组的旧版本的Bash提供了一个替代函数。

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

您不需要对代码进行任何更改。它真的很管用!

readarray -t services -u < <(lsblk --nodeps -o name,serial,size | grep "sd")
wgeznvg7

wgeznvg73#

对于那些在家玩的人来说,这个版本的目标是提供一个mapfile,它符合Bash 5的功能,但仍然可以追溯到Bash 3.x:


# !/usr/bin/env bash

if ! (enable | grep -q 'enable mapfile'); then
  function mapfile() {
    local    DELIM="${DELIM-$'\n'}";     opt_d() {    DELIM="$1"; }
    local    COUNT="${COUNT-"0"}";       opt_n() {    COUNT="$1"; }
    local   ORIGIN="${ORIGIN-"0"}";      opt_O() {   ORIGIN="$1"; }
    local     SKIP="${SKIP-"0"}";        opt_s() {     SKIP="$1"; }
    local    STRIP="${STRIP-"0"}";       opt_t() {    STRIP=1;    }
    local  FROM_FD="${FROM_FD-"0"}";     opt_u() {  FROM_FD="$1"; }
    local CALLBACK="${CALLBACK-}";       opt_C() { CALLBACK="$1"; }
    local  QUANTUM="${QUANTUM-"5000"}";  opt_c() {  QUANTUM="$1"; }

    unset OPTIND; local extra_args=()
    while getopts ":d:n:O:s:tu:C:c:" opt; do
      case "$opt" in
        :)  echo "${FUNCNAME[0]}: option '-$OPTARG' requires an argument" >&2; exit 1 ;;
       \?)  echo "${FUNCNAME[0]}: ignoring unknown argument '-$OPTARG'" >&2 ;;
        ?)  "opt_${opt}" "$OPTARG" ;;
      esac
    done

    shift "$((OPTIND - 1))"; set -- ${extra_args[@]+"${extra_args[@]}"} "$@"

    local var="${1:-MAPFILE}"

    ### Bash 3.x doesn't have `declare -g` for "global" scope...
    eval "$(printf "%q" "$var")=()" 2>/dev/null || { echo "${FUNCNAME[0]}: '$var': not a valid identifier" >&2; exit 1; }

    local __skip="${SKIP:-0}" __counter="${ORIGIN:-0}"  __count="${COUNT:-0}"  __read="0"

    ### `while read; do...` has trouble when there's no final newline,
    ### and we use `$REPLY` rather than providing a variable to preserve
    ### leading/trailing whitespace...
    while true; do
      if read -d "$DELIM" -r <&"$FROM_FD"
         then [[ ${STRIP:-0} -ge 1 ]] || REPLY="$REPLY$DELIM"
         elif [[ -z $REPLY ]]; then break
      fi

      (( __skip-- <= 0 )) || continue
      ((  COUNT <= 0 || __count-- > 0 )) || break

      ### Yes, eval'ing untrusted content is insecure, but `mapfile` allows it...
      if [[ -n $CALLBACK ]] && (( QUANTUM > 0 && ++__read % QUANTUM == 0 ))
         then eval "$CALLBACK $__counter $(printf "%q" "$REPLY")"; fi

      ### Bash 3.x doesn't allow `printf -v foo[0]`...
      ### and `read -r foo[0]` mucks with whitespace
      eval "${var}[$((__counter++))]=$(printf "%q" "$REPLY")"
    done
  }

  ### Alias `readarray` as well...
  readarray() { mapfile "$@"; }
fi

if [[ -z ${PS1+YES} ]]; then
   echo "'mapfile' should only be called as a shell function; try \"source ${BASH_SOURCE[0]##*/}\" first..." >&2
   exit 1
fi

相关问题