シェルスクリプトや関数でパイプをバイパスする方法はありますか?

シェルスクリプトや関数でパイプをバイパスする方法はありますか?

psオプションが何であれ、結果をgrepしても、列が何であるpsかを示す出力の最初の行を取得できるように小さなラッパーを作成したいと思います。たとえば、の出力はps_wrapper -aux | grep thing次のようになります。

USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
name       33925  1.0  0.0   9972  5528 pts/0    Ss   19:34   0:00 /bin/thing

私はそれがうまくいくかどうかを確認しようとし、tee次のことを試しました。

function ps_wrapper {
    tmpfile=$(mktemp)
    ps $@ > $tmpfile
    head -n 1 $tmpfile >> /dev/stdout
    cat $tmpfile
}

しかし私は「手動で」書いたが/dev/stdoutまだgrep

ps印刷される列は使用されているオプションによって変更される可能性があるため、実行中の実際のコマンドの最初の行を印刷したいと思います。

私はzshを使用していますが、より適切なものがあれば他のものを使用しても大丈夫です。

読んでくれてありがとう。

ベストアンサー1

一般的に「パイプをバイパス」することは不可能です。つまり、パイプなしで出力が行ける場所に出力を送ることです。ただし、パイプをバイパスし、端末など、選択した別の場所に出力をリダイレクトできます。特殊ファイルは/dev/tty常に現在の端末を表します。

function ps_wrapper {
    tmpfile=$(mktemp)
    ps "$@" > $tmpfile
    head -n 1 $tmpfile >/dev/tty
    cat $tmpfile
    rm $tmpfile
}

元の場所を「保存」して下に渡すと、パイプをバイパスすることもできます。ファイル記述子を通して。しかし、ps_wrapper関数ではそれを行うことはできません。

function ps_wrapper {
    tmpfile=$(mktemp)
    ps "$@" > $tmpfile
    head -n 1 $tmpfile >&3
    cat $tmpfile
    rm $tmpfile
}
{ ps_wrapper … | grep …; } 3>&1

一時ファイルの生成を防ぐ方法はいくつかあります。いくつか言及します。特に明記しない限り、この回答の解決策は次の場合にbashで動作します。変数とコマンドの置換の周りに二重引用符を追加する

関数の呼び出し方法を変更したい場合は、headパイプの右側から連続して両方を呼び出すことができます。最初の行を読んで印刷し、残りは後続のジョブのために残します。grephead

ps … | { head -n 1; grep …; }

あなたはできますプロセスの交換そしてteeまたはzshにtee似た機能組み込み( multios)出力をコピーするには、あるストリームを選択したコマンドに送信head -n 1し、別のストリームを選択したコマンドに送信します。ただし、各ストリームをコマンドにパイプすると、2つの間で競合が発生し、head速度が十分に速くない場合、最初の行が一番上に表示されない可能性があります。かなり高速なので、頻繁に動作することがありますが、たとえばディスクキャッシュにheadありますが、そうでない場合は保証できません。grephead

ps | tee >(head -n 1 >/dev/tty) | grep …
ps >(head -n 1 >/dev/tty) | grep …         # zsh only, only if multios is not disabled

awkを使用して最初の行を表示してから、残りの行を渡すことができます。

function ps_wrapper {
  ps "$@" | awk 'NR == 1 {print >"/dev/tty"} NR != 1 {print}'
}

説明する:

  • awkは入力を1行ずつ処理します。
  • 健康状態{パスワード}実装するパスワード満足のラインで健康状態
  • NR行番号です。
  • awkのリダイレクトはシェルとまったく同じではありませんが、十分に似ています。
  • print引数なしで入力行を印刷します。

別のアプローチは、フィルタ機能を単一のコマンドで構築することです。引数に表示されないps区切り文字として使用する文字列を選択します(例:)|

function pipe_preserving_first_line {
  local lhs
  lhs=()
  while [[ $1 != '|' ]]; do
    lhs+=($1)
    shift
  done
  shift
  "${lhs[@]}" | {
    head -n 1;
    "$@"
  }
}
pipe_preserving_first_line ps u \| grep foo

たとえば、コマンド名でプロセスを一致させるには、psgrepの代わりに一致関数を使用できます。-C

ps uww -C mycommand

grepを使用することに加えて、マッチングpgrepツールを使用することもできます。

ps -p $(pgrep -d, mycommand)

おすすめ記事