it-swarm-ja.com

シェルの制御およびリダイレクト演算子は何ですか?

さまざまなコマンドをさまざまなシンボルに接続するチュートリアルをオンラインでよく見ます。例えば:

command1 |  command2
command1 &  command2
command1 || command2    
command1 && command2

他のものはコマンドをファイルに接続しているようです:

command1  > file1
command1  >> file1

これは何だ?彼らは何と呼ばれている?彼らは何をしますか?それらはもっとありますか?


この質問に関するメタスレッド。

278
terdon

これらはシェルオペレーターと呼ばれ、そうです。 2つの主要なクラス 制御演算子 および リダイレクト演算子 の間で最も一般的なものの概要と、bashシェルに関してそれらがどのように機能するかを簡単に説明します。

A.制御演算子

シェルコマンド言語で、制御機能を実行するトークン。
以下の記号のいずれかです。

_&   &&   (   )   ;   ;;   <newline>   |   ||
_

そしてbashでは_|&_です。

_!_は制御演算子ではありませんがですが、 予約語 です。 算術式 内およびテスト構成内で論理NOT [否定演算子]になります(スペース区切りが必要です)。

A.1ターミネーターのリスト

  • _;_:最初のコマンドの結果に関係なく、次のコマンドが終了した後に実行します。

    _command1 ; command2
    _

    最初に_command1_がフォアグラウンドで実行され、終了すると_command2_が実行されます。

    文字列リテラルにない、または特定のキーワードの後に​​ない改行は、セミコロン演算子とnotは同等です。 _;_で区切られた単純なコマンドのリストは、まだlistです。シェルのパーサーでは、_;_に続く単純なコマンドを引き続き読み取る必要があります。実行前に区切られた単純なコマンドですが、改行はコマンドリスト全体またはリストのリストを区切ることができます。違いは微妙ですが複雑です。シェルに改行に続くデータを読み取るための以前の必須事項がない場合、改行は、シェルがすでに読み取った単純なコマンドの評価を開始できるポイントをマークしますが、_;_セミコロンにはありません。

  • _&_:これはバックグラウンドでコマンドを実行し、同じシェルで作業を続行できるようにします。

    _ command1 & command2
    _

    ここでは、_command1_がバックグラウンドで起動され、_command2_が終了するのを待たずに、_command1_がフォアグラウンドですぐに実行を開始します。

    _command1_の後の改行はオプションです。

A.2論理演算子

  • _&&_:ANDリストを作成するために使用され、別のコマンドが正常に終了した場合にのみ、1つのコマンドを実行できます。

    _ command1 && command2
    _

    ここでは、_command2_は、_command1_が完了した後に実行され、_command1_が成功した場合(終了コードが0の場合)はonlyになります。どちらのコマンドもフォアグラウンドで実行されます。

    このコマンドはまた書くことができます

    _if command1
    then command2
    else false
    fi
    _

    戻りステータスが無視される場合は、単に_if command1; then command2; fi_。

  • _||_:ORリストを作成するために使用され、別のコマンドが正常に終了しなかった場合にのみ、1つのコマンドを実行できます。

    _ command1 || command2
    _

    ここで、_command2_は、_command1_が失敗した場合にのみ実行されます(0以外の終了ステータスを返した場合)。どちらのコマンドもフォアグラウンドで実行されます。

    このコマンドはまた書くことができます

    _if command1
    then true
    else command2
    fi
    _

    または短い方法で_if ! command1; then command2; fi_。

    _&&_および_||_は左結合です。詳細については、 シェルの論理演算子の優先順位&&、 を参照してください。

  • _!_:これは「not」演算子として機能する予約語ですが、コマンドの戻りステータスを否定するために使用されます—コマンドがゼロ以外のステータスを返す場合は0を返し、次の場合は1を返しますステータス0を返します。また、testユーティリティの論理NOT。

    _! command1
    
    [ ! a = a ]
    _

    そして、算術式内の真のNOT演算子:

    _$ echo $((!0)) $((!23))
    1 0
    _

A.3パイプ演算子

  • _|_:パイプ演算子。1つのコマンドの出力を別のコマンドの入力として渡します。パイプ演算子から作成されたコマンドは pipeline と呼ばれます。

    _ command1 | command2
    _

    _command1_によって出力される出力は、_command2_への入力として渡されます。

  • _|&_:これは、bashおよびzshでの_2>&1 |_の短縮形です。 1つのコマンドの標準出力と標準エラーの両方を別のコマンドの入力として渡します。

    _command1 |& command2
    _

A.4その他のリストの句読点

_;;_は、単に caseステートメント の終わりを示すために使用されます。 Ksh、bash、およびzshは、次のケースにフォールスルーする_;&_と、後続のケースを続行してテストする_;;&_(ATT kshにはない)もサポートします。

_(_および_)_は グループコマンド に使用され、サブシェルで起動します。 _{_および_}_もコマンドをグループ化しますが、サブシェルでは起動しません。シェル構文のかっこ、角かっこ、中かっこのさまざまな種類の説明については、 この答え を参照してください。

B.リダイレクト演算子

リダイレクト演算子

シェルコマンド言語で、リダイレクト機能を実行するトークン。次の記号のいずれかです。

_<     >     >|     <<     >>     <&     >&     <<-     <>
_

これらにより、コマンドの入力と出力を制御できます。それらは、単純なコマンド内のどこにでも表示でき、コマンドの後に続く場合もあります。リダイレクションは、左から右に表示される順序で処理されます。

  • _<_:コマンドに入力を与えます。

    _command < file.txt
    _

    上記は_file.txt_の内容に対してcommandを実行します。

  • _<>_:上記と同じですが、ファイルはread-onlyではなくread + writeモードで開かれます。

    _command <> file.txt
    _

    ファイルが存在しない場合は作成されます。

    コマンドは通常、標準入力からreadのみであるため、ほとんど使用されません。ただし、 特定の状況で便利です です。

  • _>_:コマンドの出力をファイルに送信します。

    _command > out.txt
    _

    上記は、commandの出力を_out.txt_として保存します。ファイルが存在する場合はその内容が上書きされ、存在しない場合は作成されます。

    この演算子は、何かを 標準エラー または 標準出力 に出力するかどうかを選択するためにもよく使用されます。

    _command >out.txt 2>error.txt
    _

    上記の例では、_>_は標準出力をリダイレクトし、_2>_は標準エラーをリダイレクトします。出力は_1>_を使用してリダイレクトすることもできますが、これはデフォルトなので、_1_は通常省略され、単に_>_と記述されます。

    したがって、commandを_file.txt_で実行し、その出力を_out.txt_に保存し、エラーメッセージを_error.txt_に保存するには、次のように実行します。

    _command < file.txt > out.txt 2> error.txt
    _
  • _>|_:_>_と同じように動作しますが、シェルが上書きを拒否するように構成されている場合でも、ターゲットを上書きします(_set -C_または_set -o noclobber_を使用)。

    _command >| out.txt
    _

    _out.txt_が存在する場合、commandの出力がその内容を置き換えます。存在しない場合は作成されます。

  • _>>_:ターゲットファイルが存在する場合に新しいデータが追加されることを除いて、_>_と同じです。

    _command >> out.txt
    _

    _out.txt_が存在する場合、commandの出力は、すでにそこにあるものの後に追加されます。存在しない場合は作成されます。

  • _&>_、_>&_、_>>&_および_&>>_:(非標準)。標準エラーと標準出力の両方をリダイレクトし、それぞれ置換または追加します。

    _command &> out.txt
    _

    commandの標準エラーと標準出力の両方が_out.txt_に保存され、その内容が上書きされるか、存在しない場合は作成されます。

    _command &>> out.txt
    _

    上記のように、_out.txt_が存在する場合を除いて、commandの出力とエラーがそれに追加されます。

    _&>_バリアントはbashに由来し、_>&_バリアントはcsh(数十年前)に由来します。これらは両方とも他のPOSIXシェルオペレーターと競合するため、移植可能なshスクリプトでは使用しないでください。

  • _<<_:ヒアドキュメント。複数行の文字列を印刷するためによく使用されます。

    _ command << Word
         Text
     Word
    _

    ここで、commandは、上記の例でWordTextが次に出現するまで、すべてを入力として使用します。 Wordは、多くの場合EoFまたはそのバリエーションですが、任意の英数字の文字列(だけでなく)にすることができます。 Wordが引用符で囲まれている場合、ヒアドキュメントのテキストは文字どおりに扱われ、展開は実行されません(変数など)。引用符で囲まれていない場合、変数は展開されます。詳細については、 bashマニュアル を参照してください。

    _command << Word ... Word_の出力を別のコマンドに直接パイプしたい場合は、_<< Word_と同じ行にパイプを配置する必要があります。終了するWordの後や、行フォロー。例えば:

    _ command << Word | command2 | command3...
         Text
     Word
    _
  • _<<<_:ここの文字列。ここのドキュメントに似ていますが、1行を対象としています。これらは、Unixポートまたはrc(元の場所)、zsh、ksh、yash、bashの一部の実装にのみ存在します。

    _command <<< Word
    _

    Wordとして指定されたものはすべて展開され、その値は入力としてcommandに渡されます。これは、変数の内容をコマンドへの入力として渡すためによく使用されます。例えば:

    _ $ foo="bar"
     $ sed 's/a/A/' <<< "$foo"
     bAr
     # as a short-cut for the standard:
     $ printf '%s\n' "$foo" | sed 's/a/A/'
     bAr
     # or
     sed 's/a/A/' << EOF
     $foo
     EOF
    _

他のいくつかの演算子(_>&-_、_x>&y_ _x<&y_)を使用して、ファイル記述子をクローズまたは複製できます。それらの詳細については、シェルのマニュアルの関連セクションを参照してください( ここでは たとえばbashの場合)。

これは、Bourneのようなシェルの最も一般的な演算子のみをカバーしています。一部のシェルには、独自のリダイレクト演算子がいくつかあります。

Ksh、bash、およびzshには、<(…)>(…)および=(…)(後者はzshにのみ存在する)構文もあります。これらはリダイレクトではありませんが、 プロセス置換 です。

381
terdon

「>」に関する警告

I/Oリダイレクト(<および>)について学んだばかりのUnix初心者は、しばしば次のようなことを試します

コマンド … 入力ファイル > the_same_file

または

コマンド …< ファイル     > the_same_file

または、ほぼ同等に、

ネコ ファイル | コマンド …> the_same_file

grepsedcutsortspellは、このような構成で人々が使用したくなるコマンドの例です。)これらのシナリオの結果、ファイルが空になることにユーザーは驚いています。

他の回答で言及されていないように見えるニュアンスは、 bash(1)Redirectionセクションの最初の文に潜んでいます:

コマンドが実行される前に、シェルによって解釈される特別な表記法を使用して、その入力と出力を redirected することができます。

最初の5つの単語は、太字、斜体、下線付き、拡大、点滅、赤く着色され、 exclamation mark in red triangle アイコン。コマンドが実行される前に、シェルが要求されたリダイレクトを実行することを強調します。そしてまた覚えて

出力のリダイレクトにより、ファイル…が書き込み用に開かれます…。ファイルが存在しない場合は作成されます。存在する場合は、サイズがゼロに切り捨てられます。

  1. したがって、この例では:

    sort roster > roster
    

    シェルは、rosterプログラムが実行を開始する前に、sortファイルを書き込み用に開き、切り捨てます(つまり、その内容をすべて破棄します)。当然、データを回復するためにできることは何もありません。

  2. ナイーブにそれを期待するかもしれません

    tr "[:upper:]" "[:lower:]" < poem > poem
    

    良いかもしれません。シェルは左から右へのリダイレクトを処理するため、読み取り(poemの標準入力の場合)のためにtrを開いてから、書き込み(標準​​出力の場合)のために開きます。しかし、それは役に立ちません。この一連の操作により2つのファイルハンドルが生成されますが、どちらも同じファイルを指します。シェルが読み取りのためにファイルを開いたとき、コンテンツはまだそこにありますが、プログラムが実行される前にファイルが破壊されます。

それで、それについて何をすべきか?

ソリューションは次のとおりです。

  • 実行しているプログラムに、出力先を指定する独自の内部機能があるかどうかを確認します。多くの場合、これは-o(または--output=)トークンで示されます。特に、

    sort roster -o roster
    

    にほぼ等しい

    sort roster > roster
    

    ただし、最初のケースでは、sortプログラムが出力ファイルを開きます。そして、 after がすべての入力ファイルを読み取るまで、出力ファイルを開かないように賢くなっています。

    同様に、少なくとも一部のバージョンのsedには-i(編集 n place)出力を入力ファイルに書き戻すために使用できるオプション(ここでも、 after すべての入力が読み込まれた)。 ed/exemacspicovi/vimなどのエディターを使用すると、ユーザーはテキストファイルを編集し、編集したテキストを元のファイルに保存できます。 edは(少なくとも)非対話的に使用できることに注意してください。

    • viには関連機能があります。 :%!commandと入力した場合Enter、編集バッファーの内容をcommandに書き込み、出力を読み取り、バッファーに挿入します(元の内容を置き換えます)。
  • シンプルだが効果的:

    コマンド … 入力ファイル > temp_file  && mv temp_file入力ファイル

    これには、input_fileがリンクの場合、(おそらく)別のファイルに置き換えられるという欠点があります。また、新しいファイルはデフォルトの保護であなたが所有します。特に、元のinput_fileがそうでなかったとしても、ファイルが最終的に誰でも読み取り可能になるというリスクがあります。

    バリエーション:

    • commandinput_file > temp_file && cp temp_fileinput_file && rm temp_file
      それでも、temp_fileは世界中で読み取り可能です。さらに良い:
    • cp input_filetemp_file && commandtemp_file > input_file && rm temp_file
      これらは、ファイルのリンクステータス、所有者、およびモード(保護)を保持しますが、I/Oが2倍になる可能性があります。 (cp-a-pなどのオプションを使用して、属性を保持するように指示する必要がある場合があります。)
    • commandinput_file > temp_file &&
      cp --attributes-only --preserve=all input_filetemp_file &&
      mv temp_fileinput_file
      (読みやすくするために別々の行に分割)これにより、ファイルのモードは保持されます(ルートである場合は所有者)。ただし、ファイルはあなたが所有するようになります(ルートでない場合)。新しい個別のファイルにします。
  • このブログ (ファイルの「インプレース」編集)の提案と説明

    {rm 入力ファイル  && コマンド …> 入力ファイル; } < 入力ファイル

    これには、commandが標準入力を処理できる必要があります(ただし、ほとんどすべてのフィルターで可能です)。ブログ自体はこれを危険なクラッジと呼んでおり、その使用を推奨していません。また、これにより、ユーザーが所有し、デフォルトの権限が付与された新しいファイル(何にもリンクされていない)も作成されます。

  • Moreutilsパッケージには、spongeというコマンドがあります。

    コマンド … 入力ファイル |スポンジ the_same_file

    詳細は this answer を参照してください。

これは私にとってまったくの驚きでした: syntaxerrorによると

[これらのソリューションのほとんど]は、読み取り専用ファイルシステムでは失敗します。「読み取り専用」とは、$HOME /は書き込み可能ですが、/tmp読み取り専用(デフォルト)になります。たとえば、Ubuntuを使用していて、回復コンソールを起動した場合、これは一般的なケースです。また、ヒアドキュメント演算子<<<も一時ファイルをそこに書き込むため、/tmpread/writeにする必要があるため、そこでも機能しません。
(cf。 この質問 にはstrace ’dの出力が含まれます)

その場合、以下が機能する可能性があります。

  • 上級ユーザーのみ]コマンドが入力と同じ量の出力データを生成することが保証されている場合(例:sort、またはtr without the -dまたは-sオプション)、お試しいただけます
    コマンド … 入力ファイル | dd of =the_same_file conv = notrunc
    上記の説明を含む詳細、およびコマンドが同じ量を生成することが保証されている場合に機能する代替手段については、 この答え および この答え を参照してください入力があるときの出力データの数またはless (たとえば、grep、またはcut)。これらの回答には、空き容量が必要ない(または必要な容量が少ない)という利点があります。上記のcommandinput_file > temp_file && …形式の回答では、入力(古い)ファイル全体と出力(新しい)ファイル全体を同時に保持できる十分な空き領域がシステムに必要であることは明らかです。これは、他のほとんどのソリューション(sed -ispongeなど)でも明らかにそうではありません。例外:sortは、出力を書き込む前にすべての入力を読み取る必要があるため、sort … | dd …はおそらく多くの空き領域を必要とし、一時ファイル内のすべてのデータではないにしても、ほとんどのバッファリングを行います。
  • 上級ユーザーのみ:
    コマンド … 入力ファイル 1 <> the_same_file
    上記のddの回答と同じです。 n<>file構文は、指定されたファイルをファイル記述子nに対して、切り捨てることなく、入力と出力の両方で開きます –ソートn<n>の組み合わせ。注:一部のプログラム(catgrepなど)は、入力と出力が同じファイルであることを検出できるため、このシナリオでは実行を拒否する場合があります。上記のディスカッションについては this answer を参照してください。また、コマンドが入力と同じ量の出力データを生成することが保証されている場合にこの回答を機能させるスクリプト以下
    警告:ピーターのスクリプトはテストしていないため、保証しません。

それで、質問は何でしたか?

これはU&Lで人気のトピックです。次の質問で取り上げます。

…そして、それはスーパーユーザーやAsk Ubuntuをカウントしていません。上記の質問に対する回答の多くの情報をこの回答に組み込みましたが、すべてではありません。 (詳細については、上記の質問とその回答をご覧ください。)

追伸私は、上記で引用したブログとnoの提携を結んでいます。

64
Scott

;&()に関するその他の観測

  • Terdonの回答の一部のコマンドはnullである可能性があることに注意してください。たとえば、あなたは言うことができます

    command1 ;
    

    command2なし)。これは

    command1
    

    (つまり、フォアグラウンドでcommand1を実行し、それが完了するのを待ちます。同等に、

    command1 &
    

    command2なし)はcommand1をバックグラウンドで起動し、すぐに別のシェルプロンプトを発行します。

  • 対照的に、command1 &&command1 ||command1 |は意味がありません。これらの1つを入力すると、シェルは(おそらく)コマンドが別の行に続くと想定します。通常は>に設定されているセカンダリ(継続)シェルプロンプトが表示され、読み続けます。シェルスクリプトでは、次の行を読み取り、すでに読み取ったものに追加します。 (注意:これはあなたが起こしたいことではないかもしれません。)

    注:一部のシェルの一部のバージョンでは、このような不完全なコマンドをエラーとして扱う場合があります。このような場合(または、実際には、コマンドが長いanyの場合)、バックスラッシュ(\)を行の最後に置いて、シェルに別の行でコマンドを読み続けます:

    command1  &&  \
    command2
    

    または

    find starting-directory -mindepth 3 -maxdepth 5 -iname "*.some_extension" -type f \
                            -newer some_existing_file -user fred -readable -print
    
  • Terdonが言うように、(および)を使用してコマンドをグループ化できます。それらがその議論に「実際には関連していない」という陳述は議論の余地があります。 terdonの回答に含まれるコマンドの一部は、コマンドgroupsの場合があります。例えば、

    ( command1 ; command2 )  &&  ( command3; command4 )
    

    これを行います:

    • command1を実行し、終了するまで待ちます。
    • 次に、その最初のコマンドの実行結果に関係なく、command2を実行して、コマンドが完了するまで待ちます。
    • 次に、command2が成功した場合、

      • command3を実行し、終了するまで待ちます。
      • 次に、そのコマンドの実行結果に関係なく、command4を実行して、コマンドが完了するまで待ちます。

      command2が失敗した場合は、コマンドラインの処理を停止します。

  • 括弧の外では、|は非常に強く結合するため、

    command1 | command2 || command3
    

    に相当

    ( command1 | command2 )  ||  command3
    

    および&&および||;よりも緊密にバインドするため、

    command1 && command2 ; command3
    

    に相当

    ( command1 && command2 ) ;  command3
    

    つまり、command3は、command1command2の終了ステータスに関係なく実行されます。