it-swarm-eu.dev

Které postavy musím utéct, když používám sed ve sh skriptu?

Vezměte následující skript:

#!/bin/sh
sed 's/(127\.0\.1\.1)\s/\1/' [some file]

Pokud se to pokusím spustit v sh (dash zde), selže to kvůli závorkám, kterým je třeba uniknout. Ale já ne musím uniknout zpětným lomítkům (mezi oktety nebo v \s nebo \1). Jaké je zde pravidlo? A co když potřebuji použít {...} nebo [...]? Existuje seznam toho, co dělám, a nemusím utéct?

271
detly

Jsou zde dvě úrovně interpretace: Shell a sed.

Ve Shell je vše mezi jednoduchými uvozovkami interpretováno doslovně, s výjimkou samotných jednoduchých uvozovek. Mezi jednotlivými uvozovkami můžete efektivně mít jedinou citaci napsáním '\'' (zavřít jednoduchou citaci, jednu doslovnou jednoduchou citaci, otevřenou jednoduchou citaci).

Sed používá základní regulární výrazy . V BRE, aby se s nimi zacházelo doslova, znaky $.*[\^ musí být citováno jejich předchozím lomítkem, s výjimkou uvnitř znakových sad ([…]). Písmena, číslice a (){}+?| nesmí být citován (v některých implementacích se můžete zbavit citace některých z nich). Sekvence \(, \), \n a v některých implementacích \{, \}, \+, \?, \| a další zpětné lomítko + alfanumerika mají zvláštní význam. Můžete dostat pryč s necitováním $^ na některých pozicích v některých implementacích.

Kromě toho potřebujete zpětné lomítko před / pokud se má objevit v regexu mimo výrazy v závorkách. Jako oddělovač můžete vybrat alternativní znak napsáním, např. s~/dir~/replacement~ nebo \~/dir~p; budete potřebovat zpětné lomítko před oddělovačem, pokud ho chcete zahrnout do BRE. Pokud si vyberete postavu, která má zvláštní význam v BRE a chcete ji zahrnout doslova, budete potřebovat tři zpětná lomítka; Nedoporučuji to, protože se může v některých implementacích chovat odlišně.

Stručně řečeno, pro sed 's/…/…/':

  • Napište regex mezi jednoduchými uvozovkami.
  • Použijte '\'' skončí s jednoduchou citací v regexu.
  • Vložte zpětné lomítko před $.*/[\]^ a pouze tyto znaky (ale nikoli uvnitř výrazů v závorce). (Technicky byste neměli dát lomítko před ] ale nevím o implementaci, která zachází ] a \] jinak mimo hranaté závorky.)
  • Uvnitř závorek výraz pro - aby se s ním mělo zacházet doslova, ujistěte se, že je první nebo poslední ([abc-] nebo [-abc], ne [a-bc]).
  • Uvnitř závorek výraz pro ^ aby se s ním mělo zacházet doslova, ujistěte se, že není první (použijte [abc^], ne [^abc]).
  • Zahrnout ] v seznamu znaků, které odpovídají výrazu v závorce, z něj udělejte první znak (nebo první za ^ pro negovanou množinu): []abc] nebo [^]abc] (ne [abc]] ani [abc\]]).

V nahrazujícím textu:

  • & a \ musí být citováno tak, že jim předchází zpětné lomítko, stejně jako oddělovač (obvykle /) a nové řádky.
  • \ následované číslicí má zvláštní význam. \ následované písmenem má v některých implementacích zvláštní význam (speciální znaky) a \ následované dalšími znaky znamená \c nebo c v závislosti na implementaci.
  • S jednoduchými uvozovkami kolem argumentu (sed 's/…/…/'), použijte '\'' k vložení jediné nabídky do nahrazujícího textu.

Pokud regex nebo náhradní text pochází z proměnné Shell, pamatujte na to

  • Regex je BRE, ne doslovný řetězec.
  • V regexu je třeba nový řádek vyjádřit jako \n (což se nikdy nebude shodovat, pokud nemáte jiný sed kód přidávající znaky newline do prostoru vzoru). U některých implementací sed však nebude fungovat uvnitř výrazů v závorkách.
  • V nahrazujícím textu &, \ a nové řádky je třeba citovat.
  • Oddělovač musí být citován (ale nikoli uvnitř výrazů v závorkách).
  • Pro interpolaci použijte dvojité uvozovky: sed -e "s/$BRE/$REPL/".

Problém, který zažíváte, není způsoben interpolací a útěky Shell - je to proto, že se pokoušíte použít rozšířenou syntaxi regulárního výrazu, aniž byste předali -r nebo --regexp-extended možnost.

Změňte svoji sed linku z

sed 's/(127\.0\.1\.1)\s/\1/' [some file]

na

sed -r 's/(127\.0\.1\.1)\s/\1/' [some file]

a bude to fungovat tak, jak věřím.

Ve výchozím nastavení sed používá základní regulární výrazy (think grep style), které by vyžadovaly následující syntaxi:

sed 's/\(127\.0\.1\.1\)[ \t]/\1/' [some file]
45
R Perrin

Pokud nechcete interpolovat proměnnou Shell do výrazu sed, použijte jednoduché uvozovky pro celý výraz, protože způsobí, že vše mezi nimi bude interpretováno tak, jak je, včetně zpětných lomítek.

Pokud tedy chcete, aby sed viděl s/\(127\.0\.1\.1\)\s/\1/, vložte do něj jednoduché uvozovky a prostředí se nedotkne závorek nebo zpětných lomítek. Pokud potřebujete interpolovat proměnnou Shell, vložte pouze tuto část do dvojitých uvozovek. Např.

sed 's/\(127\.0\.1\.1\)/'"$ip"'/'

To vám ušetří potíže s pamatováním si, které metaznaky Shell neuniknou uvozovkami.

18
Kyle Jones

Myslím, že stojí za zmínku, že zatímco sed je založen na standardu POSIX, který specifikuje podporu pouze pro základní regulární výraz (BRE), ve skutečnosti existují dvě různé verze příkazu sed - BSD (Mac OS) a GNU (Linux distros) . Každá verze implementuje podobné i jedinečné rozšíření standardu POSIX a může ovlivnit funkčnost sedu na různých platformách. Výsledkem je, že správná syntaxe příkazu sed, fungující podle očekávání na jednom systému, by se ve skutečnosti mohla překládat do úplně jiných výsledků na jiném. To může vést k neočekávanému chování s ohledem na použití uniklých a speciálních znaků.

Tato rozšíření standardu POSIX mají tendenci být více převládající ve verzi GNU sed, často poskytují pohodlí méně striktního formátování, zejména ve srovnání s verzí BSD. Přestože GNU sed umožňuje funkčnost některých speciálních znaků, stále ve skutečnosti nejsou kompatibilní s POSIX. Navíc jediný skutečný rozdíl mezi základním a rozšířeným regulárním výrazem (ERE) v rámci GNU sed je chování následujících speciálních znaků:

„?“, „+“, Závorky, rovnátka („{}“) a „|“

I když to může být případ, některé speciální znaky mají na sedech BSD sed omezenou nebo žádnou podporu, například „|“, „?“ A „+“, protože se více drží syntaktických standardů POSIX. Zahrnutí těchto znaků způsobem podobným tomu, který byl použit v GNU sed, bude mít často za následek problémy s přenositelností a funkčností skriptů využívajících sed. Za zmínku také stojí, že syntaxe POSIX BRE nedefinuje význam pro některé únikové sekvence, zejména:\|, +,\?, `,\',\<,>,\b,\B,\w a\W ,.

Pro ty, kteří používají verzi sedu BSD/Mac OS, může být emulace chování některých speciálních znaků trochu složitější, ale ve většině případů to lze udělat. Například + by mohlo být emulováno způsobem kompatibilním s POSIX, jako je tento: {1,} a \? by vypadalo takto: {0,1} Řídicí znakové sekvence však obvykle nejsou podporovány. Pokud je to vůbec možné, je nejjednodušší využít GNU sed, ale pokud potřebujete funkčnost na obou platformách, nezapomeňte používat pouze funkce POSIX, abyste zajistili přenositelnost. Pokud jste uživatelem Mac a chtěli byste využít výhodu GNU sed na rozdíl od BSD sed, můžete zkusit nainstalovat Homebrew a stáhnout GNU sed pomocí příkazového řádku s: $ vařit instalaci gnu-sed.

Aby bylo možné věci zabalit, rozdíly ve verzi mohou skutečně určovat, jak může vypadat správná syntaxe nebo jaké znaky jsou nutné k úniku. Doufám, že to poskytne nějaký další kontext pro počáteční otázku, jakož i pro přijatou odpověď, a pomůže ostatním zvážit, jak by měli postupovat, na základě konečného cíle jejich použití skriptů a příkazů.

0
forthelulz