疑惑

2014-02-12 37 views
0

我必须完成一个相对简单的任务,基本上我有文件的大量使用以下格式疑惑

“2014年1月27日”,“七点20分38秒”,”数据“,”数据“,”数据“

基本上我想提取前2个字段,将它们转换为unix时代日期,将6小时添加到它(由于时区差异),并将前2个原始列结果为毫秒(unix时代,因为19700101转换为工厂) 我写了一个脚本,工作正常,好吧,问题是非常非常慢,我需要运行这个超过150个文件,总行数为超过5.000.000,我很奇怪g如果你有,我怎么能使其更快任何建议,那就是:

#!/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 $* 
+0

Unix纪元是秒,而不是毫秒。它通常是一个(长整数)整数,但是具有小数部分的表示法已被用于表示亚秒精度。 – tripleee

+0

您应该使用'“$ @”'而不是'$ *'。您的代码目前无法处理其中包含空格的文件名等。 – tripleee

回答

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 
3

我试图避免外部命令(除了日期)赢得时间。测试表明它比你的代码快4倍。 (好吧,在tripleee的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

最初的“读取行”是不必要的; 'IFS =,读取date0 date1数据;做'让你删除身体的第一线。最后的'for'循环可以是'for file;格式<“$ file”; done>“$ output”,无需预先创建文件。 – chepner

+0

谢谢!我保留了原始脚本的第一次阅读。修正了 –

+1

为了好奇,'> |'与'|'是相同的,但是如果它存在并且'noclobber'选项被设置,允许''$ output“'被覆盖。 – chepner

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(也许将标准错误重定向到一个文件然后!)

+0

thx,确实工作得很快,但缺少每个文件的第一行,它在4秒内编辑99999行而不是9 – JBoy

+0

我不明白它是如何错过第一行的,除非你有一个标题行与字段名称或者其他的东西?如果你想打印第一行逐字,请添加'下一个如果$。 == 1;'在脚本的其余部分之前(在“死亡”之前)。 – tripleee

+0

或者可能是's /“,”/ /和next,如果$。 == 1;'只是合并第一行的前两个字段。 – tripleee