GNU/Linux >> Znalost Linux >  >> Linux

Jak zajistit, aby řetězec interpolovaný do `sed` substituce unikl všem metacharům?

Mám skript, který čte textový proud a generuje soubor příkazů sed, který se později spustí pomocí sed -f . Vygenerované příkazy sed jsou podobné:

s/cid:[email protected]/https://mysite.com/files/1922/g
s/cid:[email protected]/https://mysite.com/files/1923/g
s/cid:[email protected]/https://mysite.com/files/1924/g

Předpokládejme skript, který generuje sed příkazy je něco jako:

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

Jak mohu zlepšit skript, abych zajistil všechny metaznaky regulárního výrazu v cid jsou escapovány a správně interpolovány?

Přijatá odpověď:

Chcete-li uniknout proměnným, které mají být použity na levé a pravé straně s příkaz v sed (zde $lhs a $rhs respektive), udělali byste:

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

sed "s/$escaped_lhs/$escaped_rhs/"

Všimněte si, že $lhs nemůže obsahovat znak nového řádku.

To znamená, že na LHS unikněte všem operátorům regulárních výrazů (][.^$* ), samotný únikový znak ( ) a oddělovač (/ ).

Na RHS stačí uniknout & , oddělovač, zpětné lomítko a znak nového řádku (což provedete vložením zpětného lomítka na konec každého řádku kromě posledního ($!s/$/\/ )).

To předpokládá, že používáte / jako oddělovač ve vašem sed s a že nepovolíte Extended REs s -r (GNU sed /ssed /ast /busybox sed ) nebo -E (BSD, ast , nedávný GNU, nedávný busybox) nebo PCRE s -R (ssed ) nebo Augmented REs s -A /-X (ast ), které mají všechny další operátory RE.

Několik základních pravidel pro práci s libovolnými daty:

  • Nepoužívejte echo
  • uveďte své proměnné
  • vezměte v úvahu dopad národního prostředí (zejména jeho znakové sady:je důležité, aby escapování sed příkazy se spouštějí ve stejném národním prostředí jako sed pomocí příkazu escaped řetězců (a se stejným sed například příkaz)
  • nezapomeňte na znak nového řádku (zde možná budete chtít zkontrolovat, zda $lhs obsahuje jakékoli a proveďte akci).

Další možností je použít perl místo sed a předejte řetězce v prostředí a použijte Q /E perl Operátory regulárních výrazů pro doslovné přebírání řetězců:

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

perl (ve výchozím nastavení) nebude ovlivněna znakovou sadou národního prostředí, protože ve výše uvedeném považuje řetězce pouze za pole bajtů, aniž by se starala o to, jaké znaky (pokud existují) mohou pro uživatele představovat. Pomocí sed , můžete toho dosáhnout tím, že upravíte národní prostředí na C s LC_ALL=C pro všechny sed příkazy (i když to také ovlivní jazyk chybových zpráv, pokud existují).

Související:Používání sed se speciálními znaky?
Linux
  1. Jak nahradit řetězec v souboru (souborech)?

  2. Jak nahradit řetězec řetězcem obsahujícím lomítko se Sed?

  3. Sed:Smazat všechny výskyty řetězce kromě prvního?

  1. Jak najít všechny soubory, které neobsahují textový řetězec?

  2. Jak nahradit řetězec ve více souborech v příkazovém řádku linuxu

  3. Jak přesunout všechny soubory včetně skrytých souborů do nadřazeného adresáře přes *

  1. Jak určíte skutečný příkaz, který do vás vstupuje?

  2. Jak odstranit všechny soubory začínající určitým řetězcem v Linuxu

  3. Jak si mohu být jistý, že jsem zapojil zařízení do portu USB 3?