web-dev-qa-db-ja.com

bashの<<、<<、<<の違いは何ですか?

Bashの<<<<<、および< <の違いは何ですか?

93
Searene

ヒアドキュメント

<<here-document構造体として知られています。プログラムに終了テキストを知らせ、その区切り文字が表示されるたびに、プログラムは入力としてプログラムに与えたすべてのものを読み取り、タスクを実行します。

ここに私が意味するものがあります:

$ wc << EOF
> one two three
> four five
> EOF
 2  5 24

この例では、wcプログラムにEOF文字列を待機するように指示し、5つの単語を入力してから、EOFを入力して、入力が完了したことを通知します。実際には、wcを単独で実行し、単語を入力してから CtrlD

Bashでは、これらは通常/tmp/sh-thd.<random string>の形式で一時ファイルを介して実装されますが、ダッシュでは匿名パイプとして実装されます。これは、straceコマンドを使用してシステムコールをトレースすることで確認できます。 bashshに置き換えて、/bin/shがこのリダイレクトを実行する方法を確認します。

$ strace -e open,dup2,pipe,write -f bash -c 'cat <<EOF
> test
> EOF'

ここで文字列

<<<here-stringとして知られています。テキストを入力する代わりに、事前に作成したテキストの文字列をプログラムに渡します。たとえば、bcなどのプログラムを使用すると、bc <<< 5*4を実行して、特定の場合にのみ出力を取得できます。bcをインタラクティブに実行する必要はありません。

Bashのヒア文字列は、通常は後でリンク解除される/tmp/sh-thd.<random string>形式の一時ファイルを介して実装されるため、一時的にメモリ領域を占有しますが、/tmpディレクトリエントリのリストには表示されません。事実上、匿名ファイルとして存在します。これは、シェル自体によってファイル記述子を介して引き続き参照でき、そのファイル記述子はコマンドによって継承され、後でdup2()関数を介してファイル記述子0(stdin)に複製されます。これは次の方法で確認できます

$ ls -l /proc/self/fd/ <<< "TEST"
total 0
lr-x------ 1 user1 user1 64 Aug 20 13:43 0 -> /tmp/sh-thd.761Lj9 (deleted)
lrwx------ 1 user1 user1 64 Aug 20 13:43 1 -> /dev/pts/4
lrwx------ 1 user1 user1 64 Aug 20 13:43 2 -> /dev/pts/4
lr-x------ 1 user1 user1 64 Aug 20 13:43 3 -> /proc/10068/fd

また、syscallsをトレースする(出力を読みやすくするために短縮します。一時ファイルがfd 3として開かれ、データが書き込まれた後、fd 4以降のO_RDONLYフラグで再度開かれ、その後fd 0にdup2()が開かれます、これはcatによって継承されます):

$ strace -f -e open,read,write,dup2,unlink,execve bash -c 'cat <<< "TEST"'
execve("/bin/bash", ["bash", "-c", "cat <<< \"TEST\""], [/* 47 vars */]) = 0
...
strace: Process 10229 attached
[pid 10229] open("/tmp/sh-thd.uhpSrD", O_RDWR|O_CREAT|O_EXCL, 0600) = 3
[pid 10229] write(3, "TEST", 4)         = 4
[pid 10229] write(3, "\n", 1)           = 1
[pid 10229] open("/tmp/sh-thd.uhpSrD", O_RDONLY) = 4
[pid 10229] unlink("/tmp/sh-thd.uhpSrD") = 0
[pid 10229] dup2(4, 0)                  = 0
[pid 10229] execve("/bin/cat", ["cat"], [/* 47 vars */]) = 0
...
[pid 10229] read(0, "TEST\n", 131072)   = 5
[pid 10229] write(1, "TEST\n", 5TEST
)       = 5
[pid 10229] read(0, "", 131072)         = 0
[pid 10229] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=10229, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
+++ exited with 0 +++

意見:ここの文字列は一時的なテキストファイルを使用するため、潜在的には、here-stringsが常に末尾の改行を挿入するのは、 POSIX definition によるテキストファイルには改行文字で終わる行が必要なためです。

プロセス置換

tldp.org が説明しているように、

プロセス置換は、1つまたは複数のプロセスの出力を別のプロセスの標準入力に送ります。

したがって、実際には、これは、あるコマンドを別のコマンドにパイプする stdout に似ています。 echo foobar barfoo | wc。しかし、注意してください: bash manpage では、<(list)として示されていることがわかります。したがって、基本的には複数の(!)コマンドの出力をリダイレクトできます。

注:技術的に< <と言うときは、1つのものを参照するのではなく、単一の<を使用した2つのリダイレクトと<( . . .)からの出力のリダイレクトを処理します。

置換を処理するだけでどうなるのでしょうか?

$ echo <(echo bar)
/dev/fd/63

ご覧のように、シェルは一時ファイル記述子/dev/fd/63を作成します(出力はそこにあります(これは Gilles's answer によると、匿名パイプです)。つまり、<はコマンドへの入力としてそのファイル記述子をリダイレクトします。

したがって、非常に単純な例では、2つのエコーコマンドからの出力をwcにプロセス置換します。

$ wc < <(echo bar;echo foo)
      2       2       8

したがって、ここでは、シェルでかっこ内で発生するすべての出力のファイル記述子を作成し、それをwcへの入力としてリダイレクトします。予想どおり、wcは2つのechoコマンドからそのストリームを受け取ります。 、適切に2単語、2行、6文字と2つの改行がカウントされます。

サイド注:プロセス置換は、bashism(advancedで使用可能なコマンドまたは構造bashのようなシェルですが、POSIXでは指定されていません)、 ksh man page および this answer が示唆するように、bashが存在する前にkshに実装されました。ただし、tcshmkshなどのシェルにはプロセス置換がありません。では、プロセスを置換せずに、複数のコマンドの出力を別のコマンドにリダイレクトするにはどうすればよいでしょうか?グループ化とパイピング!

$ (echo foo;echo bar) | wc
      2       2       8

事実上、これは上記の例と同じですが、サブシェル全体の標準出力とwcパイプとリンク を作成するため、内部ではプロセス置換とは異なります。一方、プロセス置換は、コマンドに一時ファイル記述子を読み取らせます。

パイプでグループ化できるのに、なぜプロセス置換が必要なのですか?時には配管が使えないからです。以下の例を考えてください-2つのコマンドの出力をdiffと比較します(2つのファイルが必要です。この場合、2つのファイル記述子を与えています)

diff <(ls /bin) <(ls /usr/bin)
107

< <は構文エラーです:

$ cat < <
bash: syntax error near unexpected token `<'

< <()プロセス置換<())とリダイレクト(<)の組み合わせです:

不自然な例:

$ wc -l < <(grep ntfs /etc/fstab)
4
$ wc -l <(grep ntfs /etc/fstab)
4 /dev/fd/63

プロセス置換では、ファイル記述子へのパスがファイル名のように使用されます。ファイル名を直接使用したくない(または使用できない)場合は、プロセス置換とリダイレクトを組み合わせます。

明確にするために、< <演算子はありません。

23
muru

< <は構文エラーです。おそらくcommand1 < <( command2 )は、単純な入力リダイレクトとそれに続くプロセス置換であり、非常に似ていますが、同等ではありません。

command2 | command1

bashcommand1であると仮定した場合の違いは、2番目の場合はサブシェルで実行され、最初のシェルでは現在のシェルで実行されます。つまり、command1で設定された変数は、プロセス置換バリアントでは失われません。

11
jlliagre

< <は構文エラーを返します。適切な使用は次のとおりです。

例の助けを借りて説明する:

< <()の例:

while read line;do
   echo $line
done< <(ls)

上記の例では、whileループへの入力はlsコマンドから行われます。このコマンドは、ループで1行ずつ読み取り、echoedできます。

<()はプロセス置換に使用されます。 <()の詳細と例は、次のリンクで見つけることができます。

プロセス置換とパイプ

10
snoop