Bash의 파일 읽기 명령 대체 이해


11

Bash가 정확히 다음 줄을 어떻게 처리하는지 이해하려고합니다.

$(< "$FILE")

Bash 매뉴얼 페이지에 따르면 다음과 같습니다.

$(cat "$FILE")

이 두 번째 줄에 대한 추론을 따를 수 있습니다. Bash는에서 변수 확장을 수행하고 $FILE, 명령 대체를 입력하고, 값을 $FILEto에 전달하고 cat, cat을 내용 $FILE을 표준 출력으로 출력하고, 전체 행을 명령 내부의 표준 출력으로 대체하여 명령 대체를 완료하고 Bash는 다음과 같이 실행하려고 시도합니다. 간단한 명령.

그러나, I는 상술 한 제 1 라인, 그것을 이해 : ON 배시 변수 대체를 수행 $FILE, 배시 열리면 $FILE표준 입력을 판독하기위한, 여하튼 기준 입력은 표준 출력으로 복사 명령 교체 완료하고, 생성 된 기준을 실행할 배시 시도 산출.

누군가 $FILEstdin에서 stdout으로 어떻게 내용을 설명 할 수 있습니까 ?

답변:


-3

이것은 bash 명령 대체< 의 측면이 아닙니다 . 파이프와 같은 리디렉션 연산자이며 일부 쉘은 명령없이 허용합니다 (POSIX는이 동작을 지정하지 않습니다).

더 많은 공간이 있으면 더 분명 할 것입니다.

echo $( < $FILE )

이것은 사실상 * POSIX 안전과 동일합니다.

echo $( cat $FILE )

... 또한 효과적으로 *

echo $( cat < $FILE )

마지막 버전부터 시작하겠습니다. 이것은 cat인수없이 실행되므로 표준 입력에서 읽습니다. $FILE로 인해 표준 입력으로 리디렉션 <되므로 cat내용이 표준 출력에 저장됩니다. $(command)subsitution는 못살게 굴지 cat의 인수로의 출력을 echo.

에서 bash(그러나 POSIX 표준에), 당신이 사용할 수있는 <명령없이. bash(그리고 zsh하고 ksh있지만이 dash) 해석하는 것 같은 경우 cat <, 새로운 서브 프로세스를 호출하지 않고 있지만. 이것은 쉘 고유이므로 외부 명령을 문자 그대로 실행하는 것보다 빠릅니다 cat. * 이것이 내가 "효과적으로 동일하다"고 말하는 이유입니다.


마지막 단락에서 "라고 bash해석 할 것입니다."라고 말하면 cat filename이 동작이 명령 대체에만 해당되는 것입니까? 내가 < filename스스로 실행하면 bash는 그것을 제거하지 않기 때문입니다. 아무것도 출력하지 않고 프롬프트로 돌아갑니다.
Stanley Yu

명령이 여전히 필요합니다. @cuonglm은 내 원래의 텍스트를 변경 cat < filenamecat filename있는 내가 반대하고 되돌아 갈 수 있습니다.
Adam Katz

1
파이프는 파일 유형입니다. 쉘 연산자 |는 두 개의 서브 프로세스 (또는 일부 쉘의 경우 서브 프로세스에서 쉘의 표준 입력까지) 사이에 파이프를 작성합니다. 쉘 연산자 $(…)는 서브 프로세스에서 쉘 자체 (표준 입력이 아닌)로 파이프를 작성합니다. 쉘 연산자 <는 파이프를 포함하지 않으며 파일 만 열고 파일 디스크립터를 표준 입력으로 이동합니다.
Gilles 'SO- 악마 그만해

3
< file와 같지 않습니다 (같은 위치 cat < file제외 ). 완벽하게 POSIX이며 읽기 전용으로 열린 다음 아무것도하지 않습니다 (그래서 바로 가까이에 있습니다). 그것은의 또는 그의 특별한 연산자 , 와 (과 동작은 POSIX에서 지정되지 않은 남아 있습니다). 자세한 내용은 내 답변 을 참조하십시오. zsh$READNULLCMD < file< filefilefile$(< file)`< file`kshzshbash
Stéphane Chazelas

2
@ StéphaneChazelas의 의견을 다른 관점에서 살펴보면 첫 번째 근사치와 $(cmd1) $(cmd2)일반적으로 같습니다 $(cmd1; cmd2). 그러나 경우보고 cmd2있다 < file. 이라고 말하면 $(cmd1; < file)파일을 읽지 않고로 읽습니다 $(cmd1) $(< file). 따라서 명령이 $(< file)일반적인 경우 라고 말하는 것은 올바르지 않습니다 .   A는 특별한 경우 명령 대체 및 리디렉션이 아니 정상적인 사용은. $(command)< file$(< …)
Scott

14

$(<file)(와 함께 작동 `<file`)은 zshand로 복사 한 Korn 쉘의 특수 연산자입니다 bash. 명령 대체와 비슷하게 보이지만 실제로는 그렇지 않습니다.

POSIX 셸에서 간단한 명령은 다음과 같습니다.

< file var1=value1 > file2 cmd 2> file3 args 3> file4

모든 부분은 선택 사항이며 방향 전환 만, 명령 만, 할당 만 또는 조합 할 수 있습니다.

재 지정이 있지만 명령이없는 경우 재 지정이 수행되므로 ( > file열리고 잘림 file) 아무 일도 일어나지 않습니다. 그래서

< file

엽니 다 file읽을 수 있지만 명령이 없습니다로 다음 아무 반응이 없습니다. 그래서 file닫히고 그게 끝입니다. 만약이 $(< file)간단한이었다 명령 치환 그때는 아무것도 확장 것이다.

에서 POSIX 사양$(script), 경우는 script그 만 리디렉션 구성 지정되지 않은 결과를 생성합니다 . 그것은 Korn 쉘의 특별한 행동을 허용하는 것입니다.

ksh (여기서는으로 테스트 됨 ksh93u+)에서 스크립트 가 리디렉션 (명령 없음, 할당 없음)으로 만 구성된 하나의 간단한 명령 (전과 후에 주석이 허용되지만)과 첫 번째 리디렉션이 stdin (fd) 인 경우 0) 입력 만 ( <, <<또는 <<<) 리디렉션 :

  • $(< file)
  • $(0< file)
  • $(<&3)( $(0>&3)실제로 같은 연산자이기 때문에)
  • $(< file > foo 2> $(whatever))

하지만:

  • $(> foo < file)
  • ...도 아니다 $(0<> file)
  • ...도 아니다 $(< file; sleep 1)
  • ...도 아니다 $(< file; < file2)

그때

  • 첫 번째 리디렉션을 제외한 모든 것은 무시됩니다 (구문 분석됩니다)
  • 그리고 파일 / heredoc / herestring (또는 같은 것을 사용하는 경우 파일 설명자에서 읽을 수있는 내용 <&3)에서 마지막 줄 바꿈 문자를 뺀 내용으로 확장됩니다 .

$(cat < file)그것을 제외하고 사용 하는 것처럼

  • 읽기는 쉘이 아닌 내부적으로 수행합니다. cat
  • 파이프 나 추가 프로세스가 필요하지 않습니다
  • 위의 결과로, 내부 코드가 서브 쉘에서 실행되지 않기 때문에, 이후의 수정은 ( $(<${file=foo.txt})또는$(<file$((++n))) )
  • 읽기 오류 (파일을 열거 나 파일 설명자를 복제하는 동안 오류는 발생하지 않음)는 자동으로 무시됩니다.

에서은 zsh, 그것은 단지 하나의 파일 입력 리디렉션있을 때 특별한의 동작은 트리거 것을 제외하고는 동일합니다 ( <file또는 0< file, 아니 <&3, <<<here, < a < b...)

그러나 다른 쉘을 에뮬레이션 할 때를 제외하고는 다음과 같습니다.

< file
<&3
<<< here...

즉, 명령 대체 외부에서 명령이없는 입력 리디렉션 만 zsh있는 경우 $READNULLCMD(기본적으로 호출기)를 실행하고 입력 및 출력 리디렉션이 모두있는 경우 $NULLCMD( cat기본적으로) 따라서 ( $(<&3)특별히 인식되지 않는 경우에도) 연산자, ksh호출기를 호출하여 호출하는 것처럼 작동 합니다 (호출기 작동cat 표준 이 파이프가되기 때문에 ).

그러나 동안 ksh의는 $(< a < b)의 내용으로 확장 할 a에, zsh그것의 내용으로 확장, ab(또는 b경우 생성 multios옵션을 사용하지), $(< a > b)복사 할 ab아무것도 등으로 확장

bash 비슷한 연산자가 있지만 몇 가지 차이점이 있습니다.

  • 코멘트는 전에 허용되지만 후에는 허용되지 않습니다.

    echo "$(
       # getting the content of file
       < file)"

    작동하지만 :

    echo "$(< file
       # getting the content of file
    )"

    아무것도 확장되지 않습니다.

  • 처럼 zshA와 더 가을 다시가 없습니다 불구하고, 하나 개의 파일의 표준 입력 리디렉션, $READNULLCMD그래서 $(<&3), $(< a < b)리디렉션을 수행하지만, 아무것도 확장 않습니다.

  • 어떤 이유로 bash호출하지는 않지만 cat파이프를 통해 파일의 내용을 공급하는 프로세스를 여전히 포크하여 다른 쉘보다 최적화 수준이 훨씬 낮습니다. 내장 된 $(cat < file)위치 와 같은 효과 cat가 있습니다 cat.
  • 위의 결과로, 변경 내용은 이후에 손실됩니다 ( $(<${file=foo.txt})예 : 위에서 언급 한 $file할당은 나중에 손실 됨).

에서은 bash, IFS= read -rd '' var < file (도에서 작동 zsh)를의 내용을 읽을 수있는보다 효과적인 방법입니다 텍스트 변수로 파일을. 또한 후행 줄 바꿈 문자를 유지하는 이점이 있습니다. 참고 $mapfile[file]zsh합니다 (의 zsh/mapfile바이너리 파일과 함께 작동하는 모듈과 정규 파일의 경우).

pdksh 기반 변형은 kshksh93과 비교하여 몇 가지 변형이 있습니다. 관심있는 것은 mksh(pdksh 파생 쉘 중 하나)에서

var=$(<<'EOF'
That's multi-line
test with *all* sorts of "special"
characters
EOF
)

여기서는 문서의 경우와 같이 임시 파일이나 파이프를 사용하지 않고 여기 문서의 내용 (후행 문자없이)을 확장하여 효과적인 여러 줄 인용 구문으로 만듭니다.

모든 버전의 호환성을 ksh, zsh그리고 bash가장으로 만 제한하는 $(<file)변수 수정 월 내에 또는 유지되지 않을 수도 있다는 사실 이루어진 주석 및 베어링을 회피.


$(<)파일 이름의 연산자 인 것이 맞 습니까? 인가 <$(<)리디렉션 연산자, 여부 그 자체 운영자는 전체 운영자의 일부 여야합니다 $(<)?
Tim

@Tim, 전화를 거는 방법은 중요하지 않습니다. $(<file)콘텐츠 file와 유사한 방식으로 콘텐츠를 확장하는 것을 의미합니다 $(cat < file). 그것이 수행되는 방법은 셸마다 다르며 답변에 길게 설명되어 있습니다. 원하는 경우 명령 대체처럼 보이는 것 (구문 적으로)에 단일 stdin 리디렉션 (구문 적으로)처럼 보이지만 여기에 나열된 쉘에 따라주의 사항과 변형이있는 경우에 트리거되는 특수 연산자라고 말할 수 있습니다. .
Stéphane Chazelas

@ StéphaneChazelas : 평소와 같이 매혹적인; 이것을 북마크했습니다. 그래서, n<&mn>&m같은 일을? 나는 그것을 몰랐지만 너무 놀라운 것은 아니라고 생각합니다.
Scott

@Scott, 예, 둘 다 dup(m, n). stdio 및 일부를 사용하여 ksh86의 증거를 볼 수 fdopen(fd, "r" or "w")있으므로 문제가 될 수 있습니다. 그러나 쉘에 stdio를 사용하는 것은 거의 의미가 없으므로 차이가 나는 현대 쉘을 찾을 것이라고는 생각하지 않습니다. 하나 개의 차이는 즉 >&n이다 dup(n, 1)(짧은 1>&n동안) <&n이다 dup(n, 0)(짧게 0<&n).
Stéphane Chazelas

권리. 물론 파일 디스크립터 복제 호출의 두 인수 형식이 호출됩니다 dup2(); dup()처럼 하나의 인수 만 open()사용하며 가용 한 가장 낮은 파일 설명자를 사용합니다. (오늘 나는 기능있다는dup3() 것을 알게되었습니다 .)
Scott

8

bash내부적으로 파일 이름을 확장 했기 때문에 파일 이름을 확장하고 파일을 표준 출력으로 표시 $(cat < filename)합니다. 그것은 bash 기능 bash입니다. 어떻게 작동하는지 정확히 알기 위해 소스 코드를 조사해야 할 수도 있습니다 .

이 기능을 처리하는 함수는 다음과 같습니다 ( bash소스 코드에서 file builtins/evalstring.c).

/* Handle a $( < file ) command substitution.  This expands the filename,
   returning errors as appropriate, then just cats the file to the standard
   output. */
static int
cat_file (r)
     REDIRECT *r;
{
  char *fn;
  int fd, rval;

  if (r->instruction != r_input_direction)
    return -1;

  /* Get the filename. */
  if (posixly_correct && !interactive_shell)
    disallow_filename_globbing++;
  fn = redirection_expand (r->redirectee.filename);
  if (posixly_correct && !interactive_shell)
    disallow_filename_globbing--;

  if (fn == 0)
    {
      redirection_error (r, AMBIGUOUS_REDIRECT);
      return -1;
    }

  fd = open(fn, O_RDONLY);
  if (fd < 0)
    {
      file_error (fn);
      free (fn);
      return -1;
    }

  rval = zcatfd (fd, 1, fn);

  free (fn);
  close (fd);

  return (rval);
}

$(<filename)정확히 동일하지 않은 메모 $(cat filename); 파일 이름이 대시로 시작하면 후자가 실패합니다 -.

$(<filename) 원래부터 ksh , 그리고에 추가되었습니다 bash에서 Bash-2.02.


1
cat filenamecat이 옵션을 허용하기 때문에 파일 이름이 대시로 시작하면 실패합니다. 를 사용하여 대부분의 최신 시스템에서이 문제를 해결할 수 있습니다 cat -- filename.
Adam Katz

-1

명령 대체는 평소와 같이 명령을 실행하고 명령을 실행하는 지점에서 출력을 덤프하는 것으로 생각하십시오.

명령 출력은 다른 명령의 인수로 사용되어 변수를 설정하고 for 루프에서 인수 목록을 생성하는 데에도 사용할 수 있습니다.

foo=$(echo "bar")변수의 값 $foobar; 명령 출력 echo bar.

명령 대체


1
나는 OP가 명령 대체의 기본 사항을 이해한다는 것이이 질문에서 분명히 분명하다고 생각합니다. 의 특별한 경우에 대한 질문이며 $(< file)일반적인 경우에 대한 자습서가 필요하지 않습니다. 당신이 그 명령을 $(< file)하는 평범한 경우 라고 말하면 , 아담 카츠가 말하는 것과 똑같은 말을하는 것입니다. 둘 다 틀 렸습니다. $(command)< file
Scott
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.