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


245

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

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

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

command1  > file1
command1  >> file1

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


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

回答:


340

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

A.制御演算子

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

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

そして|&バッシュ。

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

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

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

    command1 ; command2

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

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

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

     command1 & command2

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

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

A.2論理演算子

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

     command1 && command2

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

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

    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を返し、ステータス0を返す場合は1を返します。また、testユーティリティの論理NOT です。

    ! command1
    
    [ ! a = a ]

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

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

A.3パイプオペレーター

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

     command1 | command2

    によって出力される出力command1は、入力としてに渡されますcommand2

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

    command1 |& command2

A.4その他の句読点

;;case文の終わりを示すためにのみ使用されます。Ksh、bash、およびzsh ;&は、次のケースに移行することもサポートします;;&(ATT kshではありません)。後続のケースに進み、テストします。

()に使用されているグループのコマンドとサブシェルでそれらを起動します。{そして}また、グループコマンドが、サブシェルでそれらを起動しません。シェル構文のさまざまなタイプの括弧、括弧、および括弧については、この回答を参照してください。

B.リダイレクト演算子

リダイレクト演算子

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

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

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

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

    command < file.txt

    上記はcommandのコンテンツに対して実行されfile.txtます。

  • <>:上記と同じですが、ファイルは読み取り専用ではなく読み取り+書き込みモードで開かれています

    command <> file.txt

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

    通常、コマンドは標準入力からしか読み取れないため、この演算子はほとんど使用されませんが、多くの特定の状況では便利です。

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

    command > out.txt

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

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

    command >out.txt 2>error.txt

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

    したがって、実行commandfile.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は、上記の例でWORD、の次の出現をText入力として検出するまですべてを取得します。一方でWORD、多くの場合であるEoFか、その変形、それはあなたが好きな任意の英数字(だけではなく)文字列を指定できます。WORDが引用されている場合、ヒアドキュメント内のテキストは文字どおりに処理され、展開は実行されません(たとえば、変数に対して)。引用符で囲まれていない場合、変数は展開されます。詳細については、bashのマニュアルを参照してください。

    の出力command << WORD ... WORDを別の1つまたは複数のコマンドに直接パイプする場合、パイプをと同じ行に<< WORD配置する必要があります。終端のWORDの後または後続の行にパイプを配置することはできません。例えば:

     command << WORD | command2 | command3...
         Text
     WORD
  • <<<:here文字列は、hereドキュメントに似ていますが、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
すべてのシェルが同等というわけではないことに注意し、bash固有の機能を明確に強調する価値があるでしょう。
グレッグヒューギル14年

1
@GregHewgillええ、私はに関して議論していると言って、それから抜け出しましたbash。これは、さまざまな「この奇妙なことは何をするのか」という質問を閉じるための正規のQ&Aとして整理されており、それらのほとんどはbashのユーザーからのものです。他の誰かがbash以外のシェルを提案して回答することを望んでいますが、bash固有のシェルを強調表示することは非常に理にかなっています。確認する必要がありますが、どれが私の頭上にあるのかわかりません。
テルドン

&>>>>、および<<<への参照であるとして、すべての非POSIXです-だけでなく、ここでは、ドキュメントの名前で非alphanum文字。この答えは、またについてほとんど議論どのように彼らは仕事-例えば、それについて話して無用のほぼ悪化しているシンプルなコマンドコマンドこれらが何であるかを説明し、どのようにシェルが決定せず。
mikeserv 14年

@mikeservありがとう。ただし、bashとzshで動作します。そのリストで本当にbash固有のものがあるのか​​どうかはわかりません。これを実行して、それぞれが機能するシェルを追加する必要がありますが、最初に調べる必要があります。
テルドン

1
@ Arc676いいえ、それらはtrueまたはfalseに評価されません。これは完全に異なるコンテキストです。これは、0以外の終了値は問題(ないfalse)を示し、0の終了コードは成功(ないtrue)を示すことを意味します。それは常に方法であり、非常に標準的です。0以外の終了コードは、私が知っているすべての環境でエラーを示します。
テルドン

60

「>」に関する警告

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

コマンドinput_file > the_same_file

または

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

または、ほぼ同等に、

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

grepsedcutsort、とspell人々はこのような構築物中で使用するように誘惑されているコマンドの例です。)ユーザーは、これらのシナリオが空になってきてファイルにつながることを発見して驚いています。

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

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

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

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

  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プログラムは出力ファイルを開きます。そして、それはまで出力ファイルを開くことがないように十分にスマートだ、それは、入力ファイル(複数可)のすべてを読みました。

    同様に、少なくともいくつかのバージョンがsed持っている-i(編集私は戻って、入力ファイルにアウト出力を書き込むために使用することができますn個置き)オプションを(再び、後にすべての入力が読み込まれています)。以下のようなエディタed/ exemacspico、およびvi/はvim 、ユーザーがテキストファイルを編集して、元のファイルに編集したテキストを保存することができます。ed(少なくとも)非対話的に使用できることに注意してください。

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

    コマンドinput_file > temp_file   && mv temp_file  input_file

    これに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か、-p上のcp 属性を保持するためにそれを伝えるために。)
    • commandinput_file > temp_file &&
      cp --attributes-only --preserve=all input_file temp_file &&
      mv temp_file input_file
      (読みやすくするために別々の行に分けています)これにより、ファイルのモード(および、rootの場合は所有者)は保持されますが、所有者(rootでない場合)になり、新しいファイルになります。別のファイル。
  • このブログ (ファイルの「インプレース」編集)は、提案と説明を行います

    {rm input_file   &&   command …> input_file ; } < input_file

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

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

    コマンドinput_file | スポンジthe_same_file

    詳細については、この回答を参照してください。

私にとって完全な驚きとして来たもの 次のとおりです。

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

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

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

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

これはU&Lで人気のあるトピックです。次の質問で対処します。

…そしてそれはスーパーユーザーやUbuntuを数えていません。上記の質問に対する回答の多くの情報をこの回答に取り入れましたが、すべてではありません。(つまり、詳細については、上記の質問とその回答をお読みください。)

PS私は、上で引用したブログと提携していません


この質問は次々と出てくるので、「標準的な答え」を書くことに手を出そうと思いました。ここに投稿する必要がありますか(また、他のよりトラフィックの多い質問からリンクする必要があります)、または実際にこの問題を提起する質問の1つに移動する必要がありますか?また、これはおそらく質問がマージされるべき状況ですか?
スコット

/ tmp 一時ファイルを作成する場所が必要なアプリケーションで使用できるディレクトリ。アプリケーションは、このディレクトリにファイルを作成することを許可されますが、アプリケーションの呼び出し間でそのようなファイルが保持されることを想定してはなりません。
mikeserv

@mikeserv:ええ、(1)構文エラーを引用しています。(2)びっくりしたと言いました。私は、何かが読み書き可能であれば、それはそうだと思った/tmp
スコット

さて、@ syntaxerrorが言ったことは二重に奇妙です。なぜなら、私が思うに、dashUbuntuのデフォルトの回復シェルであり、<<<ヒアストリングを理解しないだけでなく、ヒアドキュメントの匿名パイプを取得<<${TMPDIR:-/tmp}、そのために混乱しないからですまったく目的。参照してください。このまたはこれをここでは、文書の取り扱い上のデモのために。また、同じ量の出力または警告が少ないのなぜですか?
mikeserv

@mikeserv:まあ、dd … conv=notrunc1<>答えた出力ファイルを切り捨てることはありませんので、コマンドの出力は、入力(例えば、未満の場合grep)、ファイルの最後に残されたオリジナルのいくつかのバイトがあるでしょう。出力が入力よりも大きい場合、(例えば、cat -nnl、または(潜在的に)grep -n)、あなたはそれを読んだ前に、古いデータを上書きする危険があります。
スコット

29

もっと上の観測;&(および)

  • 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として言う、(および)groupコマンドを使用することができます。それらがその議論に「あまり関係ない」という声明は議論の余地があります。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


パーフェクト、+ 1!あまり詳しくはしたくないので、それらは関連性がないと言った。私は、さまざまなコマンドの最後にあるすべての奇妙な波線が何であるのか疑問に思っている初心者のための簡単なチートシートとして機能する答えを望んでいました。役に立たないことを意味するつもりはありませんでした。これをすべて追加してくれてありがとう。
テルドン

1
「クリティカルマス」問題が心配です。シェルについて言えることをすべて投稿すると、最終的にTL; DRバージョンのBashリファレンスマニュアルが作成されます。
Gマン14年

また、言及する価値があります:Cファミリーの言語とは異なり、;それ自体(またはそれに先行するコマンドなし)は構文エラーであり、空のステートメントではありません。したがって; ;、エラーです。(新規ユーザーのよくある落とし穴、IMHO)。また、:;;は、caseステートメント用の特別な区切り文字です。
ムル14年

1
@muru:良い点ですが、それを一般化しましょう。任意のコマンドの間に表示することができますコントロールオペレータの:;&&||&、そして|、彼らは彼らの前に何も表示された場合のエラーです。また、テルドンは;;(簡潔に)彼の答えで述べました。
Gマン14年

1
@Wildcard:OK、あなたがどこから来たのかわかりました。キーワードは「may」です。私が言っていたのは、すべてのシェルがそのような構成(YMMV)を受け入れることを保証しないということだけでした。明らかにlinebreak、POSIXシェルの文法でのトークンの使用について知る前に、それを書きました。したがって、おそらくすべてのPOSIX準拠のシェルがそれらを受け入れると言っても安全です。私は一般的な免責事項として声明を支持します。実際のBourneシェルなど、十分に古いPOSIX以前のシェルを見つけた場合、すべてのベットはオフになります。
Gマン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.