我需要执行以下操作: sed'1d'simple.tsv> noHeader.tsv
将从我的大流文件中删除第一行(> 1 GB)。
问题是 - 我需要在我的流文件上执行它,所以它是:
sed'1d'myFlowFile> myFlowFile
问题是:我应该如何配置ExecuteStreamCommand处理器,以便它在我的流文件上运行命令并将其返回到我的流文件? 如果sed不是最佳选择,我可以考虑采取其他方式(例如尾巴)
谢谢,Michal
编辑2(解决方案):
下面是最终的ExecuteStreamCommand配置,可以完成我需要的操作(从流文件中删除第一行)。 @Andy - 非常感谢所有珍贵的提示。
答案 0 :(得分:7)
米甲
我想确保我正确理解你的问题,因为我认为有更好的解决方案。
<强>问题:强>
你有一个1GB的TSV加载到NiFi,你想删除第一行。
<强>解决方案:强>
如果您的文件较小,最好的解决方案是使用具有以下处理器属性的ReplaceText
处理器:
^.*\n
&lt; - 空字符串这将剥离第一行,而不必将1GB内容从NiFi发送到命令行,然后重新获取结果。不幸的是,要使用正则表达式,您需要设置最大缓冲区大小,这意味着需要将全部内容读入堆内存以执行此操作。
使用1GB文件,如果您知道第一行的确切值,则应尝试使用ModifyBytes
,它允许您从流文件内容的开头和/或结尾修剪字节数。然后,您可以简单地指示处理器删除内容的第一个 n 字节。由于NiFi的写时复制内容存储库,您仍然会有大约2GB的数据,但它使用8192B缓冲区大小以流方式进行。
我最好的建议是使用ExecuteScript
处理器。该处理器允许您使用各种语言(Groovy,Python,Ruby,Lua,JS)编写自定义代码,并使其在流文件上执行。使用如下所示的Groovy脚本,您可以删除第一行并以流方式复制其余部分,这样就不会对堆产生不必要的负担。
我用1MB文件测试了这个,每个流文件花了大约1.06秒(MacBook Pro 2015,16 GB RAM,OS X 10.11.6)。在更好的机器上,您显然可以获得更好的吞吐量,并且您可以将其扩展到更大的文件。
def flowfile = session.get()
if (!flowfile) return
try {
// Here we are reading from the current flowfile content and writing to the new content
flowfile = session.write(flowfile, { inputStream, outputStream ->
def bufferedReader = new BufferedReader(new InputStreamReader(inputStream))
// Ignoring the first line
def ignoredFirstLine = bufferedReader.readLine()
def bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream))
def line
int i = 0
// While the incoming line is not empty, write it to the outputStream
while ((line = bufferedReader.readLine()) != null) {
bufferedWriter.write(line)
bufferedWriter.newLine()
i++
}
// By default, INFO doesn't show in the logs and WARN will appear in the processor bulletins
log.warn("Wrote ${i} lines to output")
bufferedReader.close()
bufferedWriter.close()
} as StreamCallback)
session.transfer(flowfile, REL_SUCCESS)
} catch (Exception e) {
log.error(e)
session.transfer(flowfile, REL_FAILURE)
}
一方面注意,一般来说,NiFi的一个好习惯是在可能的情况下将巨型文本文件拆分为较小的组件流文件(使用SplitText
之类的东西)以获得并行处理的好处。如果1GB输入是视频,这将不适用,但正如您提到的TSV,我认为可能将初始流文件拆分为较小的部分并并行处理它们(甚至发送到集群中的其他节点)负载平衡)可以帮助您在这里的表现。
修改强>
我意识到我没有回答你原来的问题 - 如何将流文件的内容放入ExecuteStreamCommand
处理器命令行调用中。如果您想对属性的值进行操作,则可以在 Arguments 字段中使用Expression Language语法${attribute_name}
引用属性值。但是,由于内容不能从EL引用,并且您不希望通过将1GB内容移动到属性来销毁堆,因此最好的解决方案是使用{{1}将内容写入文件},对提供的文件名运行PutFile
命令并将其写入另一个文件,然后使用sed
将这些内容读回NiFi中的流文件。
编辑2:
这是一个template,它演示了对GetFile
和ExecuteStreamCommand
使用rev
对流文件内容并将输出放入新流文件的内容中。您可以运行流并监视sed
以查看输出或使用数据来源查询来检查每个处理器执行的修改。
答案 1 :(得分:1)
由于您要从文件中删除标题,所以我认为使用StripHeader处理器会更好。
ANKIT