シェルスクリプトに設定ファイルを使用する

自分のスクリプトに設定ファイルを作成する必要があります:
例を次に示します:

スクリプト:

#!/bin/bash source /home/myuser/test/config echo "Name=$nam" >&2 echo "Surname=$sur" >&2 

/home/myuser/test/configのコンテンツ:

nam="Mark" sur="Brown" 

動作します!

私の質問:これは正しい方法ですこれを行うか、「他の方法がありますか?

コメント

  • 変数を一番上に配置する必要があります。I’それが機能することに驚いています。とにかく、なぜ設定ファイルが必要なのですか?これらの変数を別の場所で使用する予定ですか?
  • Faheem、スクリプトには多くのオプションがあるため、変数が必要です。設定ファイルはスクリプトを例示します。ありがとう
  • IMHOは問題ありません。私はこの方法で行います。
  • abcdeもこの方法で行います。これは非常に大きなプログラムです(シェルスクリプトの場合)。ここで確認できます。

回答

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バージョン4.0ソリューションであり、残念ながらAppleによって課せられた非常識なライセンス問題の影響を受け、Macではデフォルトで利用できません
  • @Sukima良い点。 ‘ Bash 3と互換性のあるバージョンを追加しました。その弱点は、入力で*を適切に処理しないことですが、 Bashでその文字を適切に処理するものは何ですか?
  • パスワードにバックスラッシュが含まれていると、最初のスクリプトは失敗します。
  • @Kusalanandaバックスラッシュがエスケープされている場合はどうなりますか? my\\password
  • このバージョンでは、構成ファイルの処理に数秒かかります。gw0のソリューションの方がはるかに高速であることがわかりました。

回答

構成ファイルを解析し、実行しないでください。

現在、作業中のアプリケーションを作成しています。非常に単純なXML構成を使用します。

<config> <username>username-or-email</username> <password>the-password</password> </config> 

シェルスクリプト(「アプリケーション」)では、これがユーザー名で取得するために行うことです(詳細または少ないですが、私はそれをシェル関数に入れました):

username="$( xml sel -t -v "/config/username" "$config_file" )" 

xmlコマンドは XMLStarlet 。ほとんどのUnicesで利用できます。

アプリケーションの他の部分もXMLファイルにエンコードされたデータを処理するため、XMLを使用しています。最も簡単でした。

JSONを使用する場合は、 jq があります。使いやすいシェルJSONパーサー。

私の構成ファイルはJSでは次のようになります。オン:

{ "username": "username-or-email", "password": "the-password" } 

次に、スクリプトでユーザー名を取得します:

username="$( jq -r ".username" "$config_file" )" 

コメント

  • スクリプトを実行することには、いくつかの長所と短所があります。主な欠点はセキュリティです。誰かが設定ファイルを変更できれば、コードを実行でき、ばかげた証拠にするのが難しくなります。利点は速度です。簡単なテストでは、pqを実行するよりも構成ファイルを取得する方が10,000倍高速であり、柔軟性があります。モンキーパッチを適用するPythonが好きな人なら誰でも、これを高く評価できます。
  • @icarus大きさ通常、構成ファイルに遭遇しますか。また、1回のセッションでそれらを解析する必要がある頻度はどれくらいですか。 XMLまたはJSONから一度に複数の値が含まれる可能性があることにも注意してください。
  • 通常、値はわずか(1〜3)です。 evalを使用して複数の値を設定している場合は、構成ファイルの選択した部分を実行しています:-)
  • @icarus配列を考えていました… evalは何もする必要はありません。 (’は外部ユーティリティですが)既存のパーサーで標準フォーマットを使用することによるパフォーマンスへの影響は、堅牢性、コードの量、使いやすさに比べればごくわずかです。 、および保守性。
  • +1 for “構成ファイルを解析し、’実行しないでください”

回答

これは、Bash3およびと互換性のあるクリーンでポータブルなバージョンです。 MacとLinuxの両方で。

すべてのシェルスクリプトで巨大で雑然とした重複した「デフォルト」設定関数が必要ないように、すべてのデフォルトを個別のファイルで指定します。また、デフォルトのフォールバックを使用するかどうかを選択できます:

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 

テストスクリプトの説明:

  • test.shでのconfig_getのすべての使用法は二重引用符で囲まれていることに注意してください。すべてのconfig_getを二重引用符で囲むことにより、変数値のテキストがフラグとして誤って解釈されないようにします。 また、構成値の行に複数のスペースがあるなど、空白を適切に保持することができます。
  • そして、そのprintf行は何ですか? 「注意すべき点:echoは、制御できないテキストを印刷するための不適切なコマンドです。二重引用符を使用しても、フラグは解釈されます。 myvarconfig.cfg内)を-eに設定してみると、空の行が表示されます。 echoはそれを「フラグ」と見なすためです。ただし、printfにはその問題はありません。 printf --は「これを印刷し、何もフラグとして解釈しない」と言い、"%s\n"は「出力を文字列としてフォーマットする」と言います末尾に改行があり、最後に最後のパラメータはprintfがフォーマットする値です。
  • 値を画面にエコーしない場合は、myvar="$(config_get myvar)";。それらを画面に出力する場合は、printfを使用して、ユーザー構成に含まれる可能性のあるエコーと互換性のない文字列に対して完全に安全にすることをお勧めします。ただし、ユーザーが指定した変数がでない場合はエコーで問題ありません。 エコーしている文字列の最初の文字。これは、「フラグ」を解釈できる唯一の状況であるため、echo "foo: $(config_get myvar)";のようなものは安全です。 foo “はダッシュで始まらないため、文字列の残りの部分もそのフラグではないことをエコーに通知します。:-)

コメント

  • @ user2993656元のコードに正しいものではなくプライベート構成ファイル名(environment.cfg)が含まれていることを確認していただきありがとうございます。”についてecho -n “編集しましたが、使用するシェルによって異なります。Mac/ Linux Bashでは、” echo -n “は、”新しい行を末尾に付けずにエコーすることを意味します”、改行の末尾を避けるために行いました。しかし、それがなくてもまったく同じように機能するようです。編集していただきありがとうございます。
  • 実際、エコーの代わりにprintfを使用するように書き直しました。これにより、’設定値の”フラグ”をエコーが誤って解釈するリスクを取り除きます。
  • 私はこのバージョンが本当に好きです。 $(config_get var_name "default_value")を呼び出すときに定義する代わりに、config.cfg.defaultsを削除しました。 tritarget.org/static/ …
  • 同様に、これはすばらしいことです。

回答

最も一般的で効率的かつ正しい方法は、source、または省略形として.。例:

source /home/myuser/test/config 

または

. /home/myuser/test/config 

ただし、考慮すべき点は追加のコードを挿入できる場合、追加の外部ソース構成ファイルを使用するとセキュリティの問題が発生する可能性があります。この問題を検出して解決する方法など、詳細については、 http://wiki.bash-hackers.org/howto/conffile#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または

コメント

  • これは想定されていません’ ‘が存在しない場合、最初に構成ファイルを作成しますか?’ tですが、touch -a "${path}"はもちろん、mtimeを軽率に更新することなく、存在することを保証します。

回答

これは簡潔で安全:

# Read common vars from common.vars # the incantation here ensures (by env) that only key=value pairs are present # then declare-ing the result puts those vars in our environment declare $(env -i `cat common.vars`) 

-iは、

更新:セキュリティの例は次のとおりです

env -i "touch evil1 foo=omg boo=$(touch evil2)" 

タッチされたファイルは生成されません。Macでテスト済みbashを使用する、つまりbsdenvを使用する。

コメント

  • これをcommに配置すると、evil1ファイルとevil2ファイルがどのように作成されるかを確認してください。 on.vars “ `touch evil1 foo = omg boo = $(touch evil2)` “
  • @pihentagy私にとって、以下はタッチされたファイルを生成しませんenv -i 'touch evil1 foo=omg boo=$(touch evil2)' 。 Macで実行しています。
  • 確かに、fooにアクセスできません。 ‘をenv -i ... myscript.sh試しましたが、そのスクリプト内でfooが定義されていません。ただし、”ガベージ”を削除すると機能します。説明してくれてありがとう。 :+1:

回答

ほとんどのユーザーは、多くのコンテナーではありませんが、すでにgitバイナリ。したがって、 git config は、以下の例のように、競合しない専用の構成ファイルを使用したアプリケーション構成管理ですか?

# Set $ git config -f ~/.myapp core.mykey myval # Get $ git config -f ~/.myapp core.mykey myval # Get invalid $ git config -f ~/.myapp core.mykey $ echo $? 1 # List git config -f ~/.myapp -l core.mykey=myval # View $ cat ~/.myapp [core] mykey = myval 

追加のコマンドについては、そのmanを参照してください。ページ。最初に構成ファイルが存在することを確認することをお勧めします:

touch -a ~/.myapp 

回答

これは安全で短いようです。これを冷酷に割ってください。より良い方法を知りたいのですが。

TL; DR;

while read LINE; do declare "$LINE"; done < evil.conf 

bash4.3.48を使用しています。

bash --posixにも準拠しています。テストについては下部を参照してください。

ただし、sh declareのため、サポートされていません。


証明が必要な人のための基本的なテスト

ファイルevil.confを作成します

 echo > evil.conf " A=1 B=2 C=$(echo hello) # Could produce side-effect D=`touch evil` C=$((1+2)) E=$(ping 8.8.8.8 -n 3) echo hello # Could produce visible side-effect touch evil2 ping 8.8.8.8 -n 3 F=ok"  

スニペットを使用して構成をロードします

 while read LINE; do declare "$LINE"; done < evil.conf  

出力(サニタイザーの動作を参照)

bash: declare: `": not a valid identifier bash: declare: `": not a valid identifier bash: declare: `# Could produce side-effect": not a valid identifier bash: declare: `echo hello": not a valid identifier bash: declare: `": not a valid identifier bash: declare: `# Could produce visible side-effect": not a valid identifier bash: declare: `touch evil2": not a valid identifier bash: declare: `ping 8.8.8.8 -n 3": not a valid identifier 

今すぐ値を確認しましょう

 for V in A B C D E F; do declare -p $V; done  
declare -- A="1" declare -- B="2" declare -- C="\$((1+2))" declare -- D="\`touch evil\`" declare -- E="\$(ping 8.8.8.8 -n 3)" declare -- F="ok" 

副作用を確認します(副作用なし):

 ls evil evil2  
ls: cannot access "evil": No such file or directory ls: cannot access "evil2": No such file or directory 

付録。テストbash --posix

bash -c "while read LINE; do declare "$LINE"; done < evil.conf; for V in A B C D E F; do declare -p $V; done" --posix 

回答

私のシナリオでは、sourceまたは. w結構ですが、構成された変数よりも優先されるローカル環境変数(つまり、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スクリプトがより堅牢になり、奇妙に見える行やそうでない行が除外される可能性があります。」定義などは行末のコメントで壊れませんが、今のところこれで十分です。

回答

あなたはそれを行うことができます:

#!/bin/bash name="mohsen" age=35 cat > /home/myuser/test/config << EOF Name=$name Age=$age EOF 

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です