パイプを別のパイプに接続した結果が期待どおりに機能しません。

パイプを別のパイプに接続した結果が期待どおりに機能しません。

私は、bashプログラミングが初めてで、パイプとグループ化されたコマンドを使用して複数の引数を渡す方法を理解しようとしたいくつかのコマンドを試している間に、この奇妙な動作を発見して混乱しました。私が望むことを達成する他の方法があることを知っていますが、なぜこれが起こるのかを理解しようとしています。

write 組み込み機能プログラムを使用して、SSHサーバーに接続されているユーザー(ユーザーIDをUSER、TTYをTTYと考える)にメッセージを送信しようとしています。

次のようにメッセージをうまく送信できますが、

$ echo "some message" | write USER TTY

ただし、USERとTTYを渡す別のパイプを使用しようとすると、メッセージは送信されません。

$ echo "some message" | { echo "USER TTY" | xargs -o write; }

結果を見ると、bashは最初の部分($ echo "some message")を無視し、コマンドの実行後にメッセージを要求するようです。

{ echo "USER TTY" | xargs -o write; }そして同じことをすることに注意してくださいwrite USER TTY(明らかに?ここには私が認識しない違いがあると思います)。

繰り返しますが、これを行うより簡単な方法があることを知っていますが、コマンドのグループ化、パイピング、および入力引数を関数に渡すという点でbashがどのように機能するかを理解したいと思います。この分野のご意見をいただきありがとうございます。


私が宿題を求めるのを疑う人のために、人々がこの仕事に興味を持っているのを見て嬉しいです。私は実際に私のSSHサーバーのすべてのユーザーにメッセージを送信するためにエイリアスを作成しようとしています。これはwall簡単だと思いますが難しいです。面白いです。私がここで言及する内容を明確にするためです。

答え1

あなたの

echo "some message" | { echo "USER TTY" | xargs -o write; }

このコマンドは次のことを行うため、機能しません。

                        |-------------------------------------------|
echo "some message"   --+->   echo "USER TTY"  -->  xargs -o write; |
                        |-------------------------------------------|

標準入力xargsはから来るパイプですecho "USER TTY"。 getsecho "some message"は複合コマンドにパイプされます。これは  { echo "USER TTY" | xargs -o write; }実際にはgetsがコマンドecho "some message"にパイプされていないecho "USER TTY"ことを意味します。 PSオプションを指定したので、標準入力が端末に設定されます。言いたいならこれはうまくいくwritesome message
-oxargswrite

echo "USER TTY" | xargs -o write

writeその後、メッセージを入力すると、最も外側のメッセージにリンクされませんecho

少し持っていたらCommand B出力USERそして TTY (しかしカップルのみ)試してみることができます

echo "任意のメッセージ"を書く $(コマンドB)

修正する:以下は、仮想状況に適用できます。Command Bペアのみ出力 USERそして TTYしかし、実際の状況には適していない可能性があります。読んで、以下の議論を見てください。

あなたが言ったようにbashを使っていて、GNU xargs (Linuxの標準、他のUnix系オペレーティングシステムでも利用可能)がある場合は、次のことを試すこともできます。

echo "任意のメッセージ" xargs -a <(コマンドB)書く

リッチが提案したとおり xargsを使用して実行されたコマンドのstdinから読み取る

 POSIXがそのオプションを指定していないxargsため、GNUがある可能性があることに気づきました。-o


その他のアイデア:

  • これは問題の一部を解決することができます。ところで、どんな例も考えられません。

    コマンドA|コマンドB|CommandC;
    とは違う
    コマンドA|コマンドB|CommandC
    グループ化が違いを生むところがここです。私は明らかなことを見落としていることを願って、誰かが私に例を教えてくれることを期待しています。同様に、
    {コマンドA|コマンドB;CommandC
    同等です。

  • 次のパイプ

    コマンドA|コマンドB|CommandC
    役に立つかもしれません。簡単な例を見てください:

    $ echo GET | tr BEG VAC | od -cb
    0000000   C   A   T  \n
            103 101 124 012
    0000004
    
  • このようなパイプ

    コマンドA|コマンドB|パラメータ(オプション)  コマンドY
    役に立つと思ったら
    {コマンドA|コマンドB; } |パラメータ(オプション)  コマンドY
    つまり、入力を提供します。そうだと思うならCommand A | Command Bxargs
    コマンドA  |コマンドB|パラメータ(オプション)  コマンドY;
    つまり、Command Bに入力を提供し、xargsに  Command A入力を提供すると、上記の理由で動作しません。との間にデータフローを設定するCommand Y方法はありません。Command ACommand Y

  • しかし、

    コマンドA  |パラメータ(オプション)  -a <(コマンドB)  コマンドY
    さまざまな理由で問題が発生します。ここでは、ジョブは(潜在的に)複数回実行されますxargsCommand Yただし、複数実行のメカニズムがないため、Command A入力を複数回提供することはできません。Command Y

    本当に何度も実行したい場合はこれを行うことができますCommand A | Command Y

    コマンドB|パラメータ(オプション)  シューさん」コマンドA|コマンドY'多様
    例えば、

    $ echo S F u o n b | xargs -n2 sh -c 'date | tr "$1" "$2"; sleep 2' sh
    Fun, Mar 27, 2022 11:11:07 AM
    Son, Mar 27, 2022 11:11:09 AM
    Sub, Mar 27, 2022 11:11:11 AM
    

    これは実行されます

    date | tr S F
    date | tr u o
    date | tr n b
    

    date秒が異なるため(07、、、  09 )  11実際には3回実行されることがわかります。

    これはあなたの場合に動作します。 (上記の例では、Command Y 実際に必要な/必要なものと同じように、両方のパラメータから同時にパラメータを取得します。)次のような出力を生成する場合(たとえば)、Command BwriteCommand Bname1 tty1 name2 tty2 name3 tty3 …fred tty17 wilma tty42 barney ttyS23

    コマンドBxargs-n2 |(たぶん他のオプションがあるかもしれません)  sh -c「エコ」いくつかのニュース" | 書き込み "$1" "$2"' sh

  • Command Aただし、(あなたの場合)複数回実行する必要がない場合はecho "some message"(毎回同じ出力が生成されるため)、これを利用してCommand A一度実行して出力を保存できます。

    エコ」いくつかのニュース> tmpファイル
            コマンドBxargs-n2 |(たぶん他のオプションがあるかもしれません)  sh -c '"$1" "$2" 書き込み < tmpfile' sh;
            rm 一時ファイル

  • したがって、上記の解決策はCommand A複数の実行を防ぎますが、ユーザーごとに一度だけシェルを実行します。これは望ましくありません。xargsシェルを何度も起動せずにこの状況を処理する方法はありません。 (誰かが指摘してくれることを期待する。) シェルを使わないと処理しにくい。それでも、xargs「単純な」コマンドのみを処理できるため、リダイレクト(<)やパイプ(|)はありません。  しかし、これを処理する代わりに、xargsシェルをより創造的に使用できます。各行がCommand B一対の出力を生成するとします(例:namen ttynfred tty17 (新しいチーム) wilma tty42 (新しいチーム) barney ttyS23 (新しいチーム) )それでできます

    エコ」いくつかのニュース> tmpファイル
                  コマンドB|utを読み取るときに "$u" "$t" < tmpfile;
                  rm 一時ファイル


質問のいくつかの重要な点を詳しく見て、解決してください。

  • 存在する

    エコ」いくつかのニュース"|{エコ"ユーザー テレタイピスト" | xargs -o 書き込み; }
    シェルは実際にその部分を無視しません。echo "some message"それ自体。  しかし、答えの最初の部分で説明したように、その部分に効果的にリンクされます。したがって、標準入力を読みません。echo "USER TTY"echomessage進まなかった。

  • { echo "USER TTY" | xargs -o write; }そして同じことをすることに注意してくださいwrite USER TTY (明らかに?ここには私が認識しない違いがあると思います)。

    いいですね。まだ明確ではない場合に備えて、両方のバリアントはwrite引数を使用してプログラムを呼び出します。USERそして TTY。標準 I/O ストリームを変更せずに、単に既存の stdin から読み込む単純なコマンドです。これはtty、パイプ、またはファイルです。このバリアントはstdinをリダイレクトする役割を果たすため、stdinをリダイレクトします。通常、(指定されていない場合)stdinは実行されるコマンドの引数ソースであるため、より大きな環境では広く使用されているstdinにアクセスできません。 write USER TTYxargswrite/dev/tty-o-axargsxargs情報for  write) - だから作成できませんそれ実行者が使用できるデータストリーム。したがって、-aorを  -o指定しないと、xargsランチャーの標準入力がに設定されます  /dev/null

  • 気づく

    コマンドB| xargs -n2 -o 書き込み
    少し機能しますが、入力を入力する必要があります。情報write繰り返しの間にメッセージを保存する方法がなくても、各ユーザーに対して一度呼び出されるため、各ユーザーに対して一度呼び出されます。あなた できる理論的には、複数のユーザーに異なるメッセージを送信したい場合は、このオプションを使用できますが、どのユーザーと話しているかについてのヒントやフィードバックを提供しないため、実用的ではありません。

答え2

最初のポイント

  • echo "<command arguments>" | commandパラメータを入力としてエコーしているため、これは使用できません。コマンド引数の入力を使用するには、xargsを「コンバータ」として使用して入力をコマンドライン引数に変換する必要があります。

xargsシェル(bashなど)の一部ではない外部バイナリ。 xargsには複数のバージョンがありますが、-oこのフラグを持つバージョンはGNUユーザースペースにあります。通常、Linuxではman <command name>。からWebバージョンマニュアル:

このマニュアルページには xargs の GNU バージョンが文書化されています。 xargsは、標準入力から次に区切られた項目を読み取ります。スペース二重引用符、一重引用符、またはバックスラッシュで保護できます。) または 改行文字、初期引数を使用してコマンド(デフォルトはecho)を1回以上実行し、標準入力から読み取った項目を実行します。

xargsがすることは、入力をテーブルの行と同じフィールドに分割することです。各フィールドに対して、xargs(デフォルト)はシステム定義の制限まで引数をプログラムに渡します。スペースと改行文字を区切り文字(|表など)として扱います。入力 "USER TTY" には USER と TTY の間にスペースがあるため、xargs は次のようなテーブル行のようなものを生成します。

ARG1 ARG2
ユーザー テレタイピスト

2番目のポイント

  • コマンドを実行するたびにファイルをパイプするには、以下のように xargs を使用してファイルを実行コマンドにパイプします。ただし、これにより新しいシェルが開き、現在のセッションの環境変数を使用できなくなります。
echo "USER TTY" | xargs -I REPL sh -c 'echo "some message" | write REPL'

分析: -Iフラグは、REPLコマンドの実行時にフィールドに置き換えられるパラメーターを使用します。

ユーザー テレタイピスト

だからxargsが実行されます

sh -c 'echo "some message" | write USER TTY'

関連情報