Я уже нашла подобный чехол проверьте В моем случае имя файла: backup_20240827000025.sql, мне нужно извлечь символы с помощью sed - 20240827. Для этой строки я пытаюсь выполнить sed с регулярным выражением в centos7 sed (GNU sed) 4.2.2.
string=backup_20240827000025.sql
echo $string | sed -r 's/\./[a-z]{6}\w\([0-9]{8}\)[0-9]+\.[a-z]{3}/\1/p'
Error:
sed: -e expression #1, char 49: invalid reference \1 on `s' command's RHS
Если я удалю \ обратную косую черту, окружающую круглые скобки
\./[a-z]{6}\w([0-9]{8})[0-9]+\.[a-z]{3},
затем я получаю свою строку обратно в том виде, в котором она была:
./backup_20240827000025.sql
Однако, если я использую Python и это регулярное выражение
'\./[a-z]{6}\w([0-9]{8})[0-9]+\.[a-z]{3}'
он работает хорошо и выдает результат типа 20240827. Может ли кто-нибудь помочь с этим? Спасибо.
🤔 А знаете ли вы, что...
Bash поддерживает переменные, которые можно использовать для хранения данных и параметров скрипта.
Можете ли вы попробовать более простое регулярное выражение?
$ string=backup_20240827000025.sql
$ echo "$string" | sed -r 's/backup_([0-9]{8}).*/\1/'
20240827
Ваша входная строка не начинается с ./
, поэтому ваше регулярное выражение вообще не соответствует. Аналогично, \*
соответствует буквальной звездочке, но во входной строке ее нет.
Непонятно, зачем удваивать звездочки и прочие квантификаторы; это синтаксические ошибки.
\w
не является переносимым, хотя, вероятно, поддерживается в CentOS.
С помощью простого, хорошо сформированного регулярного выражения вы получаете
bash$ echo "backup_20240827000025.sql" |
> sed -r -n 's/[a-z]{6}[^a-z0-9]*([0-9]{8})[0-9]+\.[a-z]{3}/\1/p'
20240827
Устранение ошибки ОП...
Без опции -r
вы должны указать sed
, когда круглые скобки должны использоваться для обозначения группы захвата. Вы делаете это, экранируя круглые скобки, окружающие группу захвата, например:
$ x=abcdef
$ sed 's/.*\(cd\).*/XX\1XX/' <<< "${x}"
XXcdXX
Если вы используете опцию -r
, вам больше не нужно экранировать скобки (т. е. скобки рассматриваются как специальные символы), например:
$ sed -r 's/.*(cd).*/XX\1XX/' <<< "${x}"
XXcdXX
Фактически, если вы используете опцию -r
, вам не нужно экранировать круглые скобки, заключающие в себя группу захвата. Когда вы экранируете скобки, вы сообщаете sed
, что это буквальные скобки (т. е. не рассматриваете их как специальные символы). В случае OP комбинация -r
и экранированных скобок оставляет команду sed
без групп захвата, что, в свою очередь, означает, что обозначение \1
относится к несуществующей/недействительной ссылке:
$ sed -r 's/.*\(cd\).*/XX\1XX/' <<< "${x}"
^^ ^^ ^^ ^^
sed: -e expression #1, char 20: invalid reference \1 on `s' command's RHS
Итак, вызов OP sed
должен выглядеть так:
sed -r 's/\./[a-z]{6}\w([0-9]{8})[0-9]+\.[a-z]{3}/\1/p'
^ ^ ---- parents are not escaped
Но теперь ОП сталкивается с другими проблемами...
\./[a-z]{6}
говорит, что мы ищем строку, состоящую из литералов .
(точка) + /
(косая черта) + [a-z]{6}
(6 символов нижнего регистра). Но образцы данных OP не включают буквальные символы .
+ /
, поэтому мы не видим никаких изменений:
$ echo "$string" | sed -r 's/\./[a-z]{6}\w([0-9]{8})[0-9]+\.[a-z]{3}/\1/p'
backup_20240827000025.sql
Наша первая попытка решить эту последнюю проблему — удалить \./
, но затем мы столкнулись с новой проблемой:
$ echo "$string" | sed -r 's/[a-z]{6}\w([0-9]{8})[0-9]+\.[a-z]{3}/\1/p'
20240827
20240827
Двойной выход?
По умолчанию sed
автоматически распечатает пространство шаблонов (первый 20240827
), в то время как операция /p
говорит о необходимости снова явно распечатать пространство шаблонов (2-й 20240827
).
Чтобы ограничить вывод одной копией пространства шаблонов, у нас есть несколько вариантов:
######
# add '-n' to suppress automatic printing of the pattern space
echo "$string" | sed -r -n 's/[a-z]{6}\w([0-9]{8})[0-9]+\.[a-z]{3}/\1/p'
^^
######
# or remove the 'p' operation
echo "$string" | sed -r 's/[a-z]{6}\w([0-9]{8})[0-9]+\.[a-z]{3}/\1/'
Оба из них будут генерировать единое пространство шаблонов:
20240827
Следует иметь в виду один момент, связанный с производительностью...
При передаче вывода одной команды на вторую команду оболочка запускает подпроцесс для второй команды. Создание подпроцесса требует относительно больших затрат ресурсов и времени и становится весьма заметным при создании чрезмерного количества подоболочек.
Хотя создание OP одной подоболочки не будет заметно в командной строке, хорошей практикой является выработать привычку удалять ненужные подоболочки, когда это возможно.
Это может заключаться в перестановке текущей команды или даже в использовании других функций оболочки.
Одна из идей заключалась в том, чтобы исключить ненужные echo
в пользу строки здесь:
$ sed -r 's/[a-z]{6}\w([0-9]{8})[0-9]+\.[a-z]{3}/\1/' <<< "$string"
20240827 ^^^^^^^^^^^^^
В качестве альтернативы, поскольку OP, похоже, работает со строками фиксированной длины, мы можем использовать функцию bash's
подстроки ("${var:start:length}"
):
$ echo "${string:7:8}"
ПРИМЕЧАНИЕ: первый символ находится на позиции 0
.
Если мы имеем дело с переменным количеством символов перед _
, мы можем использовать подстановку параметра :
$ dt = "${string#*_}" # strip off leading <string1>_
$ typeset -p dt
declare -- dt = "20240827000025.sql"
$ echo "${dt:0:8}" # now use the substring feature
20240827
В примере OP мы просто хотим напечатать подстроку на стандартный вывод. Если OP необходимо зафиксировать подстроку в переменной, то у нас возникает еще одна проблема с производительностью...
Следующее также создаст подоболочку ($( command ... )
):
$ newvar=$( sed -r 's/[a-z]{6}\w([0-9]{8})[0-9]+\.[a-z]{3}/\1/' <<< "$string" )
$ typeset -p newvar
declare -- newvar = "20240827"
Хорошей новостью является то, что мы можем повторно использовать решения по замене параметров и подстрокам, чтобы устранить эту дополнительную подоболочку.
$ newvar = "${string:7:8}"
$ typeset -p newvar
declare -- newvar = "20240827"
$ dt = "${string#*_}"
$ newvar = "${dt:0:8}"
$ typeset -p newvar
declare -- newvar = "20240827"