%20%ED%98%B8%EC%B6%9C%EC%97%90%20%EC%9E%85%EB%A0%A5%20%EC%A0%9C%EA%B3%B5.png)
두 번 호출되는 다음 프로그램이 있다고 가정합니다 read()
.
#include <stdio.h>
#include <unistd.h>
#define SIZE 0x100
int main(void)
{
char buffer1[SIZE];
char buffer2[SIZE];
printf("Enter first input: \n");
fflush(stdout);
read(STDIN_FILENO, buffer1, SIZE);
printf("Enter second input: \n");
fflush(stdout);
read(STDIN_FILENO, buffer2, SIZE);
printf("\nFirst input:\n%s", buffer1);
printf("\nSecond input:\n%s", buffer2);
return 0;
}
직접 호출하면 1
첫 번째 입력과 2
두 번째 입력을 받아 인쇄할 수 있습니다.
First input:
1
Second input:
2
입력 리디렉션을 사용할 때 이를 달성하는 방법은 무엇입니까?
첫 번째 방법은 두 개의 입력을 사용하므로 다음 방법은 작동하지 않습니다 read
.
파이프 리디렉션:
$ { echo "1"; echo "2"; } | ./main_read
Enter first input:
Enter second input:
First input:
1
2
Second input:
구분자 리디렉션:
$ ./main_read << EOF
1
2
EOF
Enter first input:
Enter second input:
First input:
1
2
Second input:
소스 코드를 변경할 수 없으며 입력이 때때로 SIZE
.
두 번째 사람이 나머지 입력을 사용할 수 read()
있도록 첫 번째 사람에게 읽기를 중지하도록 알리는 방법이 있습니까 ?read()
답변1
이는 수용 가능한 솔루션을 제공하지 않을 수 있지만 다음을 고려하십시오.
-
소스코드는 변경할 수 없습니다
쉘은 실행 중인 프로그램의 열린 파일 설명자가 가리키는 위치를 변경할 수 없으며 실행 중인 프로그램이 파일 설명자 읽기를 중지하도록 할 수도 없습니다.
경쟁 조건을 이용하는 것 외에 남은 대안 중 일부는 다음과 같습니다.
SIZE
프로그램이 항상 한 번에 바이트를 입력하는지 확인하십시오 .{ echo foo | dd bs=256 conv=sync echo bar | dd bs=256 conv=sync } 2>/dev/null | ./main_read
산출:
Enter first input: Enter second input: First input: foo Second input: bar
이는 적어도
SIZE
파이프 버퍼 크기보다 작다고 가정합니다.expect
(또는 이에 상응하는) 스크립트 로 프로그램 호출을 래핑합니다 .expect <<'EOT' spawn ./main_read expect "Enter first input:" send "foo\n" expect "Enter second input:" send "bar\n" expect eof EOT
또는 다른 명령의 출력을 파이프할 수 있는 방식으로 별도로 읽습니다(OS가 프로세스에 대한 파일 설명자를 제공한다고 가정
/dev/fd/n
) .echo foo | { echo bar | expect 4<&0 <<'EOT' spawn ./main_read set chan [open "/dev/fd/3"] gets $chan line expect "Enter first input:" send "$line\r" close $chan set chan [open "/dev/fd/4"] gets $chan line expect "Enter second input:" send "$line\r" close $chan expect eof EOT } 3<&0
두 경우 모두 출력은 다음과 같습니다.
spawn ./main_read Enter first input: foo Enter second input: bar First input: foo Second input: bar
비차단 방식으로 파이프를 열 수 있는 시스템(예: Linux)에서는 FIFO를 사용하여 쉘이 프로그램을 읽고 쓰도록 할 수 있습니다. 예를 들어:
makefifo fifo { exec 3<>fifo ./main_read 0<&3 } | sh -c ' # read a line from the pipe # read from a file or a different pipe, write to fifo # repeat ... # echo the output from the pipe ' mysh
그러나
expect
가능하다면 이를 재창조해야 할 설득력 있는 이유는 없습니다.
하지만 다른 사람들이 지적했듯이,약속은 없어모든 프로그램은 read
실제로 SIZE
바이트를 얻습니다.
답변2
소스 코드를 변경할 수 없다고 가정
주로 이 가정을 변경하는 데 중점을 두어야 합니다.
일반적으로 호출이 반환할 바이트 수는 보장되지 않습니다 read()
. 일반 파일에서 읽을 때 일반적으로 요청된 바이트 수(최대 사용 가능한 바이트 수)를 반환하지만 모든 유형의 파일 설명자에 해당되는 것은 아닙니다. 시스템에서 실행되는 프로세스 간의 예약 및 기타 타이밍 문제도 단일 호출에 사용 가능한 데이터 양에 영향을 미칠 수 있습니다.
read()
읽은 데이터의 양을 확인하지 않고 전화를 걸면 거의 항상 오류가 발생합니다. 이와 같은 상황 dd
(호출 동작을 명확하게 노출해야 함 read()
)과 데이터그램 소켓에서 읽는 경우(각각 read()
메시지가 제공됨)에도 프로그램은 얼마나 많은 데이터를 얻었는지 알아야 합니다.
프로그램이 행을 읽으려면 raw 대신 fgets()
또는 를 사용해야 합니다 . 다른 유형의 블록을 읽어야 하는 경우 이러한 블록을 구별할 수 있는 다른 방법을 구현해야 합니다. 이는 길이를 앞에 추가하거나, 별도의 파일 설명자를 사용하거나, 일부 구분 기호(개행 문자와 같지만 바이트보다 길 수 있음)를 사용하는 것일 수 있습니다.getline()
read()
즉, stdin
데이터그램 소켓에 연결하도록 준비하지 않는 한 이는 매우 특이한 설정이 되며 실제로 데이터를 제공하기 위해 일반 입력 리디렉션을 사용할 수 없습니다.
또한 버퍼를 에 전달하기 전에 printf("%s")
프로그램은 문자열을 종료하는 NUL 바이트가 포함되어 있는지 확인해야 합니다. 이제 제공된 데이터 중 하나에 NUL이 포함되어 있지 않으면 프로그램은 read()
데이터가 전혀 반환되지 않는 두 번째 경우를 포함하여 정의되지 않은 동작을 생성합니다.read()
답변3
아래 의견에서 제안한 대로 다음을 수행할 수 있습니다.
{ echo a & sleep 0.1; echo b; } | ./main
수면 일정을 조정해야 할 수도 있습니다. 이 명령의 요점은 read()에 대한 첫 번째 호출이 이것이 a
얻은 전체 입력이라고 생각하도록 하는 것입니다. 이것가설echo a &
C 프로그램은 첫 번째 read()에 의해 완료되고 처리된 후 (참고 &
- 백그라운드로 전송됨) 두 번째 read()에 도달한다는 것입니다 . 그러나 Linux는 게으른 가상 메모리 할당도 수행하는 진정한 다중 사용자 다중 작업 운영 체제이므로 sleep 0.1
이러한 가정을 모든 경우에 적용하는 것만으로는 충분하지 않을 수 있습니다.
왜 작동하는지 그리고
{ echo a && echo b; } | ./main
유일한 차이점은 read()가 첫 번째 읽기에서 사용 가능한 전체 표준 입력(최대 SIZE 문자)을 읽고 두 번째 읽기에서는 뒤에 문자를 남기지 않는다는 것입니다. read()가 반환한 값을 확인한 경우:
#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
#define SIZE 0x100
int main(void)
{
char buffer1[SIZE];
char buffer2[SIZE];
printf("Enter first line of input: \n");
ssize_t read_bytes = read(STDIN_FILENO, buffer1, SIZE);
buffer1[read_bytes] = '\0';
printf("First input - count of read bytes: %jd\n", (intmax_t) read_bytes);
printf("Enter second line of input: \n");
read_bytes = read(STDIN_FILENO, buffer2, SIZE);
printf("Second input - count of read bytes: %jd\n", (intmax_t) read_bytes);
buffer2[read_bytes] = '\0';
printf("\nFirst input:\n%s", buffer1);
printf("\nSecond input:\n%s", buffer2);
return 0;
}
두 번째에는 어떤 문자도 읽지 않는다는 것을 알 수 있습니다.
$ ./main << EOF
1
2
EOF
Enter first line of input:
First input - count of read bytes: 4
First input:
1
2
Enter second line of input:
Second input - count of read bytes: 0
Second input:
First input:
1
2
Second input:
작업을 완료 하려면 { echo a && echo b; } | ./main
getline()으로 전환하거나 두 입력을 모두 단일 버퍼에 저장하고 strtok()를 사용하여 버퍼를 줄바꿈으로 구문 분석해야 합니다. getline() 버전은 다음과 같습니다.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(void)
{
size_t size = 0x100;
char *buffer1 = malloc(size);
if (buffer1 == NULL)
{
perror("malloc");
}
char *buffer2 = malloc(size);
if (buffer2 == NULL)
{
perror("malloc");
}
printf("Enter first line of input: \n");
getline(&buffer1, &size, stdin);
printf("Enter second line of input: \n");
getline(&buffer2, &size, stdin);
printf("\nFirst input:\n%s", buffer1);
printf("\nSecond input:\n%s", buffer2);
free(buffer1);
free(buffer2);
return 0;
}
예:
$ ./main << EOF
1
2
EOF
Enter first line of input:
Enter second line of input:
First input:
1
Second input:
2
여기서는 3가지 사항을 더 논의하고 싶습니다.
fflush(stdout);
표준 출력은 항상 개행 문자 다음에 플러시되므로 그럴 필요가 없습니다.매뉴얼 페이지는 로컬에 있으므로 인터넷에서 맨 페이지를 찾을 필요가 없습니다.
man 2 read
터미널에 입력하거나 편집기에서 열면 됩니다(예: Emacs는 이를 수행합니다).귀하의 질문에 게시한 코드에 버그가 있습니다.
read()
nul 바이트는 자동으로 추가되지 않으므로 UB를 방지하려면 직접 이 작업을 수행해야 합니다. 그것은해야한다:printf("Enter first line of input: \n"); ssize_t read_bytes = read(STDIN_FILENO, buffer1, SIZE - 1); buffer1[read_bytes] = '\0';