检查实时数据时间戳并使用bash重定向输出

时间:2015-11-17 04:10:25

标签: bash

我一直在使用socat通过UDP提取ASCII流并将其写入文件。以下就是这样一条线。

socat UDP-RECV:$UDP_PORT,reuseaddr - | cat >> $INSTRUMENT_$JDAY_RAW &

收到的每个流已经由发件人使用ts(moreutils的一部分)加上时间戳,其中包括年份,Julian日,小时,分钟,秒和毫秒。如果Julian日更改,接收端的JDAY变量不会重新初始化,并且cat会使用昨天的时间戳将管道数据保存到同一个文件中。

以下是socat收到的udp流的示例。它被记录在20hz。

2015 317 06 34 43 303 winch680 000117.9 00000000 00000000.0

2015 317 06 34 43 353 winch680 000117.5 00000000 00000000.0

bash中是否有某种方式我可以接收socat收到的每一行,检查jday时间戳字段,并根据该时间戳更改输出文件?

3 个答案:

答案 0 :(得分:1)

不在cat。你需要一个[ not bash]脚本(例如perl / python或C程序)。

替换:

socat UDP-RECV:$UDP_PORT,reuseaddr - | cat >> $INSTRUMENT_$JDAY_RAW &

使用:

socat UDP-RECV:$UDP_PORT,reuseaddr - | myscript &

myscript的样子:

while (1) {
    get_data_from_socat_on_stdin();

    if (jdaynew != jdayold) {
        close_output_file();
        jdayold = jdaynew;
    }

    if (output_file_not_open)
        open_output_file(jdaynew);

    write_data_to_output_file();
}

答案 1 :(得分:0)

您可以使用bash中的read内置程序解析输入流。您可以使用$ help read获取更多信息。它通常使用空格分隔标记。如果您提供了输出结果的两行预览,则可能更容易提供帮助。

必须在启动cat命令之前定义变量$INSTRUMENT$JDAY,因为cat将在文件开始写入之前打开文件。

如果以某种方式从每一行中提取$JDAY$INSTRUMENT,您可以使用以下bash片段(假设socat读取的行看起来像<INSTRUMENT> <JDAY> <TS> yaddi yadda ...):

function triage_per_day () {
  while read INSTRUMENT JDAY TS REST; do
    echo "$TS $REST" >> "${INSTRUMENT}_${JDAY}_RAW";
  done
}
triage_per_day < <(socat UDP-RECV:"${UDP_PORT}",reuseaddr -)

如果你想变得更加漂亮,可以使用文件句柄来帮助bash运行得更快。只要日期相同,您就可以使用文件描述符重定向来保持输出到同一文件。这将最小化文件打开次数并关闭bash必须执行的操作。

function triage_per_day () {
  local LAST_JDAY=init
  exec 5>&1 # save stdout
  exec 1>&2 # echos are sent to stderr until JDAY is redefined

  while read INSTRUMENT JDAY TS REST; do
    if [[ "$JDAY" != "$LAST_JDAY" ]]; then
      # we need to change output file
      # send stdout to file in append mode
      exec 1>>"${INSTRUMENT}_${JDAY}_RAW"
      LAST_JDAY="${JDAY}"
    fi
    echo "$TS $REST"
  done

  exec 1>&5 # restore stdout
  exec 5>&- # close stdout copy
}
triage_per_day < <(socat UDP-RECV:"${UDP_PORT}",reuseaddr -)

如果你想用不同的字符标记你的行而不是空格,比如','逗号,你可以在本地修改特殊变量IFS

function extract_ts () {
   local IFS=,; # special bash variable: internal-field-separator
   # $REST will contain everything after the third token. it is a good
   # practice to specify one more name than your last token of interest.
   while read TOK1 TS REST; do
     echo "timestamp is $TS";
   done
}

如果您需要更精细地处理每一行以提取时间戳和其他字段,您可以改为执行外部程序(python / perl / cut / awk / grep等),但这比简单地坚持使用bash内置函数,如readecho。如果您必须这样做,速度是一个问题,您可以考虑将脚本更改为另一种语言,以提供您所需的表达能力。如果您需要花哨的正则表达式,您可能还希望在手册中查看bash Pattern substitution

function extract_ts () {
   # store each line in the variable $LINE
   while read LINE; do
     TS="$(echo "$LINE" | ...)";
     echo "Timestamp is $TS";
   done
 }

推荐做法

另外,我应该提一下,如果你打算将它们用作文件名参数,最好用双引号括起你的bash变量(比如在答案中)。如果名称包含空格或特殊字符,则尤其如此 - 例如可以从日期或时间派生的文件名中预期。如果您的变量扩展为空(由于人为或编程错误),位置参数将会丢失,有时会出现错误的影响。

考虑:

# copy two files to the directory (bad)
$ cp file1 file2 $MYDIR

如果$MYDIR未定义,则此命令相当于使用file1的内容覆盖file2。将此与cp file1 file2 "$MYDIR"进行对比,因为目标""不存在,因此会提前失败。

我在您的问题中看到的另一个问题来源是变量名称后跟下划线_,如$INSTRUMENT。这些应该用大括号{ }包围。

INSTRUMENT=6
BAR=49
echo $INSTRUMENT_$BAR # prints '49', but you may have expected 6_49

因为_是变量名中的有效字符,bash会尝试在INSTRUMENT之后贪婪地“粘合”'_'以匹配可能的最长有效变量名称,即{{1} }。但是,此变量未定义,并扩展为空字符串,因此您将剩下其余部分$INSTRUMENT_。此示例可以正确地重写为:

$BAR

答案 2 :(得分:0)

这是适合我的代码。 输入的udp流如下所示:

2015 317 06 34 43 303 winch680 000117.9 00000000 00000000.0

    #!/bin bash
    # This code creates a function which reads the fields in the 
    # udp stream into a table
    # and uses the fields in the table to determine output.
    UDP_PORT=5639
    function DATAOUT () {
        while read YR JDY MIN SEC MSEC INST TENS SPEED LINE; do
            echo "$YR $JDY $HR $MIN $SEC $MSEC $INST $TENS $SPEED $LINE" >> "${INST}_${JDY}_RAW";
        done
    }
    DATAOUT < <(socat udp-recv:${UDP_PORT},reuseaddr -)
相关问题