每行awk exec命令并保留列

时间:2018-09-20 16:51:44

标签: bash awk

我有一个大型数据集文件,其中有两列

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"

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

[UPDATE] 这是部分解决方案,我受到建议的

的启发
#!/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)

因此它需要从line中取出一个stdin,使用某个库trn.convert处理数据,然后将结果写入stdout,而没有任何并行性。

由于这个原因(就多行输入而言缺乏并行性),性能受到数据集大小(行数)的限制。

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

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

,基于最后接受的答案的示例脚本为here

2 个答案:

答案 0 :(得分:3)

请勿使用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

答案 1 :(得分:1)

您可以将第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)
}