Проверьте, соответствует ли строка регулярному выражению в Bash script

Один из аргументов, который получает мой script, - это дата в следующем формате: yyyymmdd.

Я хочу проверить, получаю ли я действительную дату в качестве ввода.

Как я могу это сделать? Я пытаюсь использовать регулярное выражение, например: [0-9]\{\8}

Ответ 1

Ты можешь сказать:

[[ $date =~ ^[0-9]{8}$ ]] && echo "yes"

Или точнее:

[[ $date =~ ^[0-9]{4}(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])$ ]] && echo "yes"
#           |^^^^^^^^ ^^^^^^ ^^^^^^  ^^^^^^ ^^^^^^^^^^ ^^^^^^ |
#           |   |     ^^^^^^^^^^^^^  ^^^^^^^^^^^^^^^^^^^^^^^^ |
#           |   |          |                   |              |
#           |   |           \                  |              |
#           | --year--   --month--           --day--          |
#           |          either 01...09      either 01..09     end of line
# start of line            or 10,11,12         or 10..29
#                                              or 30, 31

То есть вы можете определить регулярное выражение в bash, соответствующее желаемому формату. Таким образом, вы можете сделать:

[[ $date =~ ^regex$ ]] && echo "matched" || echo "did not match"

Обратите внимание, что это основано на решении Алекса-Даниэля Якименко, приведенном в разделе Проверка формата ввода данных пользователем в bash.


В других оболочках вы можете использовать grep. Если ваша оболочка совместима с POSIX, сделайте

(echo "$date" | grep -Eq  ^regex$) && echo "matched" || echo "did not match"

В рыбе, которая не соответствует POSIX, вы можете сделать

echo "$date" | grep -Eq "^regex\$"; and echo "matched"; or echo "did not match"

Ответ 2

В версии bash версии 3 вы можете использовать оператор '= ~':

if [[ "$date" =~ "[0-9]\{8\}" ]]; then
    echo "Valid date"
else
    echo "Invalid date"
fi

Ссылка: http://tldp.org/LDP/abs/html/bashver3.html#REGEXMATCHREF

ПРИМЕЧАНИЕ. Цитирование в операторе сопоставления в двойных скобках, [[]], больше не требуется в отношении bash версии 3.2

Ответ 3

Хороший способ проверить, является ли строка правильной датой, использовать дату команды:

if date -d "${DATE}" >/dev/null 2>&1
then
  # do what you need to do with your date
else
  echo "${DATE} incorrect date" >&2
  exit 1
fi

Ответ 4

Я бы использовал expr match вместо =~:

expr match "$date" "[0-9]\{8\}" >/dev/null && echo yes

Это лучше, чем принятый в настоящее время ответ на использование =~ потому что =~ также будет соответствовать пустым строкам, что, по-моему, не должно. Предположим, что badvar не определен, тогда [[ "1234" =~ "$badvar" ]]; echo $? [[ "1234" =~ "$badvar" ]]; echo $? дает (неверно) 0, в то время как expr match "1234" "$badvar" >/dev/null; echo $? expr match "1234" "$badvar" >/dev/null; echo $? дает правильный результат 1.

Мы должны использовать >/dev/null чтобы скрыть выходное значение expr match, которое представляет собой количество совпавших символов или 0, если совпадение не найдено. Обратите внимание, что его выходное значение отличается от его состояния выхода. Статус выхода равен 0, если совпадение найдено, или 1 в противном случае.

Как правило, синтаксис для expr:

expr match "$string" "$lead"

Или же:

expr "$string" : "$lead"

где $lead - это регулярное выражение. Его exit status будет истинным (0), если lead соответствует лидирующему фрагменту string (есть имя для этого?). Например, expr match "abcdefghi" "abc" выходит из true, но expr match "abcdefghi" "bcd" выходит из false. (Благодарю @Carlo Wood за указание на это.

Ответ 5

В тех случаях, когда использование регулярного выражения может быть полезным для определения правильности последовательности символов даты, его нельзя легко определить, является ли дата действительной. Следующие примеры передают регулярное выражение, но все они являются недопустимыми датами: 20180231, 20190229, 20190431

Поэтому, если вы хотите проверить правильность формата строки даты (пусть назовет ее datestr), лучше проанализировать ее с date и запросить date чтобы преобразовать строку в правильный формат. Если обе строки идентичны, у вас есть правильный формат и действительная дата.

if [[ "$datestr" == $(date -d "$datestr" "+%Y%m%d" 2>/dev/null) ]]; then
     echo "Valid date"
else
     echo "Invalid date"
fi