RegEx для сопоставления строки до года1 - PullRequest
1 голос
/ 06 мая 2019

У меня есть имена каталогов, которые включают номера года. Я хочу разделить их на переменные, которые находятся до номера года:

Введите:

Holidays.uS.2019.bla.bla
Holidays.ca.old.2017.bla.bla
Holidays.2015.bla.bla.bla
Holidays.1.2.3.4.at.old.1999.bla.bla.bla.bla

Год не всегда один и тот же, но он всегда состоит из 4 цифр.

Мне всегда нужно все до года.

Для входа:

Holidays.ca.old.2017.bla.bla

Выход:

Holidays.ca.old

Попытка

set name Holidays.ca.old.2017.bla.bla
set numbers [regexp -all -inline {[0-9]+} $name]

Выходные данные из моего кода - номер года, а иногда и другие неправильные числа.

Я использую это в tcl, и он отлично работает для меня:

set name_split [regsub {\.[0-9]{4}\y.*} $name ""]

Мне все еще нужно это для скрипта bash, как его использовать?

Это действительно не работает в bash:

name_split=$(echo $name | {\.[0-9]{4}\y.*}

Ответы [ 5 ]

2 голосов
/ 06 мая 2019

Вы можете использовать sed:

name="Holidays.uS.2019.bla.bla"
name_split="$(sed 's/\.[0-9]\{4\}\>.*//' <<< $name)"
echo $name_split

Вывод: Holidays.uS, см. онлайн sed демо .

Примечание: если после года должна быть точка, имеет смысл сопоставлять ее, а не полагаться на границу слова:

name_split="$(sed 's/\.[0-9]\{4\}\..*//' <<< $name)"
                                 ^^ 

Команда sed здесь означает:

  • s/ - заменить:
  • \. - точка
  • [0-9]\{4\} - четыре цифры
  • \> - конечная граница слова
  • .* - остаток строки

с пустой строкой.

1 голос
/ 06 мая 2019

Вы можете добавить дополнительные границы в вашей команде sed, просто чтобы быть в безопасности и передать желаемые символы:

Код

#!/bin/bash
STRING="Holidays.1.2.3.4.at.old.1999.bla.bla.bla.bla"
MATCH="$(sed 's/\([A-z0-9\.]*\)\(\.[0-9]\{4\}.*\)$/\1/' <<< $STRING)"
echo $MATCH

Вывод

Holidays.1.2.3.4.at.old

Специальные символы

Если вы не хотите передавать специальные символы, это выражение безопасно:

enter image description here

График

Этот график показывает, как это работает

enter image description here

Производительность

В этом фрагменте показана производительностьвыражение с миллионом раз повторить.

repeat = 1000000;
start = Date.now();

for (var i = repeat; i >= 0; i--) {
	var string = "Holidays.1.2.3.4.at.old.1999.bla.bla.bla.bla";
	var regex = /^([A-z0-9\.]*)(\.[0-9]{4}.*)/g;
	var match = string.replace(regex, "$1");
}

end = Date.now() - start;
console.log("YAAAY! \"" + match + "\" is a match ? ");
console.log(end / 1000 + " is the runtime of " + repeat + " times benchmark test. ? ");
0 голосов
/ 06 мая 2019

Вы можете сделать это с помощью расширения параметра :

$ str='Holidays.1.2.3.4.at.old.1999.bla.bla.bla.bla'
$ echo "${str%.[[:digit:]][[:digit:]][[:digit:]][[:digit:]]*}"
Holidays.1.2.3.4.at.old

. При этом удаляется точка, за которой следуют четыре цифры и затем что-нибудь до конца строки (подстановочный знак *)).

0 голосов
/ 06 мая 2019

Вот еще один способ, который не использует sed:

#!/bin/bash
test_str="Holidays.ca.old.2017.bla.bla"
reg_ex='^(.*)\.([0-9]{4})' # Easy to read reg_ex vs sed reg_ex gibberish
if [[ $test_str =~ $reg_ex ]]
then
  echo "1: ${BASH_REMATCH[1]}"
  echo "2: ${BASH_REMATCH[2]}"
fi

Вывод:

1: Holidays.ca.old

2: 2017

0 голосов
/ 06 мая 2019

Здесь есть опция, использующая sed:

echo "Holidays.ca.old.2017.bla.bla" | sed 's/^\(.*\)\.[0-9]\{4\}.*$/\1/';

Holidays.ca.old

Регулярное выражение просто фиксирует все до точки последняя , за которой следует год из четырех цифр, а затем заменяется только на захваченныйколичество:

(.*)        match and capture everything up until
\.[0-9]{4}  a literal dot, followed by a four digit year
.*          consume the remainder of the input
...