Bashの<<
、<<<
、および< <
の違いは何ですか?
ヒアドキュメント
<<
は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
コマンドを使用してシステムコールをトレースすることで確認できます。 bash
をsh
に置き換えて、/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
に実装されました。ただし、tcsh
やmksh
などのシェルにはプロセス置換がありません。では、プロセスを置換せずに、複数のコマンドの出力を別のコマンドにリダイレクトするにはどうすればよいでしょうか?グループ化とパイピング!
$ (echo foo;echo bar) | wc
2 2 8
事実上、これは上記の例と同じですが、サブシェル全体の標準出力とwc
パイプとリンク を作成するため、内部ではプロセス置換とは異なります。一方、プロセス置換は、コマンドに一時ファイル記述子を読み取らせます。
パイプでグループ化できるのに、なぜプロセス置換が必要なのですか?時には配管が使えないからです。以下の例を考えてください-2つのコマンドの出力をdiff
と比較します(2つのファイルが必要です。この場合、2つのファイル記述子を与えています)
diff <(ls /bin) <(ls /usr/bin)
< <
は構文エラーです:
$ cat < <
bash: syntax error near unexpected token `<'
< <()
は プロセス置換 (<()
)とリダイレクト(<
)の組み合わせです:
不自然な例:
$ wc -l < <(grep ntfs /etc/fstab)
4
$ wc -l <(grep ntfs /etc/fstab)
4 /dev/fd/63
プロセス置換では、ファイル記述子へのパスがファイル名のように使用されます。ファイル名を直接使用したくない(または使用できない)場合は、プロセス置換とリダイレクトを組み合わせます。
明確にするために、< <
演算子はありません。
< <
は構文エラーです。おそらくcommand1 < <( command2 )
は、単純な入力リダイレクトとそれに続くプロセス置換であり、非常に似ていますが、同等ではありません。
command2 | command1
bash
がcommand1
であると仮定した場合の違いは、2番目の場合はサブシェルで実行され、最初のシェルでは現在のシェルで実行されます。つまり、command1
で設定された変数は、プロセス置換バリアントでは失われません。
< <
は構文エラーを返します。適切な使用は次のとおりです。
例の助けを借りて説明する:
< <()
の例:
while read line;do
echo $line
done< <(ls)
上記の例では、whileループへの入力はls
コマンドから行われます。このコマンドは、ループで1行ずつ読み取り、echo
edできます。
<()
はプロセス置換に使用されます。 <()
の詳細と例は、次のリンクで見つけることができます。