2015-11-17 3 views
0

Я использую socat, чтобы вытащить потоки ASCII через UDP и записать их в файлы. Ниже приведена одна такая строка.Изучение временной метки данных и перенаправление вывода с помощью bash

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

Каждый поток принимается уже свои данные датируемые отправителем с помощью ts (часть moreutils) с года, Джулиан день, час, мин, второй и мс. Если юлианский день изменяется, переменная JDAY на принимающей стороне не получает повторной инициализации, и кошка весело хранит данные о трубопроводах в один и тот же файл со вчерашней меткой времени.

Ниже приведен пример потока udp, принимаемого socat. Он записывается в 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 timestamp field и изменить выходной файл в соответствии с этой меткой времени?

+0

Вы должны прочитать строку в переменную Баш, разобрать переменную, чтобы определить, какой файл он должен записываться, а затем писать. Вы не можете изменить выходной файл на 'cat' после того, как вы запустили' cat'. –

+0

Возможно, название вопроса должно быть изменено на «Как перенаправить вывод команды на несколько файлов с помощью bash на основе данных временной метки?» или что-то в этом роде. –

ответ

0

Вы можете анализировать входной поток, используя встроенную программу в Баш read. Вы можете получить дополнительную информацию с помощью $ help read. Он обычно разделяет токены, используя пробелы. Если вы предоставили двухстрочный предварительный просмотр того, как выглядит ваш вывод, может быть проще помочь.

Переменные $INSTRUMENT и $JDAY должны быть определены до запуска этой команды cat, потому что cat откроет файл, прежде чем он начнет писать на него.

Если $JDAY и $INSTRUMENT, так или иначе должны быть извлечены из каждой строки, вы можете использовать следующий Баш фрагмент кода (предполагая, что строки, считываемые 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, таких как read или echo. Если вам нужно это сделать, а скорость - проблема, вы можете подумать о том, чтобы изменить свой сценарий на другой язык, который дает вам необходимую вам выразительность. Вы также можете посмотреть в 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 
} 

Рекомендуемая практика

Кроме того, я должен отметить, что это хорошая практика, чтобы окружить переменные Баш в двойных кавычках (например, в ответ), если вы собираетесь использовать их в качестве параметров имен файлов. Это особенно верно, если имена содержат пробелы или специальные символы - например, можно ожидать от имени файла, полученного из дат или времени. В тех случаях, когда ваши переменные расширяются до нуля (из-за человеческой ошибки или ошибки программирования), будут отсутствовать позиционные параметры, иногда - плохие последствия.

Рассмотрим:

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

Если $MYDIR не определено, то эта команда суммы, чтобы перезаписать file2 с содержимым file1. Контрастируйте это с cp file1 file2 "$MYDIR", который провалится раньше, потому что цель "" не существует.

Другим источником проблем, которые я вижу в вашем вопросе, являются имена переменных, за которыми следуют символы подчёркивания _, например $INSTRUMENT. Они должны быть окружены фигурными фигурными скобками { }.

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

Поскольку _ являются допустимыми символами в именах переменных, Баш попытается жадностью «клей» тем «_» после того, как INSTRUMENT, чтобы соответствовать самым длинным допустимое имя переменной возможное, что бы $INSTRUMENT_. Однако эта переменная не определена и расширяется до пустой строки, поэтому вы остаетесь с остальными, $BAR. Этот пример может быть правильно переписан как:

INSTRUMENT=6 
BAR=49 
echo ${INSTRUMENT}_${BAR} # prints 6_49 

or even better (avoiding future surprises if values ever change) 

echo "${INSTRUMENT}_${BAR}" # prints 6_49 
+0

Весь великий совет, который я претворю на практике. Вышеизложенное работает очень хорошо. Это просто и достаточно быстро, чтобы регистрировать поток 20 Гц. Благодаря! –

1

Не с cat. Вам понадобится сценарий [не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(); 
} 
+0

Я бы держался подальше от языков, требующих строительных шагов, прежде чем их можно будет использовать, особенно. для одноразовых/раздаточных сценариев. особеннос C, вы получите 100-строчную программу для токенизации строки. –

+0

@init_js Упоминание C было просто дать альтернативу. Но вы можете встроить C в скрипт [со сборкой]: 'cat << EOF c_text> /tmp/tmp.c EOF; cc /tmp/tmp.c; rm /tmp/tmp.c;/TMP/tmp'. Я делал это несколько раз под perl. C идет после '__DATA__'. В bash [или другом скрипте], скрипт bash читает _itself_ и выщипывает текст C из специальных комментариев: '# + int main()'. Компонент сборки может быть: '# = cc -o -O2 ...'. Будьте такими же простыми или сложными, как вы хотите. –

0

Это код, который работал на меня. Входной УДП поток выглядит следующим образом:

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 -) 
Смежные вопросы