【问题标题】:Need alternative to readarray/mapfile for script on older version of Bash需要替代 readarray/mapfile 用于旧版本 Bash 上的脚本
【发布时间】:2017-05-19 10:03:59
【问题描述】:

脚本是:

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

【问题讨论】:

  • 这是否可以使用较旧的命令脚本,例如下面在最新版本的 Bash 上接受的答案?

标签: bash shell unix


【解决方案1】:

这是我不久前提出的解决方案。这更好,因为它为不支持 mapfile/readarray 的旧版本 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")

【讨论】:

  • 嘿,你在某个地方的 github 要点上有这个吗?如果是这样,请指出它并认为它遵循!我还没有测试过,但如果有机会,我会相应地投票——总体上看起来不错!
【解决方案2】:

您可以循环输入并附加到数组中:

$ 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 是一个好主意的绝佳解释:它确保保留空格并且不解释反斜杠序列。

【讨论】:

    猜你喜欢
    • 2021-12-08
    • 1970-01-01
    • 1970-01-01
    • 2016-04-20
    • 1970-01-01
    • 1970-01-01
    • 2020-09-13
    • 2020-08-31
    • 2018-11-12
    相关资源
    最近更新 更多