bash에서“cat << EOF”는 어떻게 작동합니까?


629

프로그램에 여러 줄 입력을 입력하는 스크립트를 작성해야했습니다 ( psql).

약간의 인터넷 검색 결과 다음과 같은 구문이 작동합니다.

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

`pg_dump ----something`

update table .... statement ...;

END;
EOF

이렇게하면 여러 줄 문자열 (에서 BEGIN;까지 END;포함) 을 올바르게 구성하고 에 입력으로 파이프합니다 psql.

그러나 어떻게 / 왜 작동하는지 잘 모르겠습니다. 어떻게 설명 할 수 있습니까?

나는 주로 말하는 겁니다 cat << EOF, 내가 알고 >, 파일로 출력하는 >>파일에 추가, <파일에서 입력을 읽습니다.

<<정확히 무엇을 합니까?

그리고 그것에 대한 맨 페이지가 있습니까?


26
아마도 쓸모없는 사용입니다 cat. 시도 psql ... << EOF ... 또한 "여기에 문자열을"참조. mywiki.wooledge.org/BashGuide/InputAndOutput?#Here_Strings
추후 공지가있을 때까지 일시 중지되었습니다.

1
나는 그것이 고양이와 함께 작동하지만 메아리와 함께 작동하는 것에 놀랐습니다. cat은 파일 이름이 문자 문자열이 아닌 stdin으로 예상해야합니다. psql << EOF는 논리적으로 들리지만 그렇지는 않습니다. 에코에서는 작동하지 않지만 고양이에서는 작동합니다. 이상한 행동. 그것에 대한 단서가 있습니까?
Alex

자신에게 응답 : 매개 변수가없는 cat은 입력 (stdin)을 통해 보내는 모든 것을 실행하고 출력을 복제하므로 출력을 사용하여 파일을>로 채 웁니다. 실제로 매개 변수로 읽은 파일 이름은 stdin 스트림이 아닙니다.
Alex

@Alex echo는 catstding (파이프 될 때)을 읽거나 명령 행 인수에 해당하는 파일을 읽는 동안 명령 행 인수를 인쇄합니다.
The-null-Pointer-

답변:


517

이것을 stdin에 문자열을 제공하기 위해 heredoc 형식 이라고 합니다 . 자세한 내용은 https://en.wikipedia.org/wiki/Here_document#Unix_shells 를 참조하십시오.


보낸 사람 man bash:

여기에 문서

이 유형의 방향 재 지정은 단어 만 포함하는 행 (후행 공백 없음)이 표시 될 때까지 쉘이 현재 소스에서 입력을 읽도록 지시합니다.

해당 지점까지 읽은 모든 라인은 명령의 표준 입력으로 사용됩니다.

여기 문서의 형식은 다음과 같습니다.

          <<[-]word
                  here-document
          delimiter

word 에서 매개 변수 확장, 명령 대체, 산술 확장 또는 경로 이름 확장이 수행되지 않습니다 . 단어의 문자 가 따옴표로 묶이면 구분 기호word 에서 따옴표를 제거한 결과이며 여기 문서 의 줄은 확장되지 않습니다. 단어 를 인용 부호로 묶지 않으면 여기 문서 의 모든 줄에 매개 변수 확장, 명령 대체 및 산술 확장이 적용됩니다. 후자의 경우, 문자 순서는 \<newline>무시되고, \문자를 인용하는 데 사용해야합니다 \, $하고 `.

재 지정 연산자가 있으면 <<-모든 선행 탭 문자가 입력 행과 구분 기호를 포함하는 행에서 제거됩니다 . 이를 통해 쉘 스크립트 내의 문서를 자연스럽게 들여 쓸 수 있습니다.


12
변수 / 매개 변수 확장을 비활성화하는 데 가장 힘든 시간을 보냈습니다. 내가해야 할 일은 "큰 따옴표"를 사용하는 것뿐이었습니다. 정보에 대해서 감사드립니다!
Xeoncross

11
에 관한 <<-유일한 최고의 것을 주시기 바랍니다 노트 되지 소프트 탭 문자 - 문자가 제거됩니다. 이것은 실제로 탭 문자가 필요한 드문 경우 중 하나입니다. 문서의 나머지 부분에서 소프트 탭을 사용하는 경우 보이지 않는 문자를 표시하고 탭 문자를 복사하여 붙여 넣어야합니다. 올바르게 수행하면 구문 강조 표시가 끝 구분 기호를 올바르게 잡아야합니다.
trkoch

1
이 답변이 아래 답변보다 더 도움이되는 방법을 모르겠습니다. 그것은 단지 다른 곳에서 발견 될 수있는 정보를 이미 언급하고 있습니다 (이미 확인되었을 가능성이 있습니다)
BrDaHa

@BrDaHa, 아닐 수도 있습니다. 왜 질문입니까? 공감 때문에? 그것은 이었다 몇 년 동안 단 하나. 날짜를 비교하여 볼 수 있습니다.
Alexei Martianov

501

cat <<EOF구문은 Bash에서 여러 줄 텍스트로 작업 할 때 매우 유용합니다. 쉘 변수, 파일 또는 파이프에 여러 줄 문자열을 지정할 때

cat <<EOFBash 의 구문 사용법 예 :

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.


1. 1과 3은 고양이없이 할 수 있습니다. 2. 예제 1은 간단한 여러 줄 문자열로 수행 할 수 있습니다.
Daniel Alder

269

귀하의 경우 "EOF"를 "여기 태그"라고합니다. 기본적으로 <<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

30
이것은 최고의 실제 답변입니다 ... 당신은 관련 이론 대신 사용의 주요 목적을 정의하고 명확하게 진술합니다 ... 중요하지만 필요하지는 않습니다 ... 감사합니다-매우 도움이됩니다
oemb1905

5
@edelans 당신은 때 추가해야 <<-탭을 선도하는 데 사용됩니다 인식되는 태그를 방지하지 않습니다
더 - 널 포인터 타입

1
당신의 대답은 "여러 줄로 된 문자열을 입력 할 것입니다"
Calculus

79

POSIX 7

kennytm이 인용 man bash했지만 대부분 POSIX 7입니다. http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_07_04 :

경로 재 지정 연산자 "<<"및 "<<-"는 모두 "여기 문서"라고하는 쉘 입력 파일에 포함 된 행을 명령 입력으로 경로 재 지정할 수 있습니다.

이 문서는 다음 단어 이후에 시작하여 사이에 문자가없는 구분 기호와 a 만 포함 된 줄이있을 때까지 계속되는 단일 단어로 취급됩니다. 그런 다음 다음 문서가 있으면 시작합니다. 형식은 다음과 같습니다.

[n]<<word
    here-document
delimiter

여기서 선택적 n은 파일 설명자 번호를 나타냅니다. 숫자를 생략하면 여기 문서는 표준 입력 (파일 설명자 0)을 나타냅니다.

단어의 문자가 따옴표로 묶인 경우 단어에서 따옴표 제거를 수행하여 구분 기호를 구성해야하며 여기에 문서 행을 확장하지 않아야합니다. 그렇지 않으면 구분자가 단어 자체가됩니다.

단어의 문자가 인용되지 않으면, 본 문서의 모든 줄은 매개 변수 확장, 명령 대체 및 산술 확장을 위해 확장되어야합니다. 이 경우 입력의 in은 내부 큰 따옴표로 작동합니다 (큰 따옴표 참조). 그러나 큰 따옴표 문자 ( ' "')는 큰 따옴표가"$ () ","`` "또는"$ {} "내에있는 경우를 제외하고는 여기에서 특별히 다루지 않습니다.

방향 재 지정 기호가 "<<-"이면 모든 선행 <tab>문자는 입력 행과 후행 구분 기호를 포함하는 행에서 제거됩니다. 한 줄에 둘 이상의 "<<"또는 "<<-"연산자가 지정된 경우 첫 번째 연산자와 관련된 여기 문서는 응용 프로그램에서 먼저 제공해야하며 셸에서 먼저 읽습니다.

여기서 터미널 문서에서이 문서를 읽고 셸이 대화 형인 경우 셸 변수에 설명 된대로 처리 된 변수 PS2의 내용을 구분자가 인식 될 때까지 각 입력 줄을 읽기 전에 표준 오류에 기록해야합니다.

아직 제시되지 않은 몇 가지 예.

따옴표는 매개 변수 확장을 방지합니다

인용없이:

a=0
cat <<EOF
$a
EOF

산출:

0

따옴표로 :

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

또는 (추악하지만 유효한) :

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들여 쓰기를 선호 했습니다. Yikes.


마지막 예제에서 <<-및에서 <tab>a, 목적은 스크립트 내에서 코드의 정상적인 들여 쓰기를 허용하는 한편 수신 프로세스에 표시되는 heredoc 텍스트는 열 0에서 시작할 수 있도록하는 것입니다. 더 많은 맥락에서 머리를 긁는 것을 막을 수 있습니다 ...
David C. Rankin

1
내 EOF 태그 사이의 일부 컨텐츠를 확장해야하고 일부는 확장하지 않으면 어떻게 지출을 피해야합니까?
Jeanmichel Cote

2
... 바로 앞에 백 슬래시를 사용$
Jeanmichel 코트

@JeanmichelCote 나는 더 나은 옵션을 보지 못합니다 :-) 일반 문자열을 사용하면 따옴표를 섞는 것도 고려할 수 "$a"'$b'"$c"있지만 여기에는 AFAIK 와 비슷한 아날로그가 없습니다.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

25

고양이 대신 티를 사용

원래 질문에 대한 대답은 아니지만 어쨌든 이것을 공유하고 싶었습니다. 루트 권한이 필요한 디렉토리에 구성 파일을 만들어야했습니다.

이 경우 다음이 작동하지 않습니다.

$ 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

귀하의 경우 사용에 sudo를 bash는 -c '고양이 << EOF> /etc/somedir/foo.conf # 내 설정 파일 foo는 = 바 EOF'
likewhoa

5

위의 답변에 대한 약간의 확장. 후행 >은 입력을 파일로 보내서 기존 컨텐츠를 겹쳐 씁니다. 그러나 특히 편리한 사용 방법은 다음 >>과 같이 파일 끝에 새 내용을 추가 하는 이중 화살표 입니다.

cat <<EOF >> /etc/fstab
data_server:/var/sharedServer/authority/cert /var/sharedFolder/sometin/authority/cert nfs
data_server:/var/sharedServer/cert   /var/sharedFolder/sometin/vsdc/cert nfs
EOF

이를 통해 fstab실수로 내용을 수정하는 것에 대해 걱정할 필요가 없습니다.


1

이것은 반드시 원래 질문에 대한 대답은 아니지만 내 자신의 테스트 결과를 공유합니다. 이:

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

다음과 같은 파일을 생성합니다 :

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

따라서 cat 명령을 사용하는 것이 중요하지 않습니다.


2
어느 껍질? 우분투 18.04에서 bash 4.4와 OSX에서 bash 3.2로 테스트했습니다. <<testwithout없이 사용할 때 둘 다 빈 파일을 만들었습니다 cat <<test.
wisbucky

이것은 zsh의 LInux Mint 19 Tara에서 작동했습니다.
Geoff Langenderfer

0

여기서 문서는 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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.