用命令结果替换列

Mic*_*ael 7 shell bash sed awk

我正在尝试找到一种方法,用命令的输出快速有效地替换文件中每一行的列值。我需要每天处理大约 500,000 行的多个文件,所以我正在寻找可以尽快完成任务的东西。

我需要将逗号分隔行的第八列作为输入,运行命令,然后用命令的输出替换该列。

这是我已经尝试过的,虽然它有效,但速度非常慢:

awk -F "," 'NR > 1 {
    cmd = "cdrtoip " $8
    cmd | getline ip
    close(cmd)
    $8=ip
    print
}' $1.csv >> $1.csv.tmp
Run Code Online (Sandbox Code Playgroud)

我更愿意坚持使用 Bash 或其他可以在 Linux 服务器上预安装的 Linux 程序。

编辑:抱歉,我应该包括 cdrtoip 是什么。

# Convert CISCO format (signed integer) to Hex
# Capitalize or else conversion from hex to decimal doesn't work later
HEXIP=$(printf '%x\n' $1 | tr '[:lower:]' '[:upper:]')

# Negative numbers will get 8 'f' in front of them
# Trim that part off
if [[ ${#HEXIP} -eq 16 ]]; then
    HEXIP=${HEXIP:8:8}
fi

# Convert hex to decimal, separate into octets, put in order
OCTETS[0]=$(echo "ibase=16; ${HEXIP:6:2}" | bc)
OCTETS[1]=$(echo "ibase=16; ${HEXIP:4:2}" | bc)
OCTETS[2]=$(echo "ibase=16; ${HEXIP:2:2}" | bc)
OCTETS[3]=$(echo "ibase=16; ${HEXIP:0:2}" | bc)

# Print the IP
echo ${OCTETS[0]}.${OCTETS[1]}.${OCTETS[2]}.${OCTETS[3]}
Run Code Online (Sandbox Code Playgroud)

cdrip 上的运行时间给出:

    0.23s real     0.00s user     0.02s system
Run Code Online (Sandbox Code Playgroud)

rub*_*ils 5

我知道您说过您想坚持使用本机应用程序,但GNU Parallel允许您并行执行单独的进程,这将允许您更快地运行此操作:

sudo apt-get update
sudo apt-get install parallel
awk -F',' '{print $8}' file.csv | parallel -j+0 cdrtoip {}
Run Code Online (Sandbox Code Playgroud)

调用 的方法有很多种parallel,但上述方法将从 .csv 文件的第 8 列获取输出,并cdrtoip在系统的每一行上同时为每个核心执行一个进程。因此,基本上,如果您运行 4 个核心,则可以用正常执行时间的 25% 来完成这项工作。

它的优点parallel是它跟踪输出并按顺序生成它,就好像它只是一个正在运行的作业一样。

安装后,man parallel了解更多执行方式(或查看链接中的文档)。很抱歉,如果这不是您正在寻找的东西,但它过去曾多次为我提供帮助。

编辑:如果您想将输出添加回 .csv 以替换第 8 列,下面的示例有效,并且已经过测试。在双核 Macbook Pro 上执行 5,000 行 .csv 文件大约需要 3.25 分钟。

设置:

$ cat file.tmp
blah1,blah2,blah3,blah4,blah5,blah6,blah7,1175063050,blah9,blah10,blah11

$ for i in {1..5000}; do cat file.tmp; done > file.csv

$ wc -l < file.csv
5000
Run Code Online (Sandbox Code Playgroud)

脚本(使用cdrtoip您提供的):

$ cat csvjob.sh
#!/bin/bash

fragment1="$(cut -d, -f1-7 file.csv | tr ',' "\t")"
fragment2="$(cut -d, -f8 file.csv | parallel -j+0 cdrtoip {})"
fragment3="$(cut -d',' -f9- file.csv | tr ',' "\t")"

paste <(echo "$fragment1") <(echo "$fragment2") <(echo "$fragment3") | sed "s/\t/,/g" > newfile.csv
Run Code Online (Sandbox Code Playgroud)

结果:

$ time ./csvjob.sh
real    3m23.092s
user    1m22.245s
sys     2m57.794s

$ head -3 newfile.csv
blah1,blah2,blah3,blah4,blah5,blah6,blah7,10.10.10.70,blah9,blah10,blah11
blah1,blah2,blah3,blah4,blah5,blah6,blah7,10.10.10.70,blah9,blah10,blah11
blah1,blah2,blah3,blah4,blah5,blah6,blah7,10.10.10.70,blah9,blah10,blah11
Run Code Online (Sandbox Code Playgroud)

另一个编辑: 以下是在四核 Mac Mini 上执行的(还运行其他东西):

$ time ./csvjob.sh
real    2m12.171s
user    2m59.816s
sys     2m15.787s
Run Code Online (Sandbox Code Playgroud)

我还刚刚意识到你说的是 500,000 行而不是 5,000 行。对于它的价值,请参阅下面cdrtoip连续执行 5,000 次的统计数据:

$ time for i in {1..5000}; do cdrtoip 1175063050; done > /dev/null
real    2m32.487s
user    1m26.537s
sys     1m8.270s
Run Code Online (Sandbox Code Playgroud)

最终编辑: 以下是在四核 Mac Mini 上的 500,000 行文件上运行的,如前所述,该文件已经在运行多个应用程序:

$ time ./csvjob.sh

real    216m22.780s
user    301m40.694s
sys     239m44.404s
Run Code Online (Sandbox Code Playgroud)

我完全明白你的意思,OP。

即使并行运行,这也需要相当长的时间才能执行。

我看到OP找到了更好的解决方案。每个文件 126 秒是无与伦比的。再次强调一下,下面是在 8 核 Debian VM 上使用(我意识到 OP 无法安装)运行最初提供的cdrtoip500,000 行 .csv的统计数据:parallel

$ time ./csvjob.sh
real    14m7.467s
user    6m3.883s
sys     4m18.556s
Run Code Online (Sandbox Code Playgroud)


cas*_*cas 4

以下内容应该适用于awk支持用户定义函数以及内置函数sprintf()rshift()函数的任何版本。这包括 GNU awk。

我从这里借用了十进制并将其改编为点分四组 IP 地址算法:

/sf/ask/2031762421/

正如我的评论中提到的,将cdrtoip外部脚本重写为 awk 函数将避免调用外部脚本超过 500,000 次。

awk -F, '
function cdrtoip(addr) {
  return sprintf ("%d.%d.%d.%d",
           rshift(and(addr,0xff000000),24),
           rshift(and(addr,0x00ff0000),16),
           rshift(and(addr,0x0000ff00),08),
           rshift(and(addr,0x000000ff),00))
};

NR > 1 {
    $8 = cdrtoip($8);
    print
}' "$1.csv" >> "$1.csv.tmp"
Run Code Online (Sandbox Code Playgroud)

我在一个包含 500,000 行的测试文件上运行了这个,它在 2 秒内完成:

$ wc -l input.csv 
500000 input.csv
$ time ./michael.sh < input.csv > output.csv

real 0m1.956s   user 0m1.935s   sys 0m0.018s
Run Code Online (Sandbox Code Playgroud)