Dlaczego muszę uciec znakom regularnym w sed, aby interpretować je jako znaki regularne?

11

Wydaje się na przykład
cat sed_data.txt | sed 's/\b[0-9]\{3\}\b/NUMBER/g'
, że musi uciec znaków w celu utworzenia wyrażenia regularnego. W tym przypadku musiałem uciec z nawiasów klamrowych, aby być interpretowanym kilka razy.
Dlaczego? Spodziewałem się, że wszystko będzie postacią regex, chyba że ucieknie. Tj. Wręcz przeciwnie.

Jim
źródło
Był taki post o wyszukiwaniu w Vimie, który w pewnym stopniu obejmuje to pytanie, krótka wersja brzmi „to zależy od implementacji polecenia” ... unix.stackexchange.com/questions/90345/…
Drav Sloan
@DravSloan: Nie jestem pewien, czy jest taki sam. W Vimie domyślnie wyszukujesz tekst i musisz uciec, aby wyszukać wyrażenie regularne, ale w tym przypadku format s/regex//gjuż oczekuje wyrażenia regularnego i oczekiwałbym, że to tekst będzie potrzebował do ucieczki
Jim

Odpowiedzi:

14

Wynika to z faktu, że sedwykorzystuje POSIX BRE (podstawowe wyrażenia regularne) w przeciwieństwie do ERE (rozszerzone wyrażenia regularne), do których prawdopodobnie przywykłeś od Perla lub znajomych.

Ze strony podręcznika sed(1):

REGULAR EXPRESSIONS
       POSIX.2 BREs should be supported, but they aren't completely because of
       performance problems.  The \n sequence in a regular expression  matches
       the newline character, and similarly for \a, \t, and other sequences.

Odpowiedni cytat z powyższego linku:

Podstawowe wyrażenia regularne lub smak BRE standaryzują smak podobny do tradycyjnego polecenia grep w systemie UNIX. Jest to właściwie najstarszy smak wyrażeń regularnych, który jest nadal używany. Jedną z cech wyróżniających ten smak jest to, że większość metaznaków wymaga odwrotnego ukośnika, aby nadać metaznakowi jego smak. Większość innych smaków, w tym POSIX ERE, używa ukośnika odwrotnego, aby ukryć znaczenie metaznaków.

Cytując dosłownie komentarz Craiga Sandersa :

Zauważ, że przynajmniej w GNU sed możesz powiedzieć sedowi, aby używał rozszerzonych wyrażeń regularnych z opcją -r lub --regexp-Extended. Jest to przydatne, jeśli chcesz uniknąć dodawania skryptu sed nadmiernemu ucieczce.

Joseph R.
źródło
1
Zauważ, że przynajmniej w GNU sed możesz powiedzieć sedowi, aby używał rozszerzonych wyrażeń regularnych z opcją -rlub --regexp-extendedwiersza poleceń. Jest to przydatne, jeśli chcesz uniknąć dodawania skryptu sed nadmiernemu ucieczce.
cas
@CraigSanders Dzięki za to. Dodano do odpowiedzi.
Joseph R.
@CraigSanders, inne sedimplementacje (kiedy obsługują ERE, głównie BSD) zwykle używają -Edo tego (co jest o wiele bardziej sensowne, ponieważ jest to ta sama opcja jak dla grep. Dlaczego GNU sedwybrał -rjest dla mnie tajemnicą).
Stéphane Chazelas
tak, dla mnie też tajemnica. Bardziej sensowne byłoby użycie -E. a następnie dodaj -F, -G i -P, aby dopasować GNU grep. IMO gawk skorzystałby również z tych samych argumentów RE ... lub przynajmniej -P.
cas
12

To z powodów historycznych.

Regexp został po raz pierwszy wprowadzony w Uniksie w edużyteczności na początku lat 70. Choć edbyła oparta na qedktórego realizacja przez tych samych autorów rozumieć bardziej złożone wyrażenia regularnego, edtylko rozumieć ^, $, [...], ., *i \aby uniknąć wszystkich wyżej wymienionych.

Teraz, gdy pojawiła się potrzeba posiadania większej liczby operatorów, trzeba było znaleźć sposób na wprowadzenie ich bez naruszania wstecznej kompatybilności. Jeśli skrypt użył s edpolecenia s/foo() {/foo (var) {/gdo zastąpienia wszystkich instancji słowem „ foo() {a” foo(var) { i wprowadzono operator (lub {, spowoduje to uszkodzenie tego skryptu.

Jednak żaden skrypt nie zrobiłby tego s/foo\(\) {/foo\(var\) {/, ponieważ jest to to samo, co s/foo() {/foo(var) {/nie było powodu do ucieczki, (ponieważ nie był to operator RE. Tak więc wprowadzenie nowego \(lub \{operatora nie psuje kompatybilności wstecznej, ponieważ bardzo mało prawdopodobne jest uszkodzenie istniejącego skryptu przy użyciu starszej składni.

Tak właśnie zostało zrobione. Później \(...\)dodano początkowo tylko dla s edpolecenia, aby robić rzeczy takie jak s/foo\(.\)/\1bar/i później jako grep '\(.\)\1'(ale nie takie rzeczy jak \(xx\)*).

W UnixV7 (1979, a więc prawie dekadę później) dodano nową formę wyrażeń regularnych w nowym narzędziu egrepi awknarzędzia zwane rozszerzonym wyrażeniem regularnym (ponieważ są to nowe narzędzia, nie ma zgodności wstecznej do złamania). Wreszcie zapewnił funkcjonalność dostępną w starożytnej wersji Kena Thompsona qed(operator przemiany |, grupowanie (..)*) i dodał kilka operatorów takich jak +i ?(ale nie posiadał funkcji wstecznego wyrażenia podstawowych wyrażeń regularnych).

Później dodano BSD \<i \>(zarówno do BRE, jak i ERE), a SysV dodano \{i \}tylko do BRE.

Dopiero znacznie później {i }zostały dodane do ERE przez takie łamanie wstecznej kompatybilności. Nie wszyscy to dodali. Na przykład GNU awkdo wersji 4.0.0 (2011) nie obsługiwał, {chyba że został zmuszony do trybu zgodności z POSIX.

kiedy GNU grepzostało napisane na początku lat 90., dodało wszystkie zalety zarówno BSD, jak i SysV (jak \<, {) i zamiast mieć dwie osobne składnie wyrażeń regularnych i silnik dla BRE i ERE, zaimplementowało te same operatory w obu, tylko odpowiedniki BRE z (, ?, {, +muszą być poprzedzone odwrotnym ukośnikiem (być kompatybilny z innymi implementacjami BRE). Dlatego możesz to zrobić .\+w GNU grep(chociaż nie jest to POSIX lub nie jest obsługiwane przez inne implementacje) i możesz to zrobić (.)\1w GNU egrep(choć nie jest to POSIX ani obsługiwane przez wiele innych implementacji, w tym GNU awk).

Dodawanie \xoperatorów nie jest jedynym sposobem na dodanie większej liczby operatorów w sposób kompatybilny wstecz. Na przykład perlużywane (?...). Jest to nadal kompatybilne wstecz z ERE, ponieważ (?=...)nie jest ważne w ERE, to samo dla .*?. vimdla podobnych operatorów zrobili to inaczej wprowadzając \@=lub .\{-}na przykład.

Stéphane Chazelas
źródło