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

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

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

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

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

command1  > file1
command1  >> file1

これらは何でしょう? 何と呼ばれていますか? 何をするのですか? 他にもあるのですか?


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

答え1

これらはシェル演算子と呼ばれ、他にもたくさんあります。2つの主要なクラスの中で最も一般的なものについて簡単に説明します。制御演算子そしてリダイレクト演算子、およびそれらが bash シェルに対してどのように機能するかについて説明します。

A. 制御演算子

POSIX定義

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

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

そして|&bashで。

A!ない制御演算子ではなく予約語内部では論理否定演算子になります算術式およびテスト構造内でも使用できます (ただし、スペース区切り文字は必要です)。

A.1 リスト終端子

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

      command1 ; command2
    

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

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

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

       command1 & command2
    

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

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

A.2 論理演算子

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

       command1 && command2
    

ここでは、終了command2後に実行され、command1のみ成功した場合command1(終了コードが 0 の場合)。両方のコマンドはフォアグラウンドで実行されます。

このコマンドは次のようにも書けます

    if command1
    then command2
    else false
    fi

if command1; then command2; fiまたは、戻りステータスが無視される場合も同様です。

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

       command1 || command2
    

ここでは、失敗したcommand2場合(0 以外の終了ステータスを返した場合)のみ実行されますcommand1。両方のコマンドはフォアグラウンドで実行されます。

このコマンドは次のようにも書けます

    if command1
    then true
    else command2
    fi

またはもっと短く言うとif ! command1; then command2; fi

&&と は||左結合であることに注意してください。シェルの論理演算子 &&、|| の優先順位詳細については。

  • !: これは「not」演算子として機能する予約語です (ただし区切り文字が必要です)。コマンドの戻りステータスを否定するために使用されます。コマンドが 0 以外のステータスを返す場合は 0 を返し、ステータス 0 を返す場合は 1 を返します。ユーティリティの論理 NOT でもありますtest

      ! command1
    
      [ ! a = a ]
    

算術式内の真の NOT 演算子:

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

A.3 パイプ演算子

  • |: パイプ演算子は、あるコマンドの出力を別のコマンドの入力として渡します。パイプ演算子から構築されたコマンドは、パイプライン

       command1 | command2
    

    によって印刷される出力はすべてcommand1への入力として渡されますcommand2

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

      command1 |& command2
    

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

;;単に終了を示すために使用されるケースステートメントKsh、bash、zsh では、;&次のケースにフォールスルーすることもサポートされており、;;&(ATT ksh ではサポートされていませんが) 後続のケースをテストすることもできます。

(そして)慣れているグループコマンドサブシェルで起動します。{また、}グループコマンドも実行しますが、サブシェルでは起動しません。この答えシェル構文におけるさまざまな種類の括弧、角括弧、中括弧の説明。

B. リダイレクト演算子

リダイレクト演算子のPOSIX定義

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

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

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

  • <: コマンドに入力します。

      command < file.txt
    

上記はcommandの内容に対して実行されますfile.txt

  • <>: 上記と同じですが、ファイルは読み取り+書き込みモードの代わりに読み取り専用:

      command <> file.txt
    

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

この演算子は、コマンドが通常読むただし、stdinからはそれはいくつかの特定の状況で役に立つ可能性がある

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

      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 -Cset -o noclobber

      command >| out.txt
    

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

  • >>: と同じですが>、対象ファイルが存在する場合は新しいデータが追加されます。

      command >> out.txt
    

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

  • >&: (POSIX仕様による) で囲まれた場合数字1>&2)または-右側(1>&-)はリダイレクトのみを行います1つファイル記述子を削除するか、閉じます ( >&-)。

の後>&にファイル記述子番号を続けると、ファイル記述子をリダイレクトする移植可能な方法となり、>&-ファイル記述子を閉じる移植可能な方法となります。

このリダイレクトの右側がファイルである場合は、次のエントリを読んでください。

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

      command &> out.txt
    

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

    command &>> out.txt
 

上記と同じですが、out.txt存在する場合は、 の出力とエラーがcommandそれに追加されます。

バリアント&>は に由来bashし、バリアントは csh (数十年前) に由来します。どちらも他の POSIX シェル演算子と競合するため、移植可能なスクリプト>&では使用しないでください。sh

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

       command << WORD
           Text
       WORD
    

    ここで、 は、上記の例で が次に出現するまでのすべてを入力 として受け取ります。 はまたはcommandそのバリエーションであることが多いですが、任意の英数字(および英数字だけではない)文字列にすることができます。 のいずれかの部分が引用符で囲まれているかエスケープされている場合、ヒアドキュメント内のテキストは文字どおりに扱われ、展開は実行されません(たとえば変数の場合)。 が引用符で囲まれていない場合は、変数が展開されます。詳細については、WORDTextWORDEoFWORDbash マニュアル

    の出力を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プロセス置換

答え2

'>' に関する警告

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

指示入力ファイル>同じファイル

または

指示…<ファイル     >同じファイル

あるいは、ほぼ同等に、

ファイル|指示… >同じファイル

( grep、、、、およびは、このような構造でユーザーが使用したくなるコマンドの例です。) ユーザーは、これらのシナリオでファイルsedが空になることに驚きます。cutsortspell

他の回答では触れられていないニュアンスが、この質問の最初の文に潜んでいる。リダイレクションセクションバッシュ(1):

コマンドが実行される前に、その入力と出力はリダイレクト シェルによって解釈される特殊な表記法を使用します。

最初の5つの単語は、太字、斜体、下線、拡大、点滅、赤色、アイコンでマークされ赤い三角の中に感嘆符、シェルが要求されたリダイレクトを実行していることを強調する 必要があります。コマンドが実行される前にそして覚えておいてください

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

  1. したがって、この例では次のようになります。

    sort roster > roster
    

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

  2. 素朴に期待する人もいるかもしれない

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

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

それで、どうすればいいのでしょうか?

ソリューションには以下が含まれます:

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

    sort -o roster roster
    

    は、ほぼ

    sort roster > roster
    

    ただし、最初のケースでは、sortプログラムは出力ファイルを開きます。そして、出力ファイルが開かれるまでは、すべての入力ファイルを読み取りました。

    同様に、少なくともいくつかのバージョンのにsed-i(編集n桁のオプションを使用して、出力を入力ファイルに書き戻すことができます(繰り返しますが、ed/ ex、、、および/などemacsのエディタを使用すると、 ユーザーはテキストファイルを編集し、編集したテキストを元のファイルに保存できます。(少なくとも) は非対話的に使用できることに注意してください。picovivimed

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

    指示入力ファイル>一時ファイル  && 動画一時ファイル 入力ファイル

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

    バリエーション:

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

    { rm入力ファイル  &&  指示… >入力ファイル; } <入力ファイル

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

  • moreutils パッケージには、次のコマンドがありますsponge:

    指示入力ファイル| スポンジ同じファイル

    見るこの答え詳細については。

私にとってまったく驚きだったことが一つあります。 構文エラーは言う:

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

その場合は、次の方法が効果的かもしれません:

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

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

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

…そして、Super User や Ask Ubuntu は含まれていません。上記の質問に対する回答の多くの情報をこの回答に取り入れていますが、すべてではありません。(つまり、詳細については、上記の質問とその回答を読んでください。)

追伸:私はいいえ上記で引用したブログとの提携。

答え3

;、、&および(に関するさらなる観察)

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

    command1 ;
    

    ( はなしcommand2)。これは、

    command1
    

    (つまり、command1フォアグラウンドで実行され、完了するまで待機するだけです。比較すると、

    command1 &
    

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

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

    注意:一部のシェルのバージョンでは、このような不完全なコマンドをエラーとして扱う場合があります。そのような場合(または実際には、どれでも長いコマンドがある場合など、\行末にバックスラッシュ ( ) を付けて、シェルに次の行のコマンドの読み取りを続行するように指示することができます。

    command1  &&  \
    command2
    

    または

    find starting-directory -mindepth 3 -maxdepth 5 -iname "*.some_extension" -type f \
                            -newer some_existing_file -user fred -readable -print
    
  • terdonが言うように、(コマンド)をグループ化するために使用できます。それらが「その議論にはあまり関係がない」という発言は議論の余地があります。terdonの回答にあるコマンドのいくつかは、コマンドである可能性があります。グループ。 例えば、

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

    これは次のようになります:

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

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

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

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

    command1 | command2 || command3
    

    は以下と同等である

    ( command1 | command2 )  ||  command3
    

    そして、&&そしては||よりも強く結合する;ので、

    command1 && command2 ; command3
    

    は以下と同等である

    ( command1 && command2 ) ;  command3
    

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

関連情報