Wyszukiwanie bez rozróżniania wielkości liter i zamień na sed


82

Próbuję użyć SED do wyodrębnienia tekstu z pliku dziennika. Mogę przeprowadzić wyszukiwanie i zamianę bez większych problemów:

sed 's/foo/bar/' mylog.txt

Jednak chcę, aby w wyszukiwaniu nie była rozróżniana wielkość liter. Z tego, co wyszukałem w Google, wygląda na to, że dołączanie ido końca polecenia powinno działać:

sed 's/foo/bar/i' mylog.txt

Jednak powoduje to wyświetlenie komunikatu o błędzie:

sed: 1: "s/foo/bar/i": bad flag in substitute command: 'i'

Co tu jest nie tak i jak to naprawić?


2
Czy możesz spróbować zaktualizować swoją kopię seda? Ijest rozszerzeniem GNU, które może nie być dostępne z twoją kopią seda.
Lazer

4
EDYCJA : Przeszedłem przez kwalifikację OS X, ponieważ OP zaakceptował odpowiedź, która nie działa na OS X. (Jak wskazała inna odpowiedź, sed na OS X nie obsługuje dopasowywania bez rozróżniania wielkości liter, w przeciwieństwie do dokumentacji Apple).
danorton

1
@danorton: Dzięki za to; jeśli masz wrażenie, że dokumentacja Apple obiecuje coś, czego implementacja nie zapewnia z mojej odpowiedzi poniżej: man sedJEST zgodny z implementacją - nie ma wzmianki o dopasowywaniu bez uwzględniania wielkości liter (i brak wsparcia w praktyce); Jeśli znalazłeś dokument, w którym twierdzi się inaczej, daj nam znać.
mklement0

1
@ mklement0, tak, przepraszam, jestem poprawiony. Dokumentacja Apple nie zawiera żadnych twierdzeń o dopasowywaniu bez rozróżniania wielkości liter dla sed.
danorton

1
FWIW, wersje GNU narzędzi, których wersja BSD jest dostarczana z OS X, są dostępne z różnych menedżerów pakietów. Mam pełny zestaw narzędzi tekstowych zainstalowanych przez Homebrew z gprefiksem, więc mogę użyć gsedlub gdategdy potrzebuję funkcji, której nie ma w wersji podstawowej.
Mark Reed

Odpowiedzi:


72

Aktualizacja : Począwszy MacOS Big Sur (11,0) , sedteraz nie obsługuje Iflagę na wielkość liter , więc komenda w pytaniu powinny teraz działać (BSD sednie zgłasza swój wersji, ale można przejść przez dzień na dole od manstrony, która powinna być March 27, 2017lub nowszym); prosty przykład:

# BSD sed on macOS Big Sur and above (and GNU sed, the default on Linux)
$ sed 's/ö/@/I' <<<'FÖO'
F@O   # `I` matched the uppercase Ö correctly against its lowercase counterpart

Uwaga: I(wielkie litery) to udokumentowana forma flagi, ale idziała również.

Podobnie, począwszy od systemu macOS Big Sur (11.0) jestawk teraz uwzględniany język ( awk --versionpowinien zgłosić 20200816lub nowszy):

# BSD awk on macOS Big Sur and above (and GNU awk, the default on Linux)
$ awk 'tolower($0)' <<<'FÖO'
föo  # non-ASCII character Ö was properly lowercased

Poniższe informacje dotyczą macOS do Catalina (10.15) :

Dla jasności: w systemie macOS sed- który jest implementacją BSD - NIE obsługuje dopasowywania bez uwzględniania wielkości liter - trudno w to uwierzyć, ale prawda. Wcześniej zaakceptowane odpowiedź , która sama pokazuje GNU sed polecenia, zyskał ten status z powodu perlopartym roztworu wzmiankowanym w komentarzach.

Aby to rozwiązanie Perla działało również z obcymi znakami , za pośrednictwem UTF-8, użyj czegoś takiego:

perl -C -Mutf8 -pe 's/öœ/oo/i' <<< "FÖŒ" # -> "Foo"
  • -C włącza obsługę UTF-8 dla strumieni i plików, zakładając, że bieżące ustawienia regionalne są oparte na UTF-8.
  • -Mutf8mówi Perlowi, aby zinterpretował kod źródłowy jako UTF-8 (w tym przypadku ciąg przekazany do -pe) - jest to krótszy odpowiednik bardziej szczegółowego -e 'use utf8;'.Dziękuję, Mark Reed

(Zauważ, że używanie również awknie jest opcją , ponieważ awkw macOS (tj. BWK awk i BSD awk ) wydaje się być całkowicie nieświadome ustawień narodowych - jego tolower()i toupper()funkcje ignorują obce znaki (i sub()/ gsub()nie mają na początku flag niewrażliwych na wielkość liter z).)


Uwaga na temat relacji sedi awkdo standardu POSIX:

BSD sedi awkograniczają ich funkcjonalność głównie do tego, co nakazują specyfikacje POSIXsed i POSIXawk , podczas gdy ich odpowiedniki w GNU implementują znacznie więcej rozszerzeń.


Aby naprawić ustawienia regionalne: blogs.agilefaqs.com/2014/01/12/…
Eduardo Cuomo

69

Uwaga redaktora : to rozwiązanie nie działa na macOS (po wyjęciu z pudełka), ponieważ dotyczy tylko GNU sed , podczas gdy macOS jest dostarczany z BSD sed .

Wykorzystaj „ja”.

sed 's/foo/bar/I' file

2
Widziałem to również i próbowałem ... ale nadal otrzymuję ten sam komunikat o błędzie.
Craig Walker

15
Wydaje się, że sed BSD ma wiele ograniczeń. Zrobiłbym to w PERL (tj. Perl -pe 's / foo / bar / i'), jeśli tak jest.
Wesley Rice

3
Domyślna instalacja systemu OS X Lion powoduje wyświetlenie błędu: sed: 1: „s / foo / bar / I”: zła flaga w poleceniu zastępczym: „I”
Ben Clayton

13
IPrzyrostek nie jest przenośnym wykorzystanie sed. POSIX sedużywa tylko podstawowych wyrażeń regularnych (BRE), które są zaskakująco ograniczone. Nie obsługują nawet +(musisz użyć \{1,\}zamiast tego), nie mówiąc już o dopasowywaniu bez uwzględniania wielkości liter. Jedynym przenośnym sposobem na zrobienie tego z sedem jest sprawdzenie czegoś takiego /[hH][eE][lL][lL][oO]/, co często jest niepraktyczne.
edam

5
To musi być w /gIprzeciwnym razie zadziała tylko na pierwszym meczu.
Faheem Mitha,

25

Innym obejściem sedw systemie Mac OS X jest instalacja gsedz MacPorts lub HomeBrew, a następnie utworzenie aliasu sed='gsed'.


gsed "s / a / b / Ig" działa, dzięki! Dlaczego dobra, działająca odpowiedź powinna otrzymać negatywną opinię?
Matthias M

3
ta odpowiedź jest świetna. użyty brew install gnu-sednastępnie przeszedł do mojego ~ / .bash_profile i dodał alias. Dzięki @davmat
ThinkBonobo

8
Lepiej to zrobić brew install gnu-sed --with-default-names- spowoduje to zastąpienie wartości domyślnej sed.
Mar0ux

5

W sed FAQ rozwiązuje ściśle powiązany wielkości liter wyszukiwania . Wskazuje, że a) wiele wersji seda obsługuje flagę i b) jest to niewygodne w sedzie, powinieneś raczej używać awk lub Perl.

Ale aby to zrobić w sedzie POSIX , sugerują trzy opcje (dostosowane tutaj do podstawienia):

  1. Konwertuj na wielkie litery i przechowuj oryginalny wiersz w pustej przestrzeni; nie zadziała to jednak w przypadku podstawień, ponieważ oryginalna zawartość zostanie przywrócona przed wydrukowaniem, więc nadaje się tylko do wstawiania lub dodawania wierszy na podstawie dopasowania bez uwzględniania wielkości liter.

  2. Może możliwości są ograniczone FOO, Fooa foo. Można je pokryć

     s/FOO/bar/;s/[Ff]oo/bar/
    
  3. Aby wyszukać wszystkie możliwe dopasowania, można użyć wyrażeń nawiasowych dla każdego znaku:

     s/[Ff][Oo][Oo]/bar/
    


@ D.Shawley To nie jest sprzeczne z niczym w odpowiedzi, prawda? A może chcesz dodać kontekst, łącząc się z oficjalną specyfikacją? Mogę to dodać do odpowiedzi.
Benjamin W.

W Nie ma tu nic sprzecznego. Byłem szczęśliwy widząc, że ktoś odwołuje się do POSIX i chciałem dodać łącze. Większość odpowiedzi była zajęta narzekaniem na „niestandardową” implementację seda w macOS, co mnie niepokoiło.
D.Shawley

@ D.Shawley Dodano teraz link do specyfikacji :)
Benjamin W.,

3

Jeśli najpierw dopasowujesz wzorce, np.

/pattern/s/xx/yy/g

następnie chcesz wstawić Ipo wzorze:

/pattern/Is/xx/yy/g

Przykład:

echo Fred | sed '/fred/Is//willma/g'

zwroty willma; bez Iznaku zwraca ciąg nietknięty ( Fred).


2
Na MacOs dostaję:sed: 1: "/fred/Is//willma/g": invalid command code I
Chris F Carroll

Dobra wskazówka. Oto jak go używać na złożonym wyszukiwania: sed -r '/'"$PATTERN"'/I,${s//'$YELLOW'&'$NO_COLOR'/g;b};$q3'. Drukuje tekst, a jeśli znaleziono wzór (bez rozróżniania wielkości liter), podświetla tekst na żółto (kolor ansi). Jeśli nie zostanie znaleziony - zwraca kod zakończenia 3.
Noam Manos

1

Wersja Mac sedwydaje się nieco ograniczona. Jednym ze sposobów obejścia tego jest użycie kontenera linux (przez Docker), który ma użyteczną wersję sed:

cat your_file.txt | docker run -i busybox /bin/sed -r 's/[0-9]{4}/****/Ig'

17
jest to szczególnie ohydna rzecz. Jeśli ktokolwiek rozważa to poważnie, po prostu zainstaluj lokalnie sedes GNU.
ocodo

Przesada, ale przydatne ogólne podejście do wiedzy!
YvesgereY

0

Miałem podobną potrzebę i wymyśliłem to:

to polecenie, aby po prostu znaleźć wszystkie pliki:

grep -i -l -r foo ./* 

ten, aby wykluczyć this_shell.sh (na wypadek, gdybyś umieścił polecenie w skrypcie o nazwie this_shell.sh ), wyświetl dane wyjściowe konsoli, aby zobaczyć, co się stało, a następnie użyj seda na każdej znalezionej nazwie pliku, aby zastąpić tekst foo barem :

grep -i -l -r --exclude "this_shell.sh" foo ./* | tee  /dev/fd/2 | while read -r x; do sed -b -i 's/foo/bar/gi' "$x"; done 

Wybrałem tę metodę, ponieważ nie podobało mi się, gdy wszystkie znaczniki czasu były zmieniane dla plików, które nie zostały zmodyfikowane. podanie wyniku grep umożliwia przeglądanie tylko plików z tekstem docelowym (w ten sposób prawdopodobnie może również poprawić wydajność / szybkość)

pamiętaj, aby wykonać kopię zapasową plików i przetestować przed użyciem. Może nie działać w niektórych środowiskach w przypadku plików z osadzonymi spacjami. (?)


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.