bash脚本:使用命令输出动态创建菜单和数组?

时间:2016-11-12 02:27:00

标签: bash shell

我试图创建一个脚本来运行命令并获取该输出并使用它来动态创建菜单。我还需要访问每个输出行的部分以获取特定值。

我正在使用命令:

lsblk --nodeps -no name,serial,size | grep "sd"

输出:

sda   600XXXXXXXXXXXXXXXXXXXXXXXXXX872 512G
sdb   600XXXXXXXXXXXXXXXXXXXXXXXXXXf34 127G

我需要创建一个看起来像的菜单:

Available Drives:
1) sda   600XXXXXXXXXXXXXXXXXXXXXXXXXX872 512G
2) sdb   600XXXXXXXXXXXXXXXXXXXXXXXXXXf34 127G
Please select a drive: 

(注意:可以有任意数量的驱动器,此菜单将从可用的驱动器阵列动态构建)

当用户选择菜单编号时,我需要能够访问所选驱动器的驱动器ID(sdb)和驱动器序列号(600XXXXXXXXXXXXXXXXXXXXXXXXXXf34)。

非常感谢任何帮助。 如果需要澄清,请告诉我。

3 个答案:

答案 0 :(得分:3)

#!/usr/bin/env bash

# Read command output line by line into array ${lines [@]}
# Bash 3.x: use the following instead:
#   IFS=$'\n' read -d '' -ra lines < <(lsblk --nodeps -no name,serial,size | grep "sd")
readarray -t lines < <(lsblk --nodeps -no name,serial,size | grep "sd")

# Prompt the user to select one of the lines.
echo "Please select a drive:"
select choice in "${lines[@]}"; do
  [[ -n $choice ]] || { echo "Invalid choice. Please try again." >&2; continue; }
  break # valid choice was made; exit prompt.
done

# Split the chosen line into ID and serial number.
read -r id sn unused <<<"$choice"

echo "id: [$id]; s/n: [$sn]"

关于你的尝试:在数组构造函数($(...))中使用不带引号的命令替换(( ... ))使得命令输出中的标记受分词的影响和 globbing ,这意味着,默认情况下,每个以空格分隔的标记成为自己的数组元素,并且可以扩展为匹配的文件名。

以这种方式填充数组很脆弱,即使您可以通过设置IFS并关闭通配(set -f)来解决这个问题,更好的方法是使用readarray -t(Bash v4 +)或IFS=$'\n' read -d '' -ra(Bash v3.x),使用进程替换来填充数组,并使用命令输出(未修改的)行。

答案 1 :(得分:0)

我设法以优雅的方式解决了这个问题:

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

答案 2 :(得分:-1)

如下所示

#!/bin/bash

# define an array
declare -a obj

# capture the current IFS
cIFS=$IFS

# change IFS to something else
IFS=:

# assign & format output from lsblk 
obj=( $(lsblk --nodeps --no name,serial,size) )

# generate a menu system
select item from ${obj[@]}; do
  if  [ -n ${item} ]; then
    echo "Invalid selection"
    continue
  else
    selection=${item}
    break
  fi
done

# reset the IFS
IFS=${cIFS}

这应该更具可移植性和更少的依赖性,例如readarray,这在某些系统上是不可用的