Linuxサーバーに複数のパッケージをインストールして構成する自動化されたbashスクリプトを作成しています。
複数のサーバーでこのスクリプトを再利用したいが、必要に応じていくつかのパラメータを変更します。これにより、後で検索と置換を使用して複数の変更を適用して多くのコードを実行する必要がなくなります。スクリプトの上部/開始部分に対応する値を定義できるため、スクリプト全体で変数を使用します。
一部の変数はパスワードなので、プレーンテキストファイルにパスワードを保存しないようにしたいと思います。私の回避策は、変数の値を読み取ることができる暗号化された文書に保存し、スクリプトの先頭に変数を定義することでした。一連の質問を実行し、変数値を設定するための入力を要求するだけです。次のコマンドを使用してこれを行う方法を見つけました。
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
上記のコマンドが機能しているかどうかわかりません。 where is line 14を実行しても、continue
ループは##TOP OF SCRIPT##セクションに戻り、配列内のすべての質問を再度尋ねます。 2番目に質問をしたら、変数が設定されているかどうかを確認する必要があります。これはまだ設定されていない変数についてもう一度尋ねる唯一の質問です。
私ができる唯一の方法は、変数テスト部分を最初に置くことです。これは、変数の設定について最初に尋ねられたときにまだ変数が設定されていないというメッセージを受け取ることになります。
私が助けを求める理由は、私はループをほとんど扱っていないからです。私の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に投稿し、私の答えを最終版に更新して、他の人が利用できるようにしたいと思います。これは確かに他の人に役立つだろうからです。
答え1
いいですね。まず、いくつかの明確な質問をします。${script_variables[@]}
スペースで区切られた配列全体に展開されます。したがって、スクリプトの先頭で配列を定義すると、常にエラーが表示されるため、空ではありませ $script_variables
ん。配列の要素を参照するには、最初の要素、2番目の要素など、特定の数値インデックスを使用する必要があります。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
したがって、これを使用することも、2つの配列を使用することもできます。 1 つは変数名を保存し、もう 1 つはその値を保存します。あるいは、最新バージョンの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!"