If-else パラメータの置換

If-else パラメータの置換

私はbashを使用して一連の実験を実行しており、実験構成に応じた名前のディレクトリにログファイルを保存したいと思います。構成の一部の項目はブール値(true / false)です。次の構成を例にします。

batch_size=16
fp16=false
bf16=true
checkpoint_activations=true

上記で設定した実験ログファイルを次の名前のディレクトリに入力として保存したいと思います。

output_dir="experiment_bs${batch_size}_dt${fp16 if fp16=true else bf16}_${cp if checkpoint_activations=true else empty}"

もちろん、補助変数を宣言することもできます。

data_type=""
"${fp16}" && data_type=fp16
"${bf16}" && data_type=bf16
"${cp}" && cp="_cp" || cp=""
output_dir="experiment_bs${batch_size}_dt${data_type}${cp}"

しかし、これはちょっと愚かで希望的なものだと思います。パラメータの置換ここで役に立ちます。"${bf16:+bf16}"私の場合は役に立ちません。定義されるたびに、ブール値に関係なく常に "bf16"を印刷するためです。

このユースケースに適用できるパラメータ置換はありますか?それともこの問題に対するより良いオンラインソリューションはありますか?

data_type注:自分の設定で直接使用しない理由はアプリケーション固有の理由です。

答え1

必要なbashコマンドを入れることができるので、次$(...)のように書くことができます。

output_dir="experiment_bs${batch_size}_dt$([[ $fp16 = true ]] && echo $fp16 || echo $bf16)_$([[ $checkpoint_activation = true ]] && echo $cp || echo empty)"

読みやすくするために、次のように書くこともできます。

printf -v output_dir "experiment_bs%s_dt%s_%s" \
  "$batch_size" \
  "$([[ $fp16 = true ]] && echo "$fp16" || echo "$bf16")" \
  "$([[ $checkpoint_activation = true ]] && echo "$cp" || echo empty)"

サンプル入力を考えると...

batch_size=16
fp16=false
bf16=true
checkpoint_activations=true

...上記の両方が値を生成します。

experiment_bs16_dttrue_empty

答え2

では、Cを連想させる三項演算子形式(グローバルパターンと見なされないようにエイリアスと共に)を実装する関数をzsh定義できます。?? condition if-yes if-nocondition ? if-yes : if-no

alias "?='?'"
'?'() if eval $1; then print -r -- $2; else print -r -- $3; fi

output_dir=experiment_bs${batch_size}_dt$(? $fp16 fp16 bf16)_$(? $cp cp)

zsh 6.0+(2024-02-06 現在リリースされていない)の場合は、次のように変更できます。

alias "?='?'"
'?'() if eval $1; then REPLY=$2; else REPLY=$3; fi

output_dir=experiment_bs${batch_size}_dt${|? $fp16 fp16 bf16}_${|? $cp cp}

結果を得るためにプロセスを分岐することを避け、値が改行で終わることを許可します(この機能を呼び出す)発散(値の置き換え)mkshからコピーされます)。

三項演算子は、最初の引数のコードを評価して$2orを返すかどうかを決定するため、$3その$fp16/にはorを$cp含める必要があります。含まれているかどうかを確認するように変更します。truefalse$(? '[[ $fp16 = true ]]' fp16 bp16)$fp16true

また、見ることができますzshメーリングリストに関する議論三項演算子のためのいくつかの組み込みメソッド。そして今回のQ&Aは正しい詳細と選択肢を確認してください。

答え3

構成変数の場合は、その構成変数をコマンドに変換するため、fp16これは実行されません。誰かがそこに似たものを置く可能性を"${fp16}" && data_type=fp16考慮していない場合でも、Otaraは奇妙な形のエラーメッセージ(「tru:コマンドが見つかりません」など)につながる可能性があります。reboot

その場合、これは次のチェッカー機能を使用してスクリプトが取得する値を確認するように通知することもできます。

checkbool() {
    case $1 in
        true|false) return 0;; 
        *) echo >&2 "'$1' is an invalid boolean (must be 'true' or 'false'";
           exit 1;;
    esac
}
checkbool "$fp16"
checkbool "$bf16"
# ...

fp16また、bf16独立変数として意味があるかどうかを検討してください。

存在する:

"${fp16}" && data_type=fp16
"${bf16}" && data_type=bf16

と が両方fp16true の場合、bf16後者が優先します。どちらも設定されていない場合はdata_type空白のままにしてください。これはうまくいかないかもしれません。 あなたの具体的な状況はよくわかりませんが、data_type構成変数として直接使用する方が良いかどうか疑問に思います。さて、投稿には直接使用しない理由があると述べていますが、両方の設定がdata_type有効になっているか、どちらかが有効になっていない場合はどうなるか考えてみることがまだ重要です。

それにもかかわらず、パラメータ拡張が正しく機能するようにするには、"${bf16:+bf16}"nullをfalseとして使用し、空でない文字列をtrueとして使用する必要があります。たとえば、これを行うことはできますが、data_type="${enable_fp16:+fp16}"他の値を漏らさずに空の文字列をデフォルトにする良い方法がないと思うので、これも使いにくいようです。たとえば、反対の操作は空の文字列をに変換しますが、文字列を"${enable_fp16:-bf16}"そのまま返します。bf16yes

スクリプトがnull / null以外の値を使用している場合は、設定でそのビットの内部詳細をユーザーに公開しますか?それとも、構成値をスクリプトに実際に必要な値に変換するための条件文を作成する方が良いですか?

私は次のようなものを選択します。長く感じられるかもしれませんが、書くのにそれほど長い時間はかかりません。実際には次のようになります。

# config
batch_size=16
fp16=false
bf16=true
checkpoint_activations=true
## code
# this treats anything that's not 'true' as falsy
if   [[ $fp16  = true && $bf16 != true ]]; then
    data_type=fp16
elif [[ $fp16 != true && $bf16  = true ]]; then
    data_type=bf16
else
    echo >&2 "exactly one of fp16 and bf16 must be 'true'"
    exit 1
fi
cp=
if [[ $checkpoint_activations = true ]]; then
    cp=_cp
fi
# (maybe the value of $batch_size should also be checked, whatever

output_dir="experiment_bs${batch_size}_dt${data_type}${cp}"

data_typeもちろん、各条件で各入力変数の値を確認する代わりに、各割り当てが設定されていることを確認することもできます。 (上記のように3番目の変数を追加するには、2つの既存の条件を変更する必要があります。)

よりきちんとしたアプローチが必要な場合は、Stéphaneの回答の二重選択機能がBashでも少し変更されて機能します。まだ値を明示的に確認したいのですが、次のような場合もあります。

choose() if [[ $1 = true ]]; then printf "%s\n" "$2"
         else printf "%s\n" "$3"
         fi
data_type=$(choose "$fp16" fp16 bf16)
# etc.

もちろん、長くて明示的なコードと簡潔で簡潔なコードの間の決定は、常にプログラマに依存します。

関連情報