처리하는 각 파일에 대해 STDOUT에 다른 데이터 유형을 보낼 수 있는 스크립트가 있는 경우. STDIN을 읽는 다른 스크립트에서 어떤 데이터 유형이 무엇인지 알 수 있도록 각 데이터 유형을 어떻게 분리합니까?
예를 들어. 처리하는 모든 파일에 대해 두 개의 서로 다른(알 수 없는) 문자열과 두 개의 서로 다른(알 수 없는) 숫자를 생성하는 스크립트가 있습니다. 그런 다음 STDIN에서 데이터를 읽어 주어진 각 문자열인 숫자를 처리하는 또 다른 스크립트가 있습니다. 두 번째 스크립트가 각 유형을 올바르게 인식하도록 첫 번째 스크립트의 출력 형식을 어떻게 지정합니까?
저는 JSON을 통해 네트워크 데이터를 직렬화하는 데 익숙합니다. 하지만 STDIN/STDOUT을 위한 더 가벼운 솔루션이나 내장 솔루션이 있는지 궁금합니다. 어쩌면 고유한 구분 기호나 내가 놓친 것이 있을까요?
답변1
표준 출력이 무엇인지, 어디로 가는지 설명하지 않았습니다! (예를 들어, 표준 출력은컴퓨터 그래픽 이미지 처리애플리케이션은 일부 브라우저에 간접적으로 액세스합니다.
이것유닉스 철학그리고유닉스 파이프stdin과 stdout이 일반 텍스트이기를 바라지만 이것은 단지관습. 어떤 경우에는 다른 규칙이 있을 수 있습니다(예: 일반적 lpr
으로 lp
표준 입력에서 PDF를 선호함).
프로그램을 작성하는 경우 JSON 또는 XML과 같은 보다 구조화된 텍스트를 출력하기 위한 특정 스키마(예: 일부 프로그램 매개변수로 지정)가 있을 수 있습니다.CSV또는YAML. 참고하세요잭JSON을 처리할 수 있습니다.
일부 프로그램은 (사용하여이사티그리고/또는통계자료) 그들의 경우표준 출력(또는 그들의표준 입력)는 터미널이며 그에 따라 작동합니다(아마도 다음을 사용하여).저주,테미오스, 또는ANSI 이스케이프 코드).
많은 텍스트 형식(특히 XML)에는 전통적인 시작 문자나 헤더가 있으므로 어떤 경우에는 이를 추측해야 합니다(예:파일 형식)것이 가능하다. 또한 살펴보세요몸짓 광대극 그리고libmagic(그리고파일 1)).
처리하는 모든 파일에 대해 두 개의 서로 다른(알 수 없는) 문자열과 두 개의 서로 다른(알 수 없는) 숫자를 생성하는 스크립트가 있습니다. 그런 다음 STDIN에서 데이터를 읽어 주어진 각 문자열인 숫자를 처리하는 또 다른 스크립트가 있습니다. 두 번째 스크립트가 각 유형을 올바르게 인식하도록 첫 번째 스크립트의 출력 형식을 어떻게 지정합니까?
알 수 없는 문자열을 충분히 알고(그리고 문서화) 한다면 개행 문자와 같은 제어 문자를 포함하지 마십시오.하나의행) 특정 형식을 결정할 수 있습니다.
FIRSTSTRING:
첫 번째 문자열
FIRSTNUMBER:
첫 번째 숫자
예를 들어
FIRSTSTRING: foo bar is nice!
FIRSTNUMBER: 42
그런 다음 두 번째 스크립트에는 간단한 awk
스크립트를 작성하고 사용합니다.sed
즉, 결정하고문서귀하의 경우에는 간단한 임시 형식입니다.가능한JSON보다 처리가 더 쉽습니다. 원하는 경우 자신만의 임시 세션을 가질 수 있습니다.문서(어쩌면 일부를 사용하여EBNF상징).
많은 도구에는 모든 것이 준비되어 있습니다. 예를 들어 ps
, ls
, df
, 및 ifconfig
임시이지만 잘 문서화된 출력 형식 및 규칙이 있습니다. 동일공정(5). 따라서 많은 스크립트가 이러한 출력을 구문 분석할 수 있습니다.
그러나 JSON은 단순하고 유연하며 확장 가능하도록 설계되었습니다.평상복문자열(제어 문자, 여러 줄 등 포함). 이것이 당신에게 중요하다면 그것을 사용하십시오.
JSON이나 XML을 출력하기 위해 모든 Unix 유틸리티를 재창조하고 재구현할 수 있습니다.많은일하다). 예를 들어 어떤 사람들은 재창조했습니다.공정(5)그리고생산시스템의 커널 데이터를 일부 XML 형식으로 출력하는 파일 시스템 /xmlproc/
이 아닌 의사 파일 시스템을 갖춘 커널 모듈입니다 . /proc/
그러나 그것은 효과가 없었습니다! 사회적 관습은 매우 중요합니다(그렇게 중요한 이유는 다음과 같습니다).문서출력 형식(적어도 긴 주석)
(JSON이나 XML을 사용하더라도 어떻게 사용하는지 문서화해야 합니다)
그런데 기존의 많은 Unix 도구는 규칙에 미묘함을 추가할 수 있습니다. 예를 들어, 파일 경로에는 공백, 탭 또는 캐리지 리턴이 포함될 수 있습니다(참조:경로 해상도(7)) 하지만 눈살을 찌푸릴 수도 있습니다(그래서 저는 절대 하지 않습니다). $HOME
이론적으로 일부 사용자의 디렉토리에는 반환 문자나 콜론이 포함될 수 있지만 이렇게 하면 대부분의 도구가 영향을 받습니다.비밀번호(5)...). 대시로 시작하는 파일 경로는 매우 긴 경로와 마찬가지로 친숙하지 않습니다. 예를 들어 $HOME
이론적으로 길이는 3000자일 수 있지만 실제로는 현명하지 않습니다.
답변2
두 개의 스크립트가 있는데, 하나는 두 개의 문자열과 두 개의 숫자를 생성합니다. 두 번째 스크립트에서는 이를 두 개의 문자열과 두 개의 숫자로 구문 분석할 수 있습니다.
두 스크립트 모두 사용자가 제어할 수 있으므로 두 번째 스크립트가 데이터를 읽는 데 편리한 방식으로 한 스크립트에서 다른 스크립트로 데이터를 보낼 수 있습니다.
두 스크립트 사이에 파이프되는 데이터의 형식, 순서 및 해석을 결정할 수 있습니다. 합리적이라고 생각되면 바이너리로 인코딩된 데이터를 보낼 수도 있습니다.
두 스크립트나 프로그램 간의 특정 "계약" 또는 "합의"를 결정하여 직접 작성한 것 외에는 데이터에 적용할 수 있는 데이터 유형이 없습니다.
일부 표준 Unix 도구는 입력이 특정 형식인 것을 선호 sort
하고 가정하지만 입력 데이터의 해석은 명령줄 옵션을 사용하여 변경할 수 있습니다.cut
고안된 예:
#!/bin/sh
echo 'first string'
echo 'second string'
echo '1.1'
echo '3.14'
두 번째 스크립트는 다음과 같습니다.
#!/bin/sh
IFS= read -r string1
IFS= read -r string2
read number1 number2
또는 JSON을 사용하세요.
#!/bin/sh
echo '{ "string1": "hello", "string2": "world", "numbers": [1.1,3.14] }'
두 번째 스크립트:
#!/bin/sh
jq -r '"A number: \(.numbers[])"'
답변3
stdin
stdout
프로세스의 파일 설명자 0과 1 등입니다 .파일 설명 열기예를 들어 시스템 호출을 사용하여 파일을 열거나 명명된 파이프를 사용하여 또는 명명된 파이프에 파이프를 생성 하거나 // 등을 사용하여 소켓을 생성함으로써 자체적으로 open()
생성 된다고 명시합니다.pipe()
open()
connect()
accept()
socketpair()
적어도 대부분은 모든 바이트 시퀀스를 스트림으로 읽고 쓸 수 있습니다. 파이프와 같은 프로세스 간 통신에 일반적으로 사용되는 개체의 경우개울소켓에서는 일반적으로 메시지 경계가 유지되지 않습니다.
예를 들어, 셸 명령줄에서는 다음과 같습니다.
writer | reader
여기서 기록기의 표준 출력은 파이프(또는 쉘에 따라 소켓 쌍)의 한쪽 끝이고 판독기의 표준 출력은 다른 쪽 끝입니다.
a가 이렇게 writer
하면 두 개의 쓰기 사이에 실행 되지 않는 한 두 개의 메시지가 들어오는 것을 알 수 없습니다 write(1, "foo", 3); write(1, "bar", 3)
.reader
read()
writer
데이터그램 소켓(적어도 UDP, SCTP 또는 unix 도메인)과 같은 일부 파일 유형이 있거나 메시지 경계를 유지하는 일부 시스템이 있지만 빈 메시지를 허용하려면 SOCK_SEQPACKET
다른 API가 필요하며 리더는 가능한 최대 크기 이러한 메시지는 미리 알려져야 하며 이를 수신할 수 있을 만큼 큰 버퍼가 할당되어야 합니다. 이러한 메시지 내용의 특성을 지정하려면 특정 형식의 인코딩을 사용해야 합니다.read()
write()
예:
$ strace -e write dd bs=2 count=3 if=/dev/zero status=none | strace -fe read cat
write(1, "\0\0", 2) = 2
write(1, "\0\0", 2) = 2
write(1, "\0\0", 2) = 2
read(0, "\0\0\0\0\0\0", 131072) = 6
read(0, "", 131072) = 0
크기 2의 쓰기 3회와 크기 6의 읽기 1회. 그러나 시간에 따라 크기 2의 읽기 3회 또는 크기 4의 읽기 1회와 크기 2의 읽기 1회를 볼 수 있습니다. 파이프 또는 SOCK_STREAM 소켓 쌍을 사용하고 있습니다.
SOCK_SEQPACKET 소켓 쌍을 사용하십시오.
$ perl -MSocket -e '
socketpair(my $rdr, my $wtr, AF_UNIX, SOCK_SEQPACKET, PF_UNSPEC);
shutdown($rdr, 1); shutdown($wtr, 0);
if (fork) {
open STDIN, "<&", $rdr; close $wtr; close $rdr; sleep 1;
exec qw(strace -e read cat)
} else {
open STDOUT, ">&", $wtr; close $rdr; close $wtr;
exec qw(strace -e write dd count=3 bs=2 status=none if=/dev/zero)
}'
write(1, "\0\0", 2) = 2
write(1, "\0\0", 2) = 2
write(1, "\0\0", 2) = 2
+++ exited with 0 +++
read(0, "\0\0", 131072) = 2
read(0, "\0\0", 131072) = 2
read(0, "\0\0", 131072) = 2
read(0, "", 131072) = 0
+++ exited with 0 +++
이 3개의 쓰기에는 3개의 읽기가 필요하지만 쓰기가 완료된 후 읽기를 1초 정도 지연합니다.
따라서 결국 유형과 길이를 어떤 방식으로든 인코딩하는 것이 좋습니다. 데이터가 도착하자마자 독자가 데이터를 처리할 수 있도록 하는 간결한 형식(그러나 작성자는 메시지의 길이를 미리 알고 있음)을 사용하는 것입니다.TLV(유형, 길이, 값) 인코딩. "유형" 및 "길이" 단어의 길이와 유형(예: 32비트 리틀 엔디안 정수) 및 유형 값을 해석하는 방법에 대해 작성자와 독자가 동의해야 합니다.
또는 생성할 수 있는 모든 직렬화 형식을 사용할 수 있습니다.텍스트이는 또한 NUL 바이트나 특정 줄 구분 기호 트랜스코딩을 허용하지 않는 채널이나 바이트 순서가 다른 시스템 간에 스왑하는 경우에도 더 안전합니다. json, XML, perl Data::Dumper
, php 와 같이 serialize()
일부 쉘의 출력은 typeset -p
사용하는 언어에 따라 다릅니다.