如何解决基于前缀将标准输入路由到其他文件/程序的程序?
我正在寻找一个程序或(可能是 C)代码,我将其称为 route
,它在 stdin 上获取字节串/流并将其路由到 n 个程序之一,基于最初的几个字节(前缀)。前缀不会被转发/管道到目标程序。我想它会有一个命令行界面,如:
echo -n 'for-p1.perform-operation-A' | route 'for-p1.' >(program1) 'for-p2.' >(program2)
echo -n 'for-p2.perform-operation-B' | route 'for-p1.' >(program1) 'for-p2.' >(program2)
在上面给出的例子中,program1 会收到'perform-operation-A'(就像我执行了'echo 'perform-operation-A' | program1),program2 会收到'perform-operation-B'。前缀始终位于目标的(虚拟)文件名之前。
是否有任何现有的解决方案可以做到这一点,还是我必须自己推出?
编辑:根据普遍的要求,这是我 30 分钟的解决方案尝试,但我非常喜欢现有的解决方案,或者至少推荐图书馆的步骤:
/*
build:
sudo apt install -y build-essential && g++ main.cpp
usage example:
(
rm output* || true
echo -n "foo-hello" | ./a.out 2>/dev/null 'foo-' >(cat | tee -a output-foo) 'bar-' >(cat | tee -a output-bar) 1>/dev/null
echo -n "bar-world" | ./a.out 2>/dev/null 'foo-' >(cat | tee -a output-foo) 'bar-' >(cat | tee -a output-bar) 1>/dev/null
echo
echo -n "output-foo: " ; cat output-foo ; echo
echo -n "output-bar: " ; cat output-bar ; echo
)
*/
#include <fcntl.h>
#include <stdio.h>
#include <map>
#include <string>
using namespace std;
typedef FILE* File;
#define hasKey(map,key) (map.find((key)) != map.end())
// I assume this is pretty inefficient,looking for a good solution
void pipeRest(File fromFile,File toFile) {
size_t bytesRead = 0;
do {
char data;
bytesRead = fread(&data,1,fromFile);
if (bytesRead) fwrite(&data,toFile);
} while (bytesRead > 0);
}
int main(
int const argc,char const * const * const argv
) {
if (argc <= 1) {
fprintf(stderr,"usage:\n\n\troute prefix1 >(ouput-program-1) prefix2 >(output-program-2) ...\n\nreads from stdin,recognizes any of n prefixes and pipes the rest to the filename following the prefix\n");
exit(1);
}
// Parse [prefix outputFile] pairs from commandline args
map<string,string> prefixesToOutputFileNames;
map<string,File> prefixesToOutputFiles;
for (int i = 0; i < argc; i++) {
fprintf(stderr,"argv[%d] = '%s'\n",i,argv[i]);
if (i > 0 && i % 2 == 0) {
auto const prefix = argv[i - 1];
fprintf(stderr,"prefix = '%s'\n",prefix);
auto const outputFileName = argv[i];
fprintf(stderr,"outputFileName = '%s'\n",outputFileName);
prefixesToOutputFileNames[prefix] = outputFileName;
auto const outputFile = fopen(outputFileName,"wb");
prefixesToOutputFiles[prefix] = outputFile;
}
}
// Start reading bytes from stdin,collect the prefix
freopen(0,"rb",stdin);
string prefix = "";
char nextPrefixChar[2] = { 0 };
size_t bytesRead = 0;
do {
bytesRead = fread(nextPrefixChar,stdin);
prefix += nextPrefixChar;
fprintf(stderr,"read %zd bytes = '%s',prefix = '%s'\n",bytesRead,bytesRead ? nextPrefixChar : 0,prefix.c_str());
if (hasKey(prefixesToOutputFiles,prefix)) {
// Prefix found -> pipe to corresponding output file
auto const outputFileName = prefixesToOutputFileNames[prefix];
fprintf(stderr,"prefix '%s' was recognized,will pipe rest to '%s'\n",prefix.c_str(),outputFileName.c_str());
auto const outputFile = prefixesToOutputFiles[prefix];
pipeRest(stdin,outputFile);
exit(0);
}
} while (bytesRead > 0);
fprintf(stderr,"input ends and did not recognize any prefix,rest will be piped to stdout\n");
pipeRest(stdin,stdout);
return 0;
}
解决方法
在 bash shell 中,您只需编写:
tee >(sed -n 's/^for\.p1\.//p' | program1) | sed -n 's/^for\.p2\.//p' | program2
以下程序:
filter() {
keyword=$1
shift
# https://stackoverflow.com/questions/407523/escape-a-string-for-a-sed-replace-pattern
keyword=$(printf '%s\n' "$keyword" | sed -e 's/[]\/$*.^[]/\\&/g');
LC_ALL=C sed -n "s/^$keyword//p" | "$@"
}
hexfilter() {
keyword=$1
shift
keyword=$(printf '%s\n' "$keyword" | sed -e 's/../[\\x&]/g');
LC_ALL=C sed -n "s/^$keyword//p" | "$@"
}
program1() { :; }
program2() { :; }
echo -e '\x00\xca\xfe\x2a world!' |
{ tee >(filter 'for.p1.' program1 >&3) >(hexfilter '00cafe2a' sed 's/^/Hello/' >&3) | filter 'for.p2.' program2 >&3; } 3>&1 |
cat
过滤前缀 0x00 0xca 0xfe 0x2a
并输出 Hello world!
。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。