Bash 5.0では、キャプチャを${PIPESTATUS[@]}
通過したいと思いますeval
。しかし、マスクはマスクと同じではないようeval
です。結果から抽出する方法はありますか?以下のコマンド文字列に何かを追加しても機能しないようです。${PIPESTATUS[@]}
$?
${PIPESTATUS[-1]}
${PIPESTATUS[@]}
eval
&& array=( ${PIPESTATUS[@]} ) && export array
$?
これが単純ではないと仮定するのは正しいですか${PIPESTATUS[-1]}
?
私のサンプルコード(rootとして実行):
#!/usr/bin/env bash
#without eval, ${PIPESTATUS[@]} has two entries as it should.
apt-get install -y java-17-openjdk-amd64 2>&1 | tee -a ~/log
commandsPipestatus=( ${PIPESTATUS[@]} )
for status in ${commandsPipestatus[@]}; do
echo $status
done
echo ""
echo ""
#with eval, ${PIPESTATUS[@]} has one entry and is equal to $?
commandstring="apt-get install -y java-17-openjdk-amd64 2>&1 | tee -a ~/log"
eval "$commandstring"
commandsPipestatus=( ${PIPESTATUS[@]} )
for status in ${commandsPipestatus[@]}; do
echo $status
done
編集:PIPESTATUSの元の説明でいくつかの技術的な修正を修正しました。また、以下の回答に基づいていくつかの説明があります。
- プログラムでコマンド文字列を作成しているため、これを使用しています
eval
。その中には、bash -c
他のユーザーとしてコマンドを実行してください。 - 設定を見た
pipefail
結果、時には機能することがありますが、必ずしも機能するわけではありません。パイプラインのさまざまな段階の状態を知る必要がある場合もあるからです。設定set -o pipefail
してから設定を解除できるとうまくいきますが、設定を解除する方法がわからないので、単にサブシェルを終了し、未設定の新しいサブシェルに進むことはpipefail
できません。pipefail
たとえば、シェルオプションの設定を解除するにはpipefail
? - 埋め込み配列をエクスポートする方法の上記の理解が間違っていますか
PIPESTATUS
?サブシェルPIPESTATUS
からどのように簡単にエクスポートできますか?eval
編集2:以下に示す素晴らしい答えのおかげで、最終的な決定は、リダイレクトを含むコマンド文字列を動的に組み合わせる状況(評価が必要な理由)を使用し、set -o pipefail
実行後に実行することでしたset +o pipefail
。
また、前回から数年間のbash経験があるため、動的ビルドコマンドを実行するためのベストプラクティスを再検討しています。私のユースケースeval
は次のとおりです
- コマンドにが含まれる可能性があるため、これを実行するため
bash -c
に使用することはできません。bash -c
- リダイレクトを含めることができます。
>> log
- 動的コマンドは、デバッグ目的でパラメータを追加することもできます。
私が知っている限り、1では別のことを行うことができ、3ではとのようなものをset -x [command]
使用trap
する必要があります。 2の解決策はまだ見つかりませんでした。
答え1
$?
最後のコマンド実行の最も右側のコンポーネント(または設定されている場合は最も右側の失敗したコンポーネント)の状態を含みますpipefail
。ただし、これは!
prefixキーワードで変更できます。
要素は$PIPESTATUS
前のパイプラインの各コンポーネントの終了状態であり、!
値には影響しません。
それ以降は(exit 1) | (exit 2) | (exit 3)
($PIPESTATUS
1 2 3)になり、$?
3になります。それ以降は! (exit 1) | (exit 2) | (exit 3)
同じです$PIPESTATUS
が、$?
0になっています$(( ! 3 ))
。
false
または(exit 1)
まだ1つのコンポーネントを持つパイプです。最初のケースは単純なコマンドで、2番目のケースはサブシェルなので、PIPESTATUS=(1)
and $?
= 1を取得します。
これは同じです。eval 'any shell code'
ただ単純なコマンドです。
これには、eval 'A | B' | C
終了$PIPESTATUS
状態eval
(最後に実行したコマンドの終了状態)B
と終了状態が含まれますC
。(A | B) | C
ところで、同様です。
次のことができます。
eval '
A | B
saved_STATUS=$? saved_PIPESTATUS=( "${PIPESTATUS[@]}" )
'
呼び出すインタプリタでわかるように、パイプラインコンポーネントの状態を維持したいので、eval
あなたの場合は次のようにします。
preserve_status='
saved_STATUS=$? saved_PIPESTATUS=( "${PIPESTATUS[@]}" )
'
eval "$commandstring $preserve_status"
commandsPipestatus=( "${saved_PIPESTATUS[@]}" )
for status in "${commandsPipestatus[@]}"; do
echo "$status"
done
しかし、ここでは以下が欲しいようです。
install_java() (
set -o pipefail
apt-get install -y java-17-openjdk-amd64 2>&1 | tee -a ~/log
)
それは:
- 変数ではなく関数にコードを保存する
- 関数が失敗したり失敗した場合に失敗を返すには、
pipefail
このオプションを使用します。関数本体は、より一般的なサブシェルではなく、サブシェルです。apt-get
tee
コマンドグループcommand({ ...; }
) は、オプションがローカルでのみ設定されていることを示します。最新バージョンでは、サブシェルなしで関数にローカルに設定されたオプションをbash
使用することもできます。local -; set -o pipefail
ただし、通常の出力とエラーの両方がapt-get
stdoutに送信されます。
を使用している場合は、zsh
次のことができます。
exec {log}>> ~/log # at the beginning of the script for instance.
apt-get install -y java-17-openjdk-amd64 >&1 >&$log 2>&2 2>&$log
stderrのstdoutをapt-get
ログとその元の宛先に送信します。内部的にはingが完了し、終了tee
状態が維持されます。apt-get
次のように、このためのヘルパー関数を作成することもできます。
#! /bin/zsh -
die() { print -ru2 -C1 -- "$@"; exit 1; }
exec {log}>> ~/log
with_log() { "$@" >&1 >&$log 2>&2 2>&$log; }
with_log apt-get ... || die "Aborting..."
答え2
代わりに、関数を使用する方がeval
良い解決策かもしれません。
したがって、必要に応じて複数のパッケージをインストールできるようにするだけです。
install() {
apt-get install -y "${1}" 2>&1 | tee -a ~/log
}
packages=("java-17-openjdk-amd64")
for pkg in "${packages[@]}" ; do
install pkg
# replace below with actions based on status
printf "%s\n" "${PIPESTATUS[@]}"
done
set -o pipefail
apt-get
すべてのコマンドを確認せずにエラーを削除する方法ですが、PIPESTATUS
ログに書き込めない場合は、状況が若干ぼやけることがあります。tee
編集:追加情報に基づいて
私はあなたが経験している問題がeval
サブシェル(または少なくともそれに似たもの)でコマンドを実行するためだと思います。終了コードだけを受け取るように感じます(家庭が間違っていて幸いです)。
コマンドからパイプの状態を印刷できますが、インポートが難しい場合があります。
このモンスターは処理する1つの方法ですが、非常に複雑で、テキスト処理の面でコマンドをいくつかカスタマイズする必要があり、最終的には終了コードのみを受け取ります。
# just some stuff to print some text and exit with a non zero
# before the next pipeline step masks the exit code
foo='bash -c "echo hello;false" | tee log;echo PIPE ${PIPESTATUS[@]}'
# awk grabs the line with the pipe status text, pulls one of
# the exit codes from it and exits with it, otherwise prints
# the output as seen
eval "$foo" | awk '
{if ($1=="PIPE"){print "just showing you I saw all the codes "$0;exit $2}; print $0}'
echo $?
awk
さまざまなパイプライン状態をキャプチャするために出力を提供するために何かをすることができるようですがreadarray
、それでは非常に汚れてしまいます(すでに汚れた状態にもかかわらず)。
状況がこのように醜くなると、全体的なアプローチが間違っているかどうか疑問になり始める必要があります。
おそらく要件をさらに詳しく説明すると、これを達成するための別の方法が表示されることがあります。