【问题标题】:awk exec command for every line and keep columns每一行的 awk exec 命令并保留列
【发布时间】:2019-02-25 01:02:51
【问题描述】:

我有一个包含两列的大型数据集文件

AS  জীৱবিজ্ঞানবিভাগ
AS  চেতনাদাস
AS  বৈকল্পিক

我想在第二列上运行我的命令,存储结果并获得具有相同列格式的输出:

AS jibvigyanvibhag
AS chetanadas
AS baikalpik

我的命令是这个管道:

echo "$0" | indictrans -s asm -t eng --ml --build-lookup

所以我这样做

awk -v OFS="\t" '{ print "echo "$2" | indictrans -s asm -t eng --ml --build-lookup" | "/bin/sh"}' in.txt > out.txt

但这不会保留列,它只是像这样打印出第一列

jibvigyanvibhag
chetanadas
baikalpik

我的解决方案如下

awk -v OFS="\t" '{ "echo "$2" | indictrans -s asm -t eng --ml --build-lookup" | getline RES; print $1,$2,RES}' in.txt > out.txt

会打印出来

AS  জীৱবিজ্ঞানবিভাগ    jibvigyanvibhag
AS  চেতনাদাস    chetanadas
AS  বৈকল্পিক    baikalpik

现在我想对命令进行参数化,但是这里的转义看起来很奇怪:

"echo "$0" | indictrans -s $SOURCE -t $TARGET --ml --build-lookup"

它不起作用。如何正确执行该命令并转义参数?

[更新] 这是我受到建议的启发而得出的部分解决方案

#!/bin/bash

SOURCE=asm
TARGET=eng
IN=$2
OUT=$3

awk -v OFS="\t" '{
        CMD = "echo "$2" | indictrans -s asm -t eng --ml --build-lookup"
        CMD | getline RES
        print $1,RES
        close(CMD)
}' $IN > $OUT

我仍然无法摆脱变量,似乎我无法像往常一样用-v定义

awk -v OFS="\t" -v source=$SOURCE -v target=$TARGET '{
            CMD = "echo "$2" | indictrans -s source -t target --ml --build-lookup"
...

注意事项。

indictrans 进程处理stdin 并以这种方式写入stdout

    for line in ifp:
        tline = trn.convert(line)
        ofp.write(tline)
    # close files
    ifp.close()
    ofp.close()

在哪里

ifp = codecs.getreader('utf8')(sys.stdin)
ofp = codecs.getwriter('utf8')(sys.stdout)

所以它从stdin 中提取一个line,使用某个库trn.convert 处理数据,并将结果写入stdout,没有任何并行性。

出于这个原因(在多行输入方面缺乏并行性),性能受到数据集大小(行数)的限制。

here 提供了一个示例输入两列数据集(1K 行)。示例示例是

KN   ಐಕ್ಯತೆ ಕ್ಷೇಮಾಭಿವೃದ್ಧಿ ಸಂಸ್ಥೆ  ವಿಜಯಪುರ
KN   ಹೊರಗಿನ ಸಂಪರ್ಕಗಳು 
KN    ಮಕ್ಕಳ ಸಾಹಿತ್ಯ ಮತ್ತು ಸಾಂಸ್ಖ್ರುತಿಕ ಕ್ಷೇತ್ರದಲ್ಲಿ ಸೇವೆ ಸಲ್ಲಿಸುತ್ತಿರುವ ಸಂಸ್ಠೆ ಮಕ್ಕಳ ಲೋಕ  

而基于最后接受答案的示例脚本是here

【问题讨论】:

  • 您是否有理由为此使用awk?该代码存在严重的安全漏洞;编写一个根本不尝试在管道中涉及awk 的安全实现会更容易。
  • ...shell 本身对来自参数扩展结果的数据的处理方式与对代码的处理方式不同,但如果将某些内容以与代码相同的字符串传递到 shell , 这样的安全预防措施是有争议的。
  • 顺便说一句,您可以将多行输入流式传输到indictrans 吗?保持单个长时间运行的实例打开可能会更有效;只要它每行输入生成一行输出,就可以轻松生成整个流,然后将其与带外的第一列连接起来。这样你也会获得更好的性能。
  • 当然——改进的空间是速度而不是简单;如果您要翻译数百行代码,更简单的方法会运行数百个 indictrans 副本,而代码稍多的则只运行一个(并通过它移动所有一百行),因此启动 Python 所涉及的开销要少得多口译员一遍又一遍。仅从您显示的代码来看,我认为更高性能的版本可能会正常工作,但如果不能正常工作,那么 minimal reproducible example (理想情况下,如果它是公开的,则带有指向 indictrans 的链接)将是需要。

标签: bash awk


【解决方案1】:

不要使用 awk 调用 shell。 shell 本身会避免将数据视为代码,除非明确指示这样做 - 但是当您使用 system()popen() ,正如 awk 代码在这里所做的那样,作为参数传递的所有内容都会在数据能够转义其引用并被视为代码的上下文中进行解析。


简单方法:每行一个indictrans

如果您需要为要执行的每一行提供indictrans 的单独副本,请使用:

while read -r col1 rest; do
  printf '%s\t%s\n' "$col1" "$(indictrans -s asm -t eng --ml --build-lookup <<<"$rest")"
done <in.txt >out.txt

快速方法:一个indictrans 处理所有

如果indictrans 每行输入生成一行输出,您可以做得更好,将一个流与所有第一列粘贴在一起,将第二个字符串与其余行的翻译粘贴在一起,因此只需要要运行的indictrans 的一份副本:

#!/usr/bin/env bash
#              ^^^^- not compatible with /bin/sh

paste <(<in.txt awk '{print $1}') \
      <(<in.txt sed -E 's/^[^[:space:]]*[[:space:]]//' \
                | indictrans -s asm -t eng --ml --build-lookup) \
  >out.txt

【讨论】:

  • 谢谢!我还注意到awk 在执行命令时引入了更多问题,例如让这些进程分叉导致too many file opened 错误(system 和 open/getline` 方法)。
  • 我已经更新了这个答案(以前被接受),因为对于上面的 cmets,可以在性能方面进行改进,给出有关数据集和运行示例的更多信息。有关更多信息,请参阅更新的问题/cmets。
  • 这个答案已经包含性能改进——这是使用paste的后半部分。
  • 对不起!对,顺便说一句,优化版本不保留列结构 - 请参阅此处gist.github.com/loretoparisi/2abb25c1db934bf7b77c6cc62cd857d7 会发生什么。谢谢!
  • 您的代码将我原来的print $1 更改为print $2,所以这就是第一列的问题。第二列我无能为力,因为我无法运行indictrans——这是一个混乱的软件(不完整的setup.py、未声明的库依赖项等)——但我会给你另一个脚本可以用来证明该模式在使用正确逐行转换输入的代码运行时有效:paste &lt;(&lt;"$in" awk '{print $1}') &lt;(&lt;"$in" sed -E 's/^[^[:space:]]*[[:space:]]//' | while IFS= read -r line; do echo "$(base64 -w 0 &lt;&lt;&lt;"$line")"; done)
【解决方案2】:

您可以将第 2 列通过管道传输到您的命令,并在 awk 中使用如下命令的输出来更改它。

{
    cmd = "echo "$2" | indictrans -s asm -t eng --ml --build-lookup"
    cmd | getline $2
    close(cmd)
} 1

如果 SOURCETARGET 是 awk 变量

{
    cmd = "echo "$0" | indictrans -s "SOURCE" -t "TARGET" --ml --build-lookup"
    cmd
    close(cmd)
}

【讨论】:

  • 谢谢,我需要将 -s 和 -t 设置为 -s $SOURCE 和 -t $TARGET 等参数
  • 使用字符串替换来生成由 shell 执行的代码 - 因此,将数据评估为代码 - 是注入攻击的快速途径。
猜你喜欢
  • 1970-01-01
  • 2015-11-05
  • 1970-01-01
  • 2016-07-11
  • 1970-01-01
  • 1970-01-01
  • 2021-01-29
  • 2014-07-06
  • 1970-01-01
相关资源
最近更新 更多