$ 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
시도하는 다음 파일 설명자는 3
lseek입니다.
_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
명령줄에서 명명된 파일을 여는 것은 표준 입력(아마도 특별히 설정된 전역 표준 입력 스트림 개체로 표시됨)에서 읽는 것과 다른 프로세스를 거칠 것으로 예상할 수 있습니다.