違いは何だ<<
、<<<
と< <
bashのでは?
違いは何だ<<
、<<<
と< <
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
そして、(出力が読みやすくするために短縮システムコールのトレースを介して、一時ファイルがFD 3、そこに書き込まれたデータは、それがで再開放されるように開放されている様子がわかりO_RDONLY
、その後、FD 4としてフラグ以降リンク解除dup2()
によって継承されたFD 0に、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 +++
意見:ここでの文字列は一時テキストファイルを使用するため、POSIX定義によるテキストファイルは改行文字で終わる行を持たなければならないため、ヒア文字列が常に末尾の改行を挿入する理由が考えられます。
プロセス置換
以下のようtldp.orgは説明して、
プロセス置換は、1つまたは複数のプロセスの出力を別のプロセスの標準入力に送ります。
それで、実際にこれは1つのコマンドのstdoutを他に配管するのと同様echo foobar barfoo | wc
です、例えば。ただし、bashのマンページでは、と表示されていることに注意してください<(list)
。したがって、基本的には複数の(!)コマンドの出力をリダイレクトできます。
注:技術的に< <
は、1つのことではなく、<
からの出力のシングルリダイレクトとプロセスリダイレクトの2つのリダイレクトを指していると言います <( . . .)
。
置換を処理するだけの場合はどうなりますか?
$ echo <(echo bar)
/dev/fd/63
ご覧のとおり、シェルは/dev/fd/63
出力が行く一時ファイル記述子を作成します(Gillesの答えによれば、これは匿名パイプです)。つまり <
、そのファイル記述子をコマンドへの入力としてリダイレクトします。
したがって、非常に単純な例では、2つのエコーコマンドからの出力をwcにプロセス置換します。
$ wc < <(echo bar;echo foo)
2 2 8
そのため、ここでは、シェルで括弧内で発生するすべての出力用のファイル記述子を作成し、それを入力としてリダイレクトします。wc
予想どおり、wcは2つのエコーコマンドからそのストリームを受け取ります。適切には、2単語、2行、6文字に加えて2つの改行がカウントされます。
サイドノート:プロセス置換は、bashism(のような高度なシェルで使用可能なコマンドまたは構造ですbash
が、POSIXでは指定されていません)と呼ばれる場合がありますがksh
、kshのマニュアルページおよびこの回答が示唆するように、bashが存在する前に実装されていました。以下のようなシェルtcsh
とmksh
しかし、プロセス置換を持っていません。では、プロセスを置換せずに、複数のコマンドの出力を別のコマンドにリダイレクトするにはどうすればよいでしょうか?グループ化とパイピング!
$ (echo foo;echo bar) | wc
2 2 8
事実上、これは上記の例と同じですがwc
、パイプとリンクされたサブシェル全体とstdinのstdoutを作成するため、これはプロセス置換とはまったく異なります。一方、プロセス置換は、コマンドに一時ファイル記述子を読み取らせます。
パイプでグループ化できるのに、なぜプロセス置換が必要なのですか?時には配管が使えないからです。以下の例を検討してください-2つのコマンドの出力を比較しますdiff
(2つのファイルが必要です。この場合、2つのファイル記述子を与えています)
diff <(ls /bin) <(ls /usr/bin)
< <
プロセス置換からstdinを得ているときに使用されます。このようなコマンドは次のようになりますcmd1 < <(cmd2)
。たとえば、wc < <(date)
< <
それ自体は物ではなく、プロセス置換の場合は、<
その後に続く何かが偶然に続くだけです<
<<<
は、最初にPlan 9 rcシェルのUnixポートによって実装され、その後zsh、bash、およびksh93によって採用されました。私はそれをバシズムとは呼びません。
echo 'foo' | read; echo ${REPLY}
うではない返すfoo
ので、read
サブシェルで開始された-配管は、サブシェルを起動します。ただし、read < <(echo 'foo'); echo ${REPLY}
サブシェルfoo
がないため、は正しく返されます。
< <
構文エラーです:
$ cat < <
bash: syntax error near unexpected token `<'
< <()
あるプロセス置換(<()
リダイレクションと組み合わせる)は、( <
):
不自然な例:
$ wc -l < <(grep ntfs /etc/fstab)
4
$ wc -l <(grep ntfs /etc/fstab)
4 /dev/fd/63
プロセス置換では、ファイル記述子へのパスがファイル名のように使用されます。ファイル名を直接使用したくない(または使用できない)場合は、プロセス置換とリダイレクトを組み合わせます。
明確にするために、< <
演算子はありません。
<()
はファイル名のようなものを与えるので、より一般的には便利です- < <()
必要でないかもしれないstdinを置き換えることです。ではwc
、後者の方がより便利です。これは、他の場所ではあまり有用であるかもしれない
< <
構文エラーが発生します。適切な使用は次のとおりです。
例の助けを借りて説明する:
例< <()
:
while read line;do
echo $line
done< <(ls)
上記の例では、whileループへの入力はls
コマンドからecho
取得されます。このコマンドは、1行ずつ読み取り、ループ内で編集できます。
<()
プロセス置換に使用されます。詳細と例について<()
は、次のリンクをご覧ください。