通過使用者輸入設置變數並在繼續腳本之前檢查所有變數是否已設置
我正在建構一個自動 bash 腳本,它將在 Linux 伺服器上安裝和配置多個包。
我希望在多台伺服器上重複使用這個腳本,但根據需要更改一些參數。因此,我以後不必通過大量程式碼使用查找和替換進行多項更改,我在整個腳本中使用了變數,因為它們可以簡單地在腳本的頂部/開頭定義它們的值。
由於一些變數是密碼,我想避免將密碼儲存在純文字文件中。我的解決方法是將變數值儲存在加密文件中,我可以從中讀取,然後在腳本開頭定義變數。這將通過執行一系列問題並詢問我的輸入來設置變數的值來完成。我已經制定瞭如何使用以下命令執行此操作:
read -p "`echo -e 'Enter admin user password: \n\b'`" admin_user_password echo "You entered '$admin_user_password"
在編寫 bash 腳本方面變得棘手的領域是,我想確保所有變數都已設置(不留空)並確保在腳本可以繼續並自動執行自己的操作之前正確輸入它們。在開始時設置變數是腳本中唯一需要使用者互動的部分。為了讓它工作,我相當肯定我會研究循環。
這是我希望在腳本的自動化部分開始之前執行循環的順序。
- 該腳本通過詢問使用者輸入來詢問變數
- 將根據所有變數的列表檢查使用者設置的變數。任何未在步驟 1 中輸入/設置的變數將貫穿剩餘的問題,警告使用者“您尚未為 $some-variable 設置值”。
- 一旦設置了所有變數,它將列印出所有設置的變數及其值,並通過簡單的是/否詢問它們是否正確。如果它們不正確,我想再次返回第 1 步以重新解決問題。但是,如果變數和值正確,則腳本將繼續進入自動化部分。
這就是我到目前為止所擁有的。
## TOP OF SCRIPT ## script_variables=( admin_user_name admin_user_password ) for i in "${script_variables[@]}"; do read -p "`echo -e 'Enter value for: \n\b'`" ${script_variables[@]} echo "You entered '${script_variables[@]}'" if test -z "${script_variables[@]}" then echo "${script_variables[@]} has not been set" # loopback to the top of the script continue else while true; do read -p "Are the variables ccorrect?" yn case $yn in [Yy]* ) echo "All variables have been set. The script will now continue."; sleep 2; break;; [Nn]* ) continue;; * ) echo "Please answer yes or no.";; esac fi done # Automated script commands below
我不確定上述命令是否有效。即使它
continue
在第 14 行執行 where,循環將返回到 ## TOP OF SCRIPT ## 部分,它也會再次詢問我數組中的所有問題。在第二遍問我問題時,它需要檢查變數是否已設置。對於那些尚未設置的變數,這應該是我被重新提出的唯一問題。我可能做到的唯一方法是將變數測試部分放在開頭。這只是意味著在第一次被問到設置變數的問題時,我會收到一條消息,說沒有設置任何變數。
我尋求幫助的原因是我很少處理循環。我的 Unix 知識主要是通過在網際網路上查看類似這樣的論壇或在虛擬環境中嘗試 Linux PC 上的命令來自學的,我還沒有冒險深入循環。每當我使用循環時,它們都是我從網際網路上獲取並在我自己的腳本中使用的片段。顯然,在上面的腳本中,我正在查看嵌套循環,這進一步增加了複雜性。
更新 1
關於您出色的答案,我想調整顯示的文本,以便在輸入變數時將其輸入到下面的新行中。
例如,我將如何在
read -p ("Enter value for $varName \n\b " script_variables[$varName]
我想顯示這樣的東西:
Enter value for admin_user_name >
我看到網際網路上的指南談論使用
echo -e
,以便\n\b
可以使用。我確實玩過,但變數沒有擴大。https://stackoverflow.com/questions/8467424/echo-newline-in-bash-prints-literal-n
更新 2
我正在考慮擴展您的腳本並實現一種可選的方式來讀取和儲存臨時文件中的變數。我將能夠通過設置適當的 Unix 權限來保護其他系統使用者的安全,然後我將在腳本的最後刪除該文件,這樣就沒有它的痕跡了。
在腳本的開頭,我想檢查是否存在包含變數的特定文本文件。如果文件確實存在,則檢查是否所有變數都已設置並與變數名數組進行比較。如果文件中儲存的變數不完整,請執行循環直到設置它們。顯然,只有當使用者在被問及“你確定這是正確的嗎?”時回答“是”時,才會設置儲存在文件中的變數。
請注意,一旦我讓這個腳本工作,我希望將它放在 GitHub 上並在此處使用最終版本更新我的答案,以便其他人可以使用它,因為它必然會幫助其他人。
好的,首先是幾個明顯的問題。擴展到由空格分隔
${script_variables[@]}
的整個數組。$script_variables
因此test -z "${script_variables[@]}"
,當您在腳本開頭定義數組時,您將始終是錯誤的,因此"${script_variables[@]}"
永遠不會為空。當你想引用數組的一個元素時,你需要使用一個特定的數字索引:"${script_variables[0]}"
對於第一個元素,"${script_variables[1]}"
對於第二個等。其次,當你
read
將一個值存入一個變數時,你需要將它儲存在一個變數中。但是,您提供read
的擴展數組只是儲存在數組中的值:$ echo "${script_variables[@]}" admin_user_name admin_user_password
更重要的是,您似乎希望將使用者提供的值儲存在一個變數中,然後您可以按名稱呼叫該變數。這個,你設置
var="foo"
然後擁有variableName="var"
並試圖獲取命名變數var
的值(因此,在這種情況下為“foo”)被稱為“間接擴展”。其語法是${!variableName}
. 例如:$ var="foo" $ variableName="var" $ echo "${!variableName}" foo
因此,您可以使用它,也可以使用兩個數組:一個用於儲存變數名,另一個用於儲存它們的值。或者,使用現代版本的 bash 更好,使用單個關聯數組,其鍵將是變數名。這是使用間接擴展的腳本的工作版本:
#/bin/bash script_variables=( admin_user_name admin_user_password ) ## This will be used to exit the loop allSet=""; while [[ -z $allSet ]]; do for varName in "${script_variables[@]}"; do ## No need to loop the whole thing, just loop ## until this particular variable has been set while [[ -z ${!varName} ]]; do read -p "Enter value for $varName: " $varName done done ## We will only exit the loop once all vars have been set. ## Now print and check them. printf '\n=========\nYou have entered:\n' for varName in "${script_variables[@]}"; do printf '%s=%s\n' "$varName" "${!varName}" done while true; do read -p "Are the variables correct? " yn case $yn in [Yy]* ) echo "All variables have been set. The script will now continue."; ## Setting this to 1 exits the top "while [[ -z $allSet ]]; do" loop allSet=1 break;; [Nn]* ) ## Clear the stored values to start again for varName in "${script_variables[@]}"; do unset $varName done break;; * ) echo "Please answer yes or no.";; esac done done
這是一個使用關聯數組的版本:
#/bin/bash declare -A script_variables=( [admin_user_name]="" [admin_user_password]="" ) ## This will be used to exit the loop allSet=""; while [[ -z $allSet ]]; do ## '${!array[@]}' returns all keys of an associative array for varName in "${!script_variables[@]}"; do read -p "Enter value for $varName: " script_variables[$varName] done ## We will only exit the loop once all vars have been set. ## Now print and check them. printf '\n=========\nYou have entered:\n' for varName in "${!script_variables[@]}"; do printf '%s=%s\n' "$varName" "${script_variables[$varName]}" done while true; do read -p "Are the variables correct? " yn case $yn in [Yy]* ) echo "All variables have been set. The script will now continue."; ## Setting this to 1 exits the top "while [[ -z $allSet ]]; do" loop allSet=1 break;; [Nn]* ) ## Clear the stored values to start again for varName in "${!script_variables[@]}"; do script_variables[$varName]="" done break;; * ) echo "Please answer yes or no.";; esac done done
現在,就個人而言,我會使用稍微不同的方法。我不會設置所有內容,然後讓使用者最後檢查,而是會在輸入時檢查所有內容。這樣,您一開始就會遇到錯誤,並且不需要重新設置所有變數,而只需要重新設置您出錯的那個。像這樣的東西:
#/bin/bash declare -A script_variables=( [admin_user_name]="" [admin_user_password]="" ) allSet=0; while [[ $allSet -lt ${#script_variables[@]} ]]; do for varName in "${!script_variables[@]}"; do ok="" while [[ $ok != [Yy]* ]]; do read -p "Enter value for $varName: " script_variables[$varName] read -p "You entered '${script_variables[$varName]}'. Is this correct? " ok done ## If we're satisfied, increment the value of allSet ((allSet++)) done done ## You can add a second test here, but just exit the script if it's wrong. ## There's no point in complicating your code if you're just going back to ## the beginning anyway: just exit and rerun it. printf '\n=========\nYou have entered:\n' for varName in "${!script_variables[@]}"; do printf '%s=%s\n' "$varName" "${script_variables[$varName]}" done read -p "Is everything OK? (y/n; n will exit the script)": yn if [[ $yn != [Yy]* ]]; then exit fi echo "Everything correctly set, continuing!"