web-dev-qa-db-ja.com

"cat << EOF"はbashでどのように機能しますか?

プログラムに複数行の入力を入力するためのスクリプトを書く必要がありました(psql)。

ちょっとグーグルした後、私は以下の構文がうまくいくのを見つけました:

cat << EOF | psql ---params
BEGIN;

`pg_dump ----something`

update table .... statement ...;

END;
EOF

これは複数行の文字列(BEGIN;からEND;までを含む)を正しく構成し、それを入力としてpsqlへのパイプとして渡します。

しかし、私はそれがどのように/なぜ動くのかわかりません、何人か説明してもらえますか?

主にcat << EOFを参照しています。>はファイルに出力し、>>はファイルに追加し、<はファイルから入力を読み取ります。

<<は正確に何をしますか?

それにmanページはありますか?

492
hasen

これはheredocformatと呼ばれ、標準入力に文字列を渡します。詳細は https://en.wikipedia.org/wiki/Here_document#Unix_shells を参照してください。


man bashから:

こちらのドキュメント

このタイプのリダイレクトは、(末尾の空白がない)Wordのみを含む行が表示されるまで、現在のソースから入力を読み取るようにシェルに指示します。

その時点までに読み取られたすべての行が、コマンドの標準入力として使用されます。

ヒアドキュメントの形式は次のとおりです。

          <<[-]Word
                  here-document
          delimiter

Wordでは、パラメーター展開、コマンド置換、算術展開、またはパス名展開は行われません。 Word内の文字が引用符で囲まれている場合、delimiterWordの引用符を削除した結果であり、here-document内の行は展開されません。 Wordが引用符で囲まれていない場合、here-documentのすべての行がパラメータ展開、コマンド置換、および算術展開の対象になります。後者の場合、文字シーケンス\<newline>は無視され、\を使用して、文字\$、および`を引用符で囲む必要があります。

リダイレクト演算子が<<-の場合、すべての先行タブ文字は入力行およびdelimiterを含む行から削除されます。これにより、シェルスクリプト内のヒアドキュメントを自然な形でインデントすることができます。

430
kennytm

cat <<EOF構文は、Bashで複数行のテキストを扱うときにとても便利です。複数行の文字列をシェル変数、ファイル、またはパイプに割り当てるとき。

Bashでのcat <<EOF構文の使用例

1.シェル変数に複数行の文字列を割り当てる

$ sql=$(cat <<EOF
SELECT foo, bar FROM db
WHERE foo='baz'
EOF
)

$sql変数は改行文字も保持するようになりました。 echo -e "$sql"で確認できます。

2. Bashでファイルに複数行の文字列を渡す

$ cat <<EOF > print.sh
#!/bin/bash
echo \$PWD
echo $PWD
EOF

print.shファイルには、次の内容が含まれています。

#!/bin/bash
echo $PWD
echo /home/user

3. Bashで複数行の文字列をパイプに渡す

$ cat <<EOF | grep 'b' | tee b.txt
foo
bar
baz
EOF

b.txtファイルにはbar行とbaz行が含まれています。同じ出力がstdoutに出力されます。

393
Vojtech Vitek

あなたの場合、 "EOF"は "Here Tag"として知られています。基本的に<<Hereは、 "tag" Hereまでは複数行の文字列を入力することをシェルに指示します。このタグには好きな名前を付けることができます。通常はEOFまたはSTOPです。

Hereタグに関するいくつかの規則:

  1. タグは大文字、小文字のいずれでもかまいませんが、慣例ではほとんどの場合大文字を使用します。
  2. その行に他の単語がある場合、タグはHereタグとは見なされません。この場合、単に文字列の一部と見なされます。タグと見なされるためには、タグは単独で別の行にある必要があります。
  3. タグと見なされるためには、そのタグの先頭または末尾にスペースを入れないでください。それ以外の場合は、文字列の一部と見なされます。

例:

$ cat >> test <<HERE
> Hello world HERE <-- Not by itself on a separate line -> not considered end of string
> This is a test
>  HERE <-- Leading space, so not considered end of string
> and a new line
> HERE <-- Now we have the end of the string
193
edelans

POSIX 7

kennytmはman bashを引用しましたが、そのほとんどはPOSIX 7でもあります: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_07_04

リダイレクト演算子 "<<"および "<<-"はどちらも、シェル入力ファイル( "here-document"と呼ばれる)に含まれる行をコマンドの入力にリダイレクトできます。

ヒアドキュメントは、次の文字列の後に始まり、区切り文字とaのみを含む行があり、その間に文字が入らないまで続く単一のWordとして扱われます。次に、次のヒアドキュメントがあれば、開始します。形式は次のとおりです。

[n]<<Word
    here-document
delimiter

ここで、オプションのnはファイル記述子番号を表します。番号を省略すると、ヒアドキュメントは標準入力(ファイル記述子0)を参照します。

Wordの文字が引用されている場合、区切り記号はWordで引用の削除を実行して形成され、ヒアドキュメントの行は展開されません。それ以外の場合、区切り文字はWord自体になります。

Word内の文字が引用されていない場合、ヒアドキュメントのすべての行は、パラメーター展開、コマンド置換、および算術展開のために展開されます。この場合、入力のinは二重引用符として動作します(二重引用符を参照)。ただし、二重引用符が "$()"、 "` `"、または "$ {}"内にある場合を除き、二重引用符( '"')はヒアドキュメント内で特別に扱われません。

リダイレクト記号が「<<-」の場合、すべての先行<tab>文字は、入力行と末尾の区切り文字を含む行から削除されます。 1行に複数の「<<」または「<<-」演算子が指定されている場合、最初の演算子に関連付けられたヒアドキュメントは、アプリケーションによって最初に提供され、シェルによって最初に読み取られます。

ヒアドキュメントが端末デバイスから読み取られ、シェルが対話型である場合、シェル変数で説明されているように処理される変数PS2の内容を、区切り文字が認識されるまで入力の各行を読み取る前に標準エラーに書き込みます。

まだ与えられていないいくつかの例。

引用符はパラメーターの展開を防ぎます

引用符なし:

a=0
cat <<EOF
$a
EOF

出力:

0

引用符付き:

a=0
cat <<'EOF'
$a
EOF

または(ugいが有効):

a=0
cat <<E"O"F
$a
EOF

出力:

$a

ハイフンは先頭のタブを削除します

ハイフンなし:

cat <<EOF
<tab>a
EOF

ここで、<tab>はリテラルタブであり、Ctrl + V <tab>で挿入できます

出力:

<tab>a

ハイフン付き:

cat <<-EOF
<tab>a
<tab>EOF

出力:

a

これはもちろん存在するので、あなたのcatを周囲のコードのようにインデントすることができます。例えば。:

if true; then
    cat <<-EOF
    a
    EOF
fi

残念ながら、これはスペース文字に対しては機能しません。ここではPOSIXがtabインデントを優先しました。いいね。

猫の代わりにティーを使う

元々の質問に対する答えとしてではありませんが、とにかくこれを共有したいと思いました。root権限が必要なディレクトリにconfigファイルを作成する必要がありました。

その場合、以下は機能しません。

$ Sudo cat <<EOF >/etc/somedir/foo.conf
# my config file
foo=bar
EOF

リダイレクトはSudoコンテキストの外側で処理されるためです。

私は代わりにこれを使ってしまいました:

$ Sudo tee <<EOF /etc/somedir/foo.conf >/dev/null
# my config file
foo=bar
EOF
19
Andreas Maier

ここでのドキュメントはbashループでも機能することに注意してください。この例は、テーブルの列リストを取得する方法を示しています。

export postgres_db_name='my_db'
export table_name='my_table_name'

# start copy 
while read -r c; do test -z "$c" || echo $table_name.$c , ; done < <(cat << EOF | psql -t -q -d $postgres_db_name -v table_name="${table_name:-}"
SELECT column_name
FROM information_schema.columns
WHERE 1=1
AND table_schema = 'public'
AND table_name   =:'table_name'  ;
EOF
)
# stop copy , now paste straight into the bash Shell ...

output: 
my_table_name.guid ,
my_table_name.id ,
my_table_name.level ,
my_table_name.seq ,

あるいは改行なしでも

while read -r c; do test -z "$c" || echo $table_name.$c , | Perl -ne 
's/\n//gm;print' ; done < <(cat << EOF | psql -t -q -d $postgres_db_name -v table_name="${table_name:-}"
 SELECT column_name
 FROM information_schema.columns
 WHERE 1=1
 AND table_schema = 'public'
 AND table_name   =:'table_name'  ;
 EOF
 )

 # output: daily_issues.guid ,daily_issues.id ,daily_issues.level ,daily_issues.seq ,daily_issues.prio ,daily_issues.weight ,daily_issues.status ,daily_issues.category ,daily_issues.name ,daily_issues.description ,daily_issues.type ,daily_issues.owner
0
Yordan Georgiev

これは必ずしも元の質問に対する答えではありませんが、私自身のテストによる結果の共有です。この:

<<test > print.sh
#!/bin/bash
echo \$PWD
echo $PWD
test

以下と同じファイルを生成します。

cat <<test > print.sh
#!/bin/bash
echo \$PWD
echo $PWD
test

だから、私はcatコマンドを使う意味がわかりません。

0
user9048395