特定のユースケース:
私が使用したいcurl
URLは-v
、詳細なデバッグ情報を通常のようにstderrに出力し、応答の本文を通常のようにstdoutに出力しますが、正規表現が応答にあることを確認し、見つからない場合はゼロ以外のgrep
値で終了します.このコマンドの最も簡単な形式は次のとおりです。
curl -v "${url}" | grep -q "${pattern}"
ただし、grep
応答本文は消費されます。
私が試したこと:
私は見たことがない」一致するラインだけでなく、すべてのラインを出力するようにgrepを説得します。「これは次のことを示唆しています。
curl -v "${url}" | grep --color -E "${pattern}|$"
ただし、この色だけが一致を強調表示するため、
pattern
存在しない場合はゼロ以外の終了コードは提供されません。私も見たことがある」grep終了コードを取得しますが、すべての行を印刷する方法は?「これは次のことを示します。
curl -v "${url}" | tee /dev/stderr | grep -q "${pattern}"
終了コードで終了し、応答本文を印刷しますが、応答本文をstderrに印刷するので、stderrとstdoutストリームを別々に保持したいと
grep
思います。curl
次のように」パイプによる直接出力と標準出力「、私は努力した
curl -v "${url}" | tee >(grep -q "${pattern}")
しかし、これはすべてを印刷し、stdoutストリームとstderrストリームを正しく分離しますが、
grep
終了コードは削除されます。
現在の解決策:
これまで私が考えることができる唯一の方法は、応答本文を一時ファイルに貼り付けることです。
curl -v "${url}" | tee /tmp/response
grep -q "${pattern}" /tmp/response
これにより、正しい出力と正しい終了コードが表示されます。
しかし、一時ファイルなしで単一のパイプでこれを行う方法はありますか?
答え1
2番目の答えは正しい方向に行きます。努力する
{ curl -v "${url}" | tee /dev/fd/3 | grep -q "${pattern}";} 3>&1
簡単な説明:
ファイル記述子
3>&1
1 (標準出力) をファイル記述子 3 (標準使用のない最低記述子) にコピーします。これは、パイプライン全体(つまり、コマンドライン全体)の標準出力を表します。新しいファイル記述子(3)はパイプライン全体に有効です。任意の数字を使用できます(少なくとも最大9まで)。 POSIX シェルコマンド言語仕様、 2.7 リダイレクト、説明する
…すべての実装は少なくとも0から9までサポートする必要があります…
POSIX 互換シェルは 9 より大きい数字を認識しない場合があります。そして
bash(1)
説明する9より大きいファイル記述子を使用するリダイレクトは慎重に使用する必要があります。
tee /dev/fd/3
tee
ファイル記述子3に書き込むように指示するので、tee
パイプの標準出力に接続されます。- ディレクティブで使用したのと同じ番号をここで使用する必要があります
n>&1
。 - Linux以外のオペレーティングシステムでは、同様のファイル名が機能しない可能性があります。
/dev/fd/n
これはstdoutとは何の関係もありません。の
tee
, これはパイプラインですgrep
。- ディレクティブで使用したのと同じ番号をここで使用する必要があります
スティーブン・チャジェラス指摘:
grep -q
パターンが見つかったら終了します。tee
その後、出力が書き込まれると終了します。 GNU の実装は、tee
少なくとも-p
SIGPIPE を無視し、出力を取得するターゲットに引き続き書き込むことができます。さらに、一部のシェルはパイプラインの最後のコンポーネントのみを待機するため、ここでパターンが見つかるとスクリプトの残りのgrep -q
部分が渡されます。
正式に最初のことを確認しました。 (パターンが一致するとパイプが早く終了する可能性があります)1。
私は両方の問題を解決するために、次の改善されたソリューションを提供します。
{ curl -v "${url}" | tee /dev/fd/3 | { grep -q "${pattern}" && cat > /dev/null;} } 3>&1
メモ:
- データフローならいいえパターンと一致すると、
grep
ストリーム全体(つまり入力)を読み取るため、問題は発生しません。そして、この場合はgrep
「失敗」するので、&&
実行されず、複合コマンドはからcat
終了コードを返します。grep
つまり、失敗します(つまり、一致しません)。 - データフローならするパターンが一致すると(Stéphaneが指摘したように)、パターンが一致すると終了し、入力
grep
全体(つまり出力curl | tee
)は読み取られません。しかしこの場合にはgrep
「成功」するので&&
、cat
〜する実行すると、残りのデータが吸収されます(EOFまで)。これにより、tee
データストリーム全体がパイプに書き込まれ、curl
すべての出力が処理されるまでパイプ(つまりコマンドライン全体)は終了しません。
技術的には、これはまだ問題を残します。データフローがパターンと一致してgrep
「成功」すると、パイプラインの終了状態はの終了状態になりますcat
。なぜ失敗するのかわかりませんが、(pipe) | cat > /dev/null
理論的には可能です。これを防ぐために、私は以下を提案します。
{ curl -v "${url}" | tee /dev/fd/3 | if grep -q "${pattern}"; then cat > /dev/null; true; else false; fi; } 3>&1
成功した場合は明示的にtrueを返しgrep
、失敗した場合はfalseを返します。
______________
1つの オプションが追加され-p
ました。tee
バージョン 8.24 (2015-07-03)。
上記の変形のうち、必要に応じて 管路他の場所からの出力の場合は、通常どおりcurl
コマンドラインの末尾にパイプを追加するだけです。
{ curl …; fi; } 3>&1 | lpr
しかし、望むならリダイレクトファイルに保存するには、出力リダイレクトを挿入する必要があります。今後これ 3>&1
:
{カール...;手数料;}>結果ファイル3>&1
「ファイルへのパイピング」は間違った用語であることを覚えておいてください。
答え2
ここに特別なことがない場合は、grep
awkを使用してパターンマッチングを実行することもできます。
curl -v "${url}" | awk -v p="$pattern" '$0 ~ p {found=1} {print} END {exit ! found}'
-v p="$pattern"
awk 変数をp
シェル変数の値に設定します。pattern
$0 ~ p {found=1}
found
行p
が変数の正規表現と一致する場合、awk変数は1に設定されます。{print}
- すべての行を印刷します(このブロックには条件が指定されていないため)。END {exit ! found}
- 入力が終了すると負の状態で終了しますfound
(パターンが一致した場合は0、それ以外の場合は1)。
awkとgrepはさまざまな正規表現をサポートしているので、パターンを変更する必要があるかもしれません。