私は自分のスクリプトの設定ファイルを作成する必要があります:ここに例があります:
スクリプト:
#!/bin/bash
source /home/myuser/test/config
echo "Name=$nam" >&2
echo "Surname=$sur" >&2
の内容/home/myuser/test/config
:
nam="Mark"
sur="Brown"
うまくいきました!
私の質問:これはこれを行う正しい方法ですか、または他の方法がありますか?
私は自分のスクリプトの設定ファイルを作成する必要があります:ここに例があります:
スクリプト:
#!/bin/bash
source /home/myuser/test/config
echo "Name=$nam" >&2
echo "Surname=$sur" >&2
の内容/home/myuser/test/config
:
nam="Mark"
sur="Brown"
うまくいきました!
私の質問:これはこれを行う正しい方法ですか、または他の方法がありますか?
回答:
source
任意のコードを実行するため、安全ではありません。これは懸念事項ではないかもしれませんが、ファイルのアクセス許可が正しくない場合、ファイルシステムアクセス権を持つ攻撃者が、セキュリティで保護されたスクリプトなどによって読み込まれた構成ファイルにコードを挿入することにより、特権ユーザーとしてコードを実行する可能性がありますinitスクリプト。
これまでのところ、私が特定できた最良の解決策は、不器用な車輪を再発明する解決策です。
myscript.conf
password=bar
echo rm -rf /
PROMPT_COMMAND='echo "Sending your last command $(history 1) to my email"'
hostname=localhost; echo rm -rf /
を使用するとsource
、これはecho rm -rf /
2回実行され、実行中のユーザーのが変更されます$PROMPT_COMMAND
。代わりに、これを行います:
myscript.sh(Bash 4)
#!/bin/bash
typeset -A config # init array
config=( # set default values in config array
[username]="root"
[password]=""
[hostname]="localhost"
)
while read line
do
if echo $line | grep -F = &>/dev/null
then
varname=$(echo "$line" | cut -d '=' -f 1)
config[$varname]=$(echo "$line" | cut -d '=' -f 2-)
fi
done < myscript.conf
echo ${config[username]} # should be loaded from defaults
echo ${config[password]} # should be loaded from config file
echo ${config[hostname]} # includes the "injected" code, but it's fine here
echo ${config[PROMPT_COMMAND]} # also respects variables that you may not have
# been looking for, but they're sandboxed inside the $config array
myscript.sh(Mac / Bash 3互換)
#!/bin/bash
config() {
val=$(grep -E "^$1=" myscript.conf 2>/dev/null || echo "$1=__DEFAULT__" | head -n 1 | cut -d '=' -f 2-)
if [[ $val == __DEFAULT__ ]]
then
case $1 in
username)
echo -n "root"
;;
password)
echo -n ""
;;
hostname)
echo -n "localhost"
;;
esac
else
echo -n $val
fi
}
echo $(config username) # should be loaded from defaults
echo $(config password) # should be loaded from config file
echo $(config hostname) # includes the "injected" code, but it's fine here
echo $(config PROMPT_COMMAND) # also respects variables that you may not have
# been looking for, but they're sandboxed inside the $config array
私のコードにセキュリティ上の悪用を見つけたら返信してください。
*
、入力を適切に処理できないことですが、Bashではその文字を適切に処理できますか?
my\\password
MacとLinuxの両方で、Bash 3以降と互換性のあるクリーンでポータブルなバージョンを次に示します。
すべてのシェルスクリプトで巨大な、散らかった、重複した「デフォルト」設定関数の必要性を避けるために、すべてのデフォルトを個別のファイルで指定します。また、デフォルトのフォールバックを使用するか、使用しないかを選択できます。
config.cfg:
myvar=Hello World
config.cfg.defaults:
myvar=Default Value
othervar=Another Variable
config.shlib(これはライブラリであるため、シェバンラインはありません):
config_read_file() {
(grep -E "^${2}=" -m 1 "${1}" 2>/dev/null || echo "VAR=__UNDEFINED__") | head -n 1 | cut -d '=' -f 2-;
}
config_get() {
val="$(config_read_file config.cfg "${1}")";
if [ "${val}" = "__UNDEFINED__" ]; then
val="$(config_read_file config.cfg.defaults "${1}")";
fi
printf -- "%s" "${val}";
}
test.sh(または構成値を読み取るスクリプト):
#!/usr/bin/env bash
source config.shlib; # load the config library functions
echo "$(config_get myvar)"; # will be found in user-cfg
printf -- "%s\n" "$(config_get myvar)"; # safer way of echoing!
myvar="$(config_get myvar)"; # how to just read a value without echoing
echo "$(config_get othervar)"; # will fall back to defaults
echo "$(config_get bleh)"; # "__UNDEFINED__" since it isn't set anywhere
テストスクリプトの説明:
printf
行は何ですか?まあ、それはあなたが知っておくべきことです:echo
あなたが制御できないテキストを印刷するための悪いコマンドです。二重引用符を使用しても、フラグは解釈されます。myvar
(config.cfg
)に設定してみてください。-e
空行が表示echo
されます。これはフラグであると考えられるためです。しかしprintf
、その問題はありません。printf --
「これを印刷し、フラグとして何かを解釈しません」と言うと、"%s\n"
「最後の改行を含む文字列として出力をフォーマットし、最後に最後のパラメータは、フォーマットへのprintf関数の値であると言います。myvar="$(config_get myvar)";
です。それらを画面に印刷する場合は、printfを使用して、ユーザー構成にある可能性のあるエコー非互換の文字列に対して完全に安全であることをお勧めします。ただし、ユーザーが指定した変数がエコーする文字列の最初の文字でない場合、エコーは問題ありません。「フラグ」が解釈できる唯一の状況であるecho "foo: $(config_get myvar)";
ため、「foo」はダッシュで始まるため、文字列の残りの部分もフラグではないことをエコーに伝えます。:-)config.cfg.defaults
電話をかける際にそれらを定義する代わりに落としました$(config_get var_name "default_value")
。tritarget.org/static/...
構成ファイルを解析し、実行しないでください。
私は現在、非常に単純なXML構成を使用する職場でのアプリケーションを作成しています。
<config>
<username>username-or-email</username>
<password>the-password</password>
</config>
シェルスクリプト(「アプリケーション」)で、これはユーザー名を取得するために行います(多かれ少なかれ、シェル関数に入れました)。
username="$( xml sel -t -v '/config/username' "$config_file" )"
xml
コマンドがあるXMLStarletほとんどのUnixのために利用可能です。
アプリケーションの他の部分もXMLファイルでエンコードされたデータを処理するため、私はXMLを使用しています。
JSONを好む場合jq
、使いやすいシェルJSONパーサーがあります。
私の設定ファイルはJSONで次のようになります。
{
"username": "username-or-email",
"password": "the-password"
}
そして、スクリプトでユーザー名を取得します。
username="$( jq -r '.username' "$config_file" )"
eval
複数の値を設定するために使用している場合、構成ファイルの選択した部分を実行しています:-)。
eval
何もする必要はありません。既存のパーサーで標準形式を使用することによるパフォーマンスヒットは(外部ユーティリティであっても)、堅牢性、コード量、使いやすさ、および保守性と比較して無視できます。
最も一般的で、効率的で正しい方法は、を使用するsource
か.
、省略形として使用することです。例えば:
source /home/myuser/test/config
または
. /home/myuser/test/config
ただし、考慮すべき点は、追加のコードを挿入できる場合、追加の外部ソース構成ファイルを使用すると発生する可能性があるセキュリティの問題です。この問題を検出して解決する方法など、詳細については、http: //wiki.bash-hackers.org/howto/conffile#secure_itの「Secure it」セクションをご覧になることをお勧めします。
スクリプトでこれを使用します。
sed_escape() {
sed -e 's/[]\/$*.^[]/\\&/g'
}
cfg_write() { # path, key, value
cfg_delete "$1" "$2"
echo "$2=$3" >> "$1"
}
cfg_read() { # path, key -> value
test -f "$1" && grep "^$(echo "$2" | sed_escape)=" "$1" | sed "s/^$(echo "$2" | sed_escape)=//" | tail -1
}
cfg_delete() { # path, key
test -f "$1" && sed -i "/^$(echo $2 | sed_escape).*$/d" "$1"
}
cfg_haskey() { # path, key
test -f "$1" && grep "^$(echo "$2" | sed_escape)=" "$1" > /dev/null
}
すべての文字の組み合わせをサポートする必要がありますが、キーは=
それらに含めることができませんが、それはセパレータですから。それ以外は機能します。
% cfg_write test.conf mykey myvalue
% cfg_read test.conf mykey
myvalue
% cfg_delete test.conf mykey
% cfg_haskey test.conf mykey || echo "It's not here anymore"
It's not here anymore
また、これはsource
/を一切使用しないため、完全に安全です。eval
私のシナリオでは、source
または.
大丈夫でしたが、FOO=bar myscript.sh
構成された変数よりも優先されるローカル環境変数(つまり)をサポートしたかったのです。また、構成ファイルをユーザーが編集可能で、構成ファイルのソースに慣れている人にとって使いやすいものにし、非常に小さなスクリプトの主な推力から邪魔されないように、できるだけ小さく/シンプルに保つことを望んでいました。
これは私が思いついたものです:
CONF=${XDG_CONFIG_HOME:-~/config}/myscript.sh
if [ ! -f $CONF ]; then
cat > $CONF << CONF
VAR1="default value"
CONF
fi
. <(sed 's/^\([^=]\+\) *= *\(.*\)$/\1=${\1:-\2}/' < $CONF)
本質的に-変数の定義をチェックし(空白について非常に柔軟ではない)、それらの行を書き換えて、値がその変数のデフォルトに変換され、見つかった場合、変数はXDG_CONFIG_HOME
上記の変数のように変更されません。構成ファイルのこの変更されたバージョンを入手し、続行します。
将来の作業により、sed
スクリプトがより堅牢になり、奇妙に見える行や定義ではない行などが行末コメントで途切れることがなくなりますが、今のところこれで十分です。