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)
我知道您说过您想坚持使用本机应用程序,但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 无法安装)运行最初提供的cdrtoip
500,000 行 .csv的统计数据:parallel
$ time ./csvjob.sh
real 14m7.467s
user 6m3.883s
sys 4m18.556s
Run Code Online (Sandbox Code Playgroud)
以下内容应该适用于awk
支持用户定义函数以及内置函数sprintf()
和rshift()
函数的任何版本。这包括 GNU awk。
我从这里借用了十进制并将其改编为点分四组 IP 地址算法:
正如我的评论中提到的,将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)