プログラムに複数行の入力を入力するためのスクリプトを書く必要がありました(psql
)。
ちょっとグーグルした後、私は以下の構文がうまくいくのを見つけました:
cat << EOF | psql ---params
BEGIN;
`pg_dump ----something`
update table .... statement ...;
END;
EOF
これは複数行の文字列(BEGIN;
からEND;
までを含む)を正しく構成し、それを入力としてpsql
へのパイプとして渡します。
しかし、私はそれがどのように/なぜ動くのかわかりません、何人か説明してもらえますか?
主にcat << EOF
を参照しています。>
はファイルに出力し、>>
はファイルに追加し、<
はファイルから入力を読み取ります。
<<
は正確に何をしますか?
それにmanページはありますか?
これはheredocformatと呼ばれ、標準入力に文字列を渡します。詳細は https://en.wikipedia.org/wiki/Here_document#Unix_shells を参照してください。
man bash
から:
こちらのドキュメント
このタイプのリダイレクトは、(末尾の空白がない)Wordのみを含む行が表示されるまで、現在のソースから入力を読み取るようにシェルに指示します。
その時点までに読み取られたすべての行が、コマンドの標準入力として使用されます。
ヒアドキュメントの形式は次のとおりです。
<<[-]Word here-document delimiter
Wordでは、パラメーター展開、コマンド置換、算術展開、またはパス名展開は行われません。 Word内の文字が引用符で囲まれている場合、delimiterはWordの引用符を削除した結果であり、here-document内の行は展開されません。 Wordが引用符で囲まれていない場合、here-documentのすべての行がパラメータ展開、コマンド置換、および算術展開の対象になります。後者の場合、文字シーケンス
\<newline>
は無視され、\
を使用して、文字\
、$
、および`
を引用符で囲む必要があります。リダイレクト演算子が
<<-
の場合、すべての先行タブ文字は入力行およびdelimiterを含む行から削除されます。これにより、シェルスクリプト内のヒアドキュメントを自然な形でインデントすることができます。
cat <<EOF
構文は、Bashで複数行のテキストを扱うときにとても便利です。複数行の文字列をシェル変数、ファイル、またはパイプに割り当てるとき。
cat <<EOF
構文の使用例$ sql=$(cat <<EOF
SELECT foo, bar FROM db
WHERE foo='baz'
EOF
)
$sql
変数は改行文字も保持するようになりました。 echo -e "$sql"
で確認できます。
$ cat <<EOF > print.sh
#!/bin/bash
echo \$PWD
echo $PWD
EOF
print.sh
ファイルには、次の内容が含まれています。
#!/bin/bash
echo $PWD
echo /home/user
$ cat <<EOF | grep 'b' | tee b.txt
foo
bar
baz
EOF
b.txt
ファイルにはbar
行とbaz
行が含まれています。同じ出力がstdout
に出力されます。
あなたの場合、 "EOF"は "Here Tag"として知られています。基本的に<<Here
は、 "tag" Here
までは複数行の文字列を入力することをシェルに指示します。このタグには好きな名前を付けることができます。通常はEOF
またはSTOP
です。
Hereタグに関するいくつかの規則:
例:
$ 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
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
ここでのドキュメントは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
これは必ずしも元の質問に対する答えではありませんが、私自身のテストによる結果の共有です。この:
<<test > print.sh
#!/bin/bash
echo \$PWD
echo $PWD
test
以下と同じファイルを生成します。
cat <<test > print.sh
#!/bin/bash
echo \$PWD
echo $PWD
test
だから、私はcatコマンドを使う意味がわかりません。