処理するファイルごとに、STDOUTに異なるデータ型を送信できるスクリプトがある場合。 STDINを読み取る他のスクリプトからどのデータ型が何であるかを知るために、各データ型をどのように分離しますか?
例えば。処理するすべてのファイルに対して、2つの異なる(不明な)文字列と2つの異なる(不明な)数を生成するスクリプトがあります。次に、STDINからデータを読み取り、指定された各文字列である数値を処理する別のスクリプトがあります。 2番目のスクリプトが各タイプを正しく認識するように、最初のスクリプトの出力形式をどのように指定しますか?
私はJSONを介してネットワークデータをシリアル化することに慣れています。しかし、STDIN / STDOUTのためのより軽いソリューションまたは組み込みのソリューションがあるかどうか疑問に思います。たぶん、一意の区切り記号や私が逃したものはありますか?
答え1
標準出力とは何か、どこに行くのか説明していません! (例えば、標準出力はコンピュータグラフィックス画像処理アプリケーションは一部のブラウザに間接的にアクセスします。
これUnixの哲学そしてUnixパイプstdinとstdoutがプレーンテキストであることを願っていますが、これは単に習慣。場合によっては、他の規則があるかもしれません(例えば、通常はlpr
標準lp
入力からPDFを好む)。
プログラムを作成する場合は、JSONやXMLなどのより構造化されたテキストを出力するための特定のスキーマ(たとえば、いくつかのプログラムパラメータで指定)があるかもしれません。CSVまたはYAML。参考にしてくださいジャックJSONを処理できます。
一部のプログラムは(イサティそして/または統計資料)彼らの場合標準出力(またはそれら標準入力)は端末であり、それに応じて動作します(おそらく以下を使用して)。呪い、テミオスまたはANSIエスケープコード)。
多くのテキスト形式(特にXML)には伝統的な開始文字またはヘッダーがあるため、場合によってはこれを推測する必要があります(例:ファイル形式)ことが可能です。また見てくださいジェスチャー広大極 そしてlibmagic(そしてファイル1))。
処理するすべてのファイルに対して、2つの異なる(不明な)文字列と2つの異なる(不明な)数を生成するスクリプトがあります。次に、STDINからデータを読み取り、指定された各文字列である数値を処理する別のスクリプトがあります。 2番目のスクリプトが各タイプを正しく認識するように、最初のスクリプトの出力形式をどのように指定しますか?
不明な文字列を十分に知り(文書化)した場合は、改行文字などの制御文字を含めないでください。一つ行)特定の形式を決定できます。
FIRSTSTRING:
最初の文字列
FIRSTNUMBER:
最初の数字
例えば
FIRSTSTRING: foo bar is nice!
FIRSTNUMBER: 42
次に、2番目のスクリプトは簡単な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
2つのスクリプトがあり、1つは2つの文字列と2つの数字を生成します。 2番目のスクリプトでは、これを2つの文字列と2つの数字に解析できます。
どちらのスクリプトもユーザーが制御できるため、2番目のスクリプトはデータを読み取るのに便利な方法であるスクリプトから別のスクリプトにデータを送信できます。
2 つのスクリプト間でパイプされるデータの形式、順序、および解釈を決定できます。合理的であると思われる場合は、バイナリでエンコードされたデータを送信することもできます。
2つのスクリプトまたはプログラム間の特定の「契約」または「合意」を決定して直接作成した以外に、データに適用できるデータ型はありません。
一部の標準のUnixツールは、入力が特定の形式であることを好むと仮定していますsort
が、入力データの解釈はコマンドラインオプションを使用して変更できます。cut
設計された例:
#!/bin/sh
echo 'first string'
echo 'second string'
echo '1.1'
echo '3.14'
2番目のスクリプトは次のとおりです。
#!/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] }'
2番目のスクリプト:
#!/bin/sh
jq -r '"A number: \(.numbers[])"'
答え3
stdin
stdout
プロセスのファイル記述子0と1などです。ファイル説明を開くたとえば、システムコールを使用してファイルを開くか、名前付きパイプを使用して名前付きパイプにパイプを作成するか、//などを使用してソケットを生成することによってそれ自体が生成されると指定open()
します。pipe()
open()
connect()
accept()
socketpair()
少なくともほとんどは、すべてのバイトシーケンスをストリームとして読み書きできます。パイプなどのプロセス間通信に一般的に使用されるオブジェクトの場合小川ソケットでは通常、メッセージ境界は保持されません。
たとえば、シェルのコマンドラインでは次のようになります。
writer | reader
ここで、ライターの標準出力はパイプ(またはシェルによるソケットペア)の一方の端であり、リーダーの標準出力はもう一方の端です。
aがこれを行うwriter
と、2つの書き込みの間で実行されない限り、2つのメッセージが入ってくることはわかりません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
使用する言語によって異なります。