关于bash脚本效率的疑问

时间:2014-02-12 19:17:29

标签: bash scripting sed awk

我必须完成一项相对简单的任务,基本上我有大量具有以下格式的文件

“2014-01-27”,“07:20:38”,“数据”,“数据”,“数据”

基本上我想提取前两个字段,将它们转换为一个unix纪元日期,加上6个小时(由于时区差异),然后用生成的毫秒替换前两个原始列(unix epoch,自19700101起转换为工厂) 我写了一个工作正常的脚本,问题是非常非常慢,我需要运行150多个文件,总行数超过5.000.000,我想知道你是否对如何有任何建议我可以让它更快,这是:

    #!/bin/bash

function format()
{
while read line; do
 entire_date=$(echo ${line} | cut -d"," -f1-2);
 trimmed_date=$(echo ${entire_date} | sed 's/"//g;s/,/ /g');
 seconds=$(date -d "${trimmed_date} + 6 hours" +%s);
 millis=$((${seconds} * 1000));
 echo ${line} | sed "s/$entire_date/\"$millis\"/g" >> "output"
done < $*
}

format $*

3 个答案:

答案 0 :(得分:3)

我试图避免外部命令(日期除外)以获得时间。测试显示它比您的代码快4倍。 (好吧,三人的perl解决方案比我快40倍!)

#! /bin/bash

function format()
{
    while IFS=, read date0 date1 datas; do
        date0="${date0//\"/}"
        date1="${date1//\"/}"
        seconds=$(date -d "$date0 $date1 + 6 hours" +%s)
        echo "\"${seconds}000\",$datas" 
    done
}

output="output.txt"

# Process each file in argument
for file ; do
    format < "$file"
done >| "$output"

exit 0

答案 1 :(得分:3)

您正在为每个输入行产生大量进程。通过快速浏览可能很容易将其中一半的因素考虑在内,但我肯定会建议改用Perl或Python。

perl -MDate::Parse -pe 'die "$0:$ARGV:$.: Unexpected input $_"
    unless s/(?<=^")([^"]+)","([^"]+)(?=")/ (str2time("$1 $2")+6*3600)*1000 /e'

我想推荐Text::CSV,但我没有在这里安装它,如果你有要求在第二秒之后不接触字段,那么它可能不是你需要的。这很快,很脏,但可能比“适当的”CSV解决方案简单得多。

真正的肉是来自Date::Parsestr2time函数,我想它会比重复调用date快得多(ISTR它在内部做了一些记忆,所以它可以做附近的日期很快)。正则表达式用输出替换前两个字段;请注意/e标志,该标志允许在替换部件中评估Perl代码。 (?<=^")(?=")零宽度断言要求存在这些匹配,但在替换操作中不包括它们。 (我最初用附加的双引号代替,但是这个改变后,它们会被保留,因为显然你想保留它们。)

如果您希望脚本在出现错误的情况下继续(可能会将标准错误重定向到文件,那么将die更改为warn

答案 2 :(得分:2)

在awk中使用存在函数mktime,经过测试,它比perl更快。

awk  '{t=$2 " " $4;gsub(/[-:]/," ",t);printf "\"%s\",%s\n",(mktime(t)+6*3600)*1000,substr($0,25)}' FS=\" OFS=\" file

这是测试结果。

    $ wc -l file
    1244 file
    $ time awk  '{t=$2 " " $4;gsub(/[-:]/," ",t);printf "\"%s\",%s\n",(mktime(t)+6*3600)*1000,substr($0,25)}' FS=\" OFS=\" file > /dev/null

real    0m0.172s
user    0m0.140s
sys     0m0.046s

    $ time perl -MDate::Parse -pe 'die "$0:$ARGV:$.: Unexpected input $_"
        unless s/(?<=^")([^"]+)","([^"]+)(?=")/ (str2time("$1 $2")+6*3600)*1000 /e' file > /dev/null

real    0m0.328s
user    0m0.218s
sys     0m0.124s