Як переконатися, що рядок, інтерпольований у заміну sed, уникне всіх метахаргів


21

У мене є сценарій, який читає текстовий потік і генерує файл команд sed, який згодом запускається sed -f. Створені команди sed є такими:

s/cid:image002\.gif@01CC3D46\.926E77E0/https:\/\/mysite.com\/files\/1922/g
s/cid:image003\.gif@01CC3D46\.926E77E0/https:\/\/mysite.com\/files\/1923/g
s/cid:image004\.jpg@01CC3D46\.926E77E0/https:\/\/mysite.com\/files\/1924/g

Припустимо, що сценарій, який генерує sedкоманди, має щось на зразок:

while read cid fileid
do
    cidpat="$(echo $cid | sed -e s/\\./\\\\./g)"
    echo 's/'"$cidpat"'/https:\/\/mysite.com\/files\/'"$fileid"'/g' >> sedscr
done

Як я можу вдосконалити скрипт, щоб гарантувати, що всі метареакції регулярних виразів у cidрядку будуть уникнуті та інтерпольовані належним чином?

Відповіді:


24

Щоб уникнути змінних, які будуть використовуватися в лівій і правій частині sкоманди в sed(тут $lhsі $rhsвідповідно), слід зробити:

escaped_lhs=$(printf '%s\n' "$lhs" | sed 's:[][\/.^$*]:\\&:g')
escaped_rhs=$(printf '%s\n' "$rhs" | sed 's:[\/&]:\\&:g;$!s/$/\\/')

sed "s/$escaped_lhs/$escaped_rhs/"

Зверніть увагу, що $lhsне може містити символ нового рядка.

Тобто на LHS виходять усі оператори regexp ( ][.^$*), сам символ утечі ( \) та роздільник ( /).

На RHS вам потрібно лише втекти &, роздільник, зворотний косий риси та символ нового рядка (що ви робите, вставляючи зворотній косу риску в кінці кожного рядка, за винятком останнього ( $!s/$/\\/)).

Це передбачає, що ви використовуєте /як роздільник у своїх sed sкомандах, і ви не вмикаєте розширені RE-адреси за допомогою -r(GNU sed/ ssed/ ast/ busybox sed) або -E(BSD ast, останні GNU, нещодавно зайнятої скриньки ) або PCRE з -R( ssed) або доповненими REs з -A/ -X( ast), які всі мають додаткових операторів з РЕ.

Кілька основних правил при роботі з довільними даними:

  • Не використовуйте echo
  • цитуйте свої змінні
  • розглянути вплив локалі (особливо його набір символів: це важливо, щоб минути sed команди виконуються в тій же місцевості, що і sedкоманди , використовуючи вцілілі рядки (і з тієї ж sedкомандою), наприклад)
  • не забувайте про символ нового рядка (тут ви можете перевірити, чи $lhsмістить він, і вжити заходів).

Іншим варіантом є використання perlзамість sedі передавання рядків у оточенні та використання операторів \Q/ \E perlregexp для буквального взяття рядків:

A="$lhs" B="$rhs" perl -pe 's/\Q$ENV{A}\E/$ENV{B}/g'

perl(за замовчуванням) набір символів локалі не вплине, оскільки у вищезазначеному він розглядає лише рядки як масиви байтів, не піклуючись про те, які символи (якщо такі є) вони можуть представляти для користувача. З sed, ви могли б досягти того ж, зафіксувавши локаль на Cз LC_ALL=Cдля всіх sedкоманд (хоча це також вплине на мову повідомлень про помилки, якщо такі є).


Що робити, якщо мені потрібно уникнути подвійних лапок?
Менон

@ Менон, подвійні лапки не особливі sed, вам не потрібно уникати їх.
Стефан Шазелас

Це не може бути використане для відповідності шаблонів за допомогою підстановки, чи не так?
Менон

@ Менон, ні, відповідність шаблону підстановки як з find's -nameвідрізняється від регулярних виразів. Там вам потрібно лише втекти ?, *[
нахилити нахил
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.