2016-11-12 105 views
1

我想創建一個腳本來運行一個命令,並採取該輸出,並使用它來動態地創建一個菜單。我還需要訪問每個輸出行的部分特定值。bash腳本:使用命令輸出動態創建菜單和數組?

我使用命令:

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)。

任何援助將不勝感激。 如果需要澄清,請讓我知道。

回答

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

至於你嘗試過什麼:使用數組構造內的未加引號的命令替換($(...))((...)),使得命令的輸出受到分詞令牌通配符,這意味着默認情況下,每個以空格分隔的標記將成爲它自己的數組元素,並可能擴展爲匹配的文件名。

以這種方式填寫陣列是脆弱的,即使你可以通過設置IFS和關閉通配符(set -f),更好的辦法是使用readarray -t(猛砸V4 +)或IFS=$'\n' read -d '' -ra(猛砸3.x版)與修復一個進程替換,用命令輸出的(未修改的)行填充數組。

+1

哦,真棒,這是非常接近我設法提出的解決方案,甚至更好。非常感謝! –

+1

另外,感謝您提供其他詳細信息,信息和其他解決方案的評論,他們相當有幫助。 –

-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這並不適用於某些系統

+0

如果有人可以更改我的格式以便代碼塊很酷 –

+0

陣列不是完全可移植的(POSIX shell規範不支持),所以您最好使用Bash所提供的所有內容。在使用'($(...))'前設置'IFS'是一種改進,但通常情況下仍然是一種慣例。至於'readarray':在舊的Bash版本中,使用'IFS = $'\ n'讀取-d''-ra'。 – mklement0

+0

更具體地說:'readarray'和'read'都是Bash _builtins_,所以它們的可用性不會受到_platform_的影響,只有_Bash version_:'readarray'(及其同義詞'mapfile')在Bash 4.0中引入。 – mklement0

0

我設法解開在這個問題優雅的方式:

#!/bin/bash 

# Dynamic Menu Function 
createmenu() { 
    select selected_option; do # in "[email protected]" 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]}";