파일의 첫 번째 기호를 바꾸는 프로세스를 누가 중단했으며, 그 이유는 무엇입니까?

파일의 첫 번째 기호를 바꾸는 프로세스를 누가 중단했으며, 그 이유는 무엇입니까?
$ type 1.sh
#!/bin/bash -eu
php <(echo 12)
$ ./1.sh
2

$ type 2.sh
#!/bin/bash -eu
cat <(echo 12)
$ ./2.sh
12

$ type 3.sh
#!/bin/bash -eu
echo 12 | php
$ ./3.sh
12

$ type 4.sh
#!/bin/bash -eu
rm -f named_pipe
mknod named_pipe p
echo 12 > named_pipe
$ ./4.sh
$ php named_pipe   # from another window
2

Debian( php-5.4.14, bash-4.1.5)와 Arch Linux( php-5.4.12, ) bash-4.2.42에 대해 테스트했습니다 .

답변1

물론 PHP입니다. 파이프는 파일의 첫 번째 문자를 먹지 않습니다. PHP는 모든 문자를 읽지만 첫 번째 문자를 출력하지 않습니다. 지금까지는 문제가 입력에 있는지 출력에 있는지 알 수 없습니다. PHP가 어떤 이유로 첫 번째 문자를 출력하지 않는 것일 수 있습니다.

약간의 실험을 통해 실제로 입력에 문제가 있음이 나타났습니다.

$ php <(echo '<?php echo "hello" ?>') 
?php echo "hello" ?>
$ php <(echo ' <?php echo "hello" ?>')
hello$ 

PHP는 스크립트가 파일 이름으로 제공되는 경우에만(명령줄 인수가 없고 스크립트가 stdin에서 읽는 경우가 아님), 스크립트 파일이 파이프 또는 다른 것인 경우에만 스크립트의 첫 번째 문자를 먹습니다. 스크립트 파일은 검색 가능합니다(예: 일반 파일인 경우).

무슨 일이 일어나는지는 처음에 일반 PHP 파서가 시작되기 전에 명령줄 처리기가 스크립트가 다음으로 시작하는지 확인한다는 것입니다.셰르본철사. 스크립트가 두 문자로 시작하면 #!PHP는 첫 번째 줄을 건너뜁니다. 이렇게 하면 다음과 같은 PHP 스크립트를 작성할 수 있습니다.

#!/usr/bin/php
first line
<?php echo "second line"?>

스크립트가 출력됩니다.

first line
second line

#!/usr/bin/php그리고 애초에 거짓은 없었습니다.

Shebang 감지기는 다음과 같이 작동합니다.

  • 첫 번째 문자를 읽습니다.
  • 첫 번째 문자가 이면 #다른 문자를 읽습니다.
  • 처음 두 문자가 이면 #!첫 번째 개행 문자까지 계속 읽습니다.
  • 처음 두 문자가 아닌 경우 #!파일의 시작 부분으로 되감습니다.
  • 일반 PHP 구문 분석을 시작합니다.

스크립트 파일을 찾을 수 없으면 되감기 단계가 실패하지만 PHP는 이를 감지하지 않으므로 파일의 첫 번째 문자가 손실됩니다(첫 번째 문자가 a이면 두 번째 문자도 손실됩니다 #). . 이것은 PHP 명령줄 해석기의 버그입니다.

너는 볼 수있어암호자신을 위해 (함수에서 cli_seek_file_begin).

답변2

Ubuntu에서 본 내용을 재현할 수 있습니다.

#!/bin/bash -eu
rm -rf named_pipe
mkfifo named_pipe
echo 12 >  named_pipe


$ ./namedpipe.sh &  # background it
$ php named_pipe    # same terminal; don't need another window
2

php이는 .I can't presents it cat(커널의 fifo 코드가 심각하게 손상되었음을 나타냄) 과 관련이 있습니다 .

로그에는 파이프에서 두 개의 숫자와 줄 바꿈으로 데이터를 읽었음을 strace표시합니다 .php

open("named_pipe", O_RDONLY|O_LARGEFILE) = 3
fstat64(3, {st_mode=S_IFIFO|0664, st_size=0, ...}) = 0
mmap2(NULL, 65536, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb6fdc000
read(3, "12\n", 65536)                  = 3

시도하는 다음 파일 설명자는 3lseek입니다.

_llseek(3, 0, 0xbf93c640, SEEK_SET)     = -1 ESPIPE (Illegal seek)

여기서 상황이 잘못될 수 있습니다. 이것이 버퍼링된 I/O 라이브러리를 통해 호출된다고 가정하면 라이브러리는 파이프에 의해 혼란스러워지고 버퍼를 엉망으로 만듭니다.

이 시점에서 다른 많은 작업이 발생합니다.

그런 다음 tty 설정을 가져오고 다시 읽는 것을 포함하여 몇 가지 추가 작업을 시도했는데, 이는 명확하게 EOF를 나타냅니다.

ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, 0xbf93a2e8) = -1 EINVAL (Invalid argument)
fstat64(3, {st_mode=S_IFIFO|0664, st_size=0, ...}) = 0
read(3, "", 65536)                      = 0

PHP는 Unix 파이프의 차단 읽기에서 0 반환이 EOF를 의미하지 않는다고 생각하므로 다시 시도합니다.

read(3, "", 65536)                      = 0
close(3)              

추적의 다음 두 줄은 다음과 같습니다. 읽기 전에 할당된 버퍼가 해제되고(아마도 스트림 버퍼?) 잘린 출력이 이어집니다.

munmap(0xb6fdc000, 65536)               = 0
write(1, "2\n", 2)                      = 2

이는 기본적으로 어떤 소프트웨어가 책임이 있는지에 대한 질문에 답합니다. 더 자세히 알아보고 싶다면 디버그 버전을 구하는 것이 한 가지 방법 php입니다 gdb.


부록:

php이 예제에서처럼 표준 입력에서 읽을 때 동작은 완전히 다릅니다 echo 12 | php. 예를 들어 llseek파일 설명자 0에서는 작업이 시도되지 않습니다. 또한 스트림은 절대 닫히지 않으며(물론) 읽기와 쓰기 사이에 중간 작업이 없습니다. 다음은 연속된 블록으로 표시됩니다.

read(0, "12\n", 4096)                   = 3
read(0, "", 4096)                       = 0
read(0, "", 4096)                       = 0
write(1, "12\n", 3)                     = 3

명령줄에서 명명된 파일을 여는 것은 표준 입력(아마도 특별히 설정된 전역 표준 입력 스트림 개체로 표시됨)에서 읽는 것과 다른 프로세스를 거칠 것으로 예상할 수 있습니다.

관련 정보