2014-02-20 4 views
0

У меня есть данные с этим форматом даты и времени в Баш:Match формат даты и времени с Bash REGEX

28/11/13 06:20:05 (дд/мм/гггг чч: мм: сс)

Мне нужно переформатировать нравится:

2013-11-28 06:20:05 (формат даты и времени MySQL)

Я использую следующее регулярное выражение:

regex='([0-9][0-9])/([0-9][0-9])/([0-9][0-9])\s([0-9][0-9]/:[0-9][0-9]:[0-9][0-9])' 

if [[$line=~$regex]] 
then 
    $line='20$3-$2-$1 $4'; 
fi 

Это приводит к ошибке:

./filename: line 10: [[09:34:38=~([0-9][0-9])/([0-9][0-9])/([0-9][0-9])\s([0-9][0-9]/:[0-9][0-9]:[0-9][0-9])]]: No such file or directory 

UPDATE:

Я хочу прочитать этот файл "построчно", разобрать его и вставить данные в базе данных MySQL:

'filenameX':

27/11/13 12:20:05 9984 2885 260 54 288 94 696 1852 32 88 27 7 154 
27/11/13 13:20:05 9978 2886 262 54 287 93 696 1854 32 88 27 7 154 
27/11/13 14:20:05 9955 2875 262 54 287 93 696 1860 32 88 27 7 154 
27/11/13 15:20:04 9921 2874 261 54 284 93 692 1868 32 88 27 7 154 
27/11/13 16:20:09 9896 2864 260 54 283 92 689 1880 32 88 27 7 154 
27/11/13 17:20:05 9858 2858 258 54 279 92 683 1888 32 88 27 7 154 
27/11/13 18:20:04 9849 2853 258 54 279 92 683 1891 32 88 27 7 154 
27/11/13 19:20:04 9836 2850 257 54 279 93 683 1891 32 88 27 7 154 
27/11/13 20:20:05 9826 2845 257 54 279 93 683 1892 32 88 27 7 154 
27/11/13 21:20:05 9820 2847 257 54 278 93 682 1892 32 88 27 7 154 
27/11/13 22:20:04 9810 2844 257 54 277 93 681 1892 32 88 27 7 154 
27/11/13 23:20:04 9807 2843 257 54 276 93 680 1892 32 88 27 7 154 
28/11/13 00:20:05 9809 2843 257 54 276 93 680 1747 29 87 17 6 139 
28/11/13 01:20:04 9809 2842 257 54 276 93 680 1747 29 87 17 6 139 
28/11/13 02:20:05 9809 2843 256 54 276 93 679 1747 29 87 17 6 139 
28/11/13 03:20:04 9808 2842 256 54 276 93 679 1747 29 87 17 6 139 
28/11/13 04:20:05 9808 2842 256 54 276 93 679 1747 29 87 17 6 139 
28/11/13 05:20:39 9807 2842 256 54 276 93 679 1747 29 87 17 6 139 
28/11/13 06:20:05 9804 2840 256 54 276 93 679 1747 29 87 17 6 139 

Сценарий:

#!/bin/bash 

echo "Start!" 

while IFS='  ' read -ra ADDR; 
do 
    for line in $(cat results) 
    do 
     regex='([0-9][0-9])/([0-9][0-9])/([0-9][0-9]) ([0-9][0-9]:[0-9][0-9]:[0-9]$ 
     if [[ $line =~ $regex ]]; then 
     $line="20${BASH_REMATCH[3]}-${BASH_REMATCH[2]}-${BASH_REMATCH[1]} ${BASH_REMATCH[4]}" 
     fi 
     echo "insert into table(time, total, caracas, anzoategui) values('$line', '$line', '$line', '$line', '$line');" 
    done | mysql -user -password database; 
done < filenameX 

Результаты:

время | всего | каракас | anzoategui | 0000-00-00 00:00:00 | 9 | 9 | 9 |
2027-11-13 00:00:00 | 15 | 15 | 15 |

+3

Откуда эти данные? Потому что 'date' имеет много вариантов форматирования, которые облегчат вашу жизнь. – fedorqui

ответ

2

Примечание: Ответ на этот вопрос был принят на основе фиксации основанный на bash подход в ОП. Для более простого решения, основанного на awk, см. Последний раздел этого ответа.

Попробуйте следующее:

line='28/11/13 06:20:05' # sample input 

regex='([0-9][0-9])/([0-9][0-9])/([0-9][0-9]) ([0-9][0-9]:[0-9][0-9]:[0-9][0-9])' 

if [[ $line =~ $regex ]]; then 
    line="20${BASH_REMATCH[3]}-${BASH_REMATCH[2]}-${BASH_REMATCH[1]} ${BASH_REMATCH[4]}" 
fi 

echo "$line" # -> '2013-11-28 06:20:05' 

А почему ваш код не работает:

  • Как @anubhava отметил, что вам нужно по крайней мере, 1 место справа от [[ и к слева от ]].
  • Работает ли \s в bash regex, зависит от платформы (Linux: да, OSX: нет), поэтому здесь доступно более сильное, буквальное пространство.
  • Неправильное присвоение переменной ($line = ...) - когда присвоило переменной переменной, никогда не префиксное имя переменной $.
  • Ваши обратные ссылки были неверными ($1, ...): для ссылки на группы захвата (подвыражения) в регулярном выражении bash вы должны использовать специальную переменную массива ${BASH_REMATCH[@]}; ${BASH_REMATCH[0]} содержит всю строку, которая соответствует, ${BASH_REMATCH[1]} содержит то, что соответствует первой группе захвата, и так далее; напротив, $1, $2, ... ссылаются на аргумент 1, 2, ..., переданный скрипту или функции оболочки.

Update, обратиться обновленный на вопрос OP еще:

Я думать следующий делает то, что вы хотите:

# Read input file and store each col. value in separate variables. 
while read -r f1 f2 f3 f4 f5 f6 f7 f8 f9 f10 f11 f12 f13 f14 f15; do 

    # Concatenate the first 2 cols. to form a date + time string. 
    dt="$f1 $f2" 

    # Parse and reformat the date + time string. 
    regex='([0-9][0-9])/([0-9][0-9])/([0-9][0-9]) ([0-9][0-9]:[0-9][0-9]:[0-9][0-9])' 
    if [[ "$dt" =~ $regex ]]; then 
     dt="20${BASH_REMATCH[3]}-${BASH_REMATCH[2]}-${BASH_REMATCH[1]} ${BASH_REMATCH[4]}" 
    fi 

    # Echo the SQL command; all of them are piped into a `mysql` command 
    # at the end of the loop. 
    # !! Fill the $f<n> variables in as needed - I don't know which ones you need. 
    # !! Make sure the number column name matches the number of values. 
    # !! Your original code had 4 column names, but 5 values, causing an error. 
    echo "insert into table(time, total, caracas, anzoategui) values('$dt', '$f3', '$f4', '$f5');" 

done < filenameX | mysql -user -password database 

Запоздалая мысль: выше решение b что касается усовершенствований кода OP; Ниже обтекаемое решение, которое представляет собой один вкладыш на основе awk (распространения через несколько строк для удобства чтения - кончик шляпу @twalberg для AWK на основе даты переформатирования):

awk -v sq=\' '{ 
split($1, tkns, "/"); 
dt=sprintf("20%s-%s-%s", tkns[3], tkns[2], tkns[1]); 
printf "insert into table(time,total,caracas,anzoategui) values(%s,%s,%s,%s);", 
    sq dt " " $2 sq, sq $3 sq, sq $4 sq, sq $5 sq 
}' filenameX | mysql -user -password database 

Примечание: Для того, чтобы процитировать внутри программы awk простая цитата передается через переменную sq (-v sq=\').

+0

@nandophillips: добавлено объяснение – mklement0

+0

@nandophillips: см. Мой обновленный ответ. – mklement0

+0

Где находится сборник с базой данных? пользователь, пароль, база данных ... – nandophillips

0

Пространства являются обязательными в BASH, так что используйте:

[[ "$line" =~ $regex ]] && echo "${line//\//-}" 

Кроме того, вы не можете использовать \s в BASH, так что используйте это регулярное выражение:

regex='([0-9][0-9])/([0-9][0-9])/([0-9][0-9]) ([0-9][0-9]:[0-9][0-9]:[0-9][0-9])' 
+0

Решает проблему, но не форматирует дату. – nandophillips

+0

ok см. Обновленный ответ. – anubhava

+0

Он по-прежнему не перестраивает и не отбрасывает '2028-11-13 00:00:00' :( – nandophillips

1

Perl удобен здесь.

dt="28/11/13 06:20:05" 
perl -MTime::Piece -E "say Time::Piece->strptime('$dt', '%d/%m/%y %T')->strftime('%Y-%m-%d %T')" 
2013-11-28 06:20:05 
+0

+1 для этого многоплатформенного решения - хотя использование в цикле может вызвать проблемы с производительностью; на OSX (но, к сожалению, не Linux) вы можете использовать 'date' с' -f' для описания формата ввода: 'date -j -f '% d /% m /% y% T' + '% Y -% m-% d% T '"$ dt". Что касается производительности: запуск преобразования 100 раз приводит к следующим временам (абсолютное время не важно, но их соотношение есть): Perl: 3.55s; дата (OSX): 0,51 с; Регулярное выражение баша: 0,34 с. – mklement0

1

Это делает трюк без чрезмерно сложных регулярных выражений вызовов:

echo "28/11/13 06:20:05" | awk -F'[/ ]' \ 
    '{printf "20%s-%s-%s %s\n", $3, $2, $1, $4}' 

Или, как это было предложено @fedorqui в комментариях, если источник вашей временной метки является date, вы можете просто дать ему параметры форматирования, которые вы хотите ...

+0

+1; re 'date': формат даты ввода OP не работает с' date -d' в Linux (дата GNU 8.13, даже с локалями, где используется этот формат), но в OSX вы можете использовать следующее, потому что формат ввода может быть указано явно: 'date -j -f '% d /% m /% y% T' + '% Y-% m-% d% T' '28/12/13 06: 20: 05''. – mklement0

Смежные вопросы