Zdá se, že zneužívám grep / egrep.

Snažil jsem se hledat řetězce ve více řádcích a nemohl jsem najít shodu, zatímco vím, že to, co hledám, by se mělo shodovat. Původně jsem si myslel, že moje regulární výrazy jsou špatné, ale nakonec jsem si přečetl, že tyto nástroje fungují na řádek (také moje regulární výrazy byly tak triviální, že to nemohlo být problém).

Takže jaký nástroj by člověk použil k vyhledávání vzorů na více řádcích?

Komentáře

  • možný duplikát shody víceřádkových vzorů pomocí sed, awk nebo grep
  • @CiroSantilli – Nemyslím si, že tento Q a ten, na který jste odkazovali, jsou duplikáty. Druhý Q se ptá, jak ‚ d provedete shodu víceřádkového vzoru (tj. Jaký nástroj by měl / mohu použít k tomu), zatímco tento se ptá, jak to provést pomocí grep. Jsou úzce spjaty, ale ne dupoty, IMO.
  • @sim tyto případy jsou těžké se rozhodnout: vidím váš názor. Myslím, že tento konkrétní případ je lepší jako duplikát uživatel řekl "grep" navrhující sloveso “ pozdravit “ a hlavní odpovědi, včetně přijatých nepoužívejte ‚ t grep.
  • Nic nenasvědčuje tomu, že je zde zapotřebí víceřádkový regulární výraz. Zvažte prosím ukázku skutečného příkladu se vstupními údaji a očekávanými výstupními údaji, stejně jako s vaším předchozím úsilím.

Odpověď

Zde „sa sed ten, který vám poskytne grep chování podobné napříč řádky:

sed -n "/foo/{:start /bar/!{N;b start};/your_regex/p}" your_file 

Jak to funguje

  • -n potlačuje výchozí chování tisku každého řádku
  • /foo/{} instruuje jej, aby odpovídal foo a udělejte, co přijde uvnitř klikyháků na odpovídající řádky. Nahraďte foo počáteční částí vzoru.
  • :start je větvící štítek, který nám pomáhá udržovat smyčky, dokud nenajdeme konec našeho regexu.
  • /bar/!{} provede to, co je v klikačkách řádky, které neodpovídají bar. Nahraďte s koncovou částí vzoru.
  • N připojí další řádek k aktivní vyrovnávací paměti (sed tomu říká vzorový prostor)
  • b start se bezpodmínečně rozvětví na start štítek, který jsme vytvořili dříve, aby bylo možné přidávat další řádek, dokud vzorový prostor neobsahuje bar.
  • /your_regex/p vytiskne vzorový prostor, pokud odpovídá your_regex. Měli byste nahradit your_regex celým výrazem, který chcete shodovat na více řádcích.

Komentáře

  • +1 Přidání tohoto do nástroje! Děkujeme.
  • Poznámka: V systému MacOS to dává sed: 1: "/foo/{:start /bar/!{N;b ...": unexpected EOF (pending }'s)
  • chyba sed: unterminated { chyba
  • @Nomaed Zde střílíš ve tmě, ale obsahuje tvůj regulární výraz náhodou nějaké “ {“ znaky? Pokud ano, ‚ budete muset zpětně lomítkem uniknout.
  • @Nomaed Zdá se, že to má co do činění s rozdíly mezi sed implementacemi. Snažil jsem se dodržovat doporučení v této odpovědi, aby byl výše uvedený skript standardně kompatibilní, ale řekl mi, že “ start “ byl nedefinovaný označení. Takže si ‚ nejsem jistý, zda to lze provést standardním způsobem. Pokud to zvládnete, neváhejte upravit moji odpověď.

Odpověď

Obecně používám nástroj nazvaný pcregrep který lze nainstalovat na většinu linuxových verzí pomocí yum nebo apt.

Např.

Předpokládejme, že pokud máte soubor s názvem testfile s obsahem

abc blah blah blah def blah blah blah 

Můžete spustit následující příkaz:

$ pcregrep -M "abc.*(\n|.)*def" testfile 

provést porovnávání vzorů na více řádcích.

Navíc totéž můžete udělat s sed.

$ sed -e "/abc/,/def/!d" testfile 

Komentáře

  • tento sed návrh přeskočí řádek, kde by se našel def

odpověď

jednoduše tuto práci provede normální grep, který podporuje Perl-regexp parametr P.

$ echo "abc blah blah blah def blah blah blah" | grep -oPz "(?s)abc.*?def" abc blah blah blah def 

(?s) s názvem modifikátor DOTALL, který ve vašem regulárním výrazu vytvoří tečku tak, aby odpovídala nejen znakům, ale také zalomení řádků.

Komentáře

  • Když zkusím toto řešení, výstup nekončí ‚ def ‚ ale jde na konec souboru ‚ blah ‚
  • možná váš grep nepodporuje -P možnost
  • Toto bylo jediné, co pro mě fungovalo – vyzkoušel jsem všechny sed návrhy, ale ‚ nešel tak daleko, jak instalovat alternativy grep.
  • $ grep --version: grep (GNU grep) 3.1 v Windows Git Bash má možnost -P, --perl-regexp, ale (?s) není nefunguje. Stále zobrazuje pouze první řádek. Stejný vzor se stejným testovacím řetězcem funguje na regex101.com . Existuje alternativa v Git Bash? sed? (sed (GNU sed) 4.8 zde)
  • Víte, jak přidat kontext do výstupu? grep -1 zde ‚ nefunguje.

Odpovědět

Zde je jednodušší přístup pomocí Perlu:

perl -e "$f=join("",<>); print $& if $f=~/foo\nbar.*\n/m" file 

nebo (protože JosephR převzal sed trasa , bez ostychu ukradnu jeho návrh )

perl -n000e "print $& while /^foo.*\nbar.*\n/mg" file 

### Vysvětlení

$f=join("",<>);: přečte celý soubor a uloží jeho obsah (nové řádky a vše) do proměnné $f. Poté se pokusíme najít shodu foo\nbar.*\n a vytisknout ji, pokud se shoduje (speciální proměnná $& obsahuje poslední nalezenou shodu). ///m je potřeba, aby se regulární výraz shodoval s novými řádky.

-0 nastavuje oddělovač vstupních záznamů. Nastavením na 00 se aktivuje „odstavec“, kde Perl použije jako oddělovač záznamu po sobě jdoucí nové řádky (\n\n). V případech, kdy neexistují žádné po sobě jdoucí nové řádky, je celý soubor přečten (useknut) najednou.

### Upozornění: ne to nedělejte u velkých souborů, načte se celý soubor do paměti a to může být problém.

Komentáře

  • Nemám ‚ t toho o Perlu víte hodně, ale ‚ t to musí být my $f=join("",<>);, přísně vzato?
  • pouze @Sapphire_Brick pokud jste v přísném režimu (use strict;). ‚ Je dobrým zvykem se do toho dostat, zvláště když píšete větší skripty, ale ‚ je to nadměrné pro takového malého linera jeden.

Odpověď

Předpokládejme, že máme soubor test.txt obsahující:

blabla blabla foo here is the text to keep between the 2 patterns bar blabla blabla 

Lze použít následující kód:

sed -n "/foo/,/bar/p" test.txt 

Následující výstup:

foo here is the text to keep between the 2 patterns bar 

Odpovědět

Alternativa grep sift podporuje víceřádkové shody (odmítnutí odpovědnosti: jsem autor).

Předpokládejme testfile obsahuje:

 <book> <title>Lorem Ipsum</title> <description>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua</description> </book> 

sift -m '<description>.*?</description>' (zobrazit řádky obsahující popis)

Výsledek:

 testfile: <description>Lorem ipsum dolor sit amet, consectetur testfile: adipiscing elit, sed do eiusmod tempor incididunt ut testfile: labore et dolore magna aliqua</description> 

sift -m '<description>(.*?)</description>' --replace 'description="$1"' --no-filename (výpis a přeformátovat popis)

Výsledek:

description="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua" 

Komentáře

  • Velmi pěkný nástroj. Gratulujeme! Zkuste to zahrnout do distribucí, jako je Ubuntu.

Odpovědět

Vyřešil jsem to pomocí grep a – Možnost s jiným grepem.

grep first_line_word -A 1 testfile | grep second_line_word 

Možnost -A 1 vytiskne za nalezeným řádkem 1 řádek. Samozřejmě to záleží na vašem souboru a kombinaci slov. Ale pro mě to bylo nejrychlejší a spolehlivé řešení.

Komentáře

  • alias grepp = ‚ grep –color = auto -B10 -A20 -i ‚ pak cat somefile | grepp bla | grepp foo | grepp bar … ano ty -A a -B jsou velmi užitečné …máte nejlepší odpověď
  • Toto není ‚ t super deterministické a ignoruje celý vzor ve prospěch získání jiného jediného řádku (pouze na základě jeho blízkosti do prvního řádku). ‚ Je lepší říci programu, aby šel tak daleko, že musí jít, aby se dostal k nějakému vzoru, který ‚ re absolutně jistý je konec textu, který ‚ zkoušíte najít. Například pokud se testfile aktualizuje tak, že second_line_word je na třetím řádku, pak vám nejen chybí první řádek (kvůli váš druhý grep), ale ‚ vám nechybí řádek, který se mezi nimi začal zobrazovat.
  • Toto by bylo dost dobré MO pro příkazy ad hoc, kde opravdu chcete jen jeden řádek na výstupu, který jste již pochopili. Nemyslím si ‚, že ‚ je to, po čem OP je, a pravděpodobně byste v tom okamžiku mohli také kopírovat / vložit je to ad hoc.

Odpověď

Jedním ze způsobů, jak toho dosáhnout, je Perl. např. zde je obsah souboru s názvem foo:

foo line 1 bar line 2 foo foo foo line 5 foo bar line 6 

Tady je nějaký Perl, který shoda s jakýmkoli řádkem, který začíná na foo, následovaný jakýmkoli řádkem, který začíná barem:

cat foo | perl -e "while(<>){$all .= $_} while($all =~ /^(foo[^\n]*\nbar[^\n]*\n)/m) { print $1; $all =~ s/^(foo[^\n]*\nbar[^\n]*\n)//m; }" 

Perl, rozdělené:

  • while(<>){$all .= $_} Tím se načte celý standardní vstup do proměnné $all
  • while($all =~ Zatímco proměnná all má regulární výraz …
  • /^(foo[^\n]*\nbar[^\n]*\n)/m Regex: foo na začátku řádku, následovaný libovolným počtem znaků, které nejsou novým řádkem, následuje nový řádek, následovaný okamžitě „barem“ a zbytek řádku s barem v něm. /m na konci regexu znamená „shoda napříč více řádky“
  • print $1 Vytisknout část regexu to bylo v závorkách (v tomto případě celý regulární výraz)
  • s/^(foo[^\n]*\nbar[^\n]*\n)//m Vymažte první shodu pro regex, abychom mohli párovat více případů regexu v daném souboru

A výstup:

foo line 1 bar line 2 foo bar line 6 

Komentáře

  • Právě jste prohlásili, že vaše Perl lze zkrátit na více idiomatický: perl -n0777E 'say $& while /^foo.*\nbar.*\n/mg' foo

Odpovědět

Pokud chceme dostat text mezi 2 vzory, které se samy vylučují.

Předpokládejme, že máme soubor test.txt obsahující:

blabla blabla foo here is the text to keep between the 2 patterns bar blabla blabla 

Lze použít následující kód:

 sed -n "/foo/{ n b gotoloop :loop N :gotoloop /bar/!{ h b loop } /bar/{ g p } }" test.txt 

Následující výstup:

here is the text to keep between the 2 patterns 

Jak to funguje, pojďme udělejte to krok za krokem

  1. /foo/{ se spustí, když řádek obsahuje „foo“
  2. n nahraďte prostor vzoru dalším řádkem, tj. slovem „zde“
  3. b gotoloop větev štítku „gotoloop“
  4. :gotoloop definuje štítek „gotoloop“
  5. /bar/!{ pokud vzor neobsahuje „lištu“
  6. h nahradit pozdržený prostor vzorem, takže „zde“ se uloží do pozdrženého prostoru
  7. b loop větev k označení „smyčka“
  8. :loop definuje označení „smyčka“
  9. N připojí vzor k pozdrženému prostoru.
    Nyní přidržený prostor obsahuje:
    „here“
    „je“
  10. :gotoloop Nyní jsme v kroku 4 a provádíme smyčku, dokud řádek neobsahuje „bar“
  11. /bar/ smyčka není dokončena, „bar“ nebyl nalezen, to “ s prostor vzorů
  12. vzorový prostor je nahrazen pozdrženým prostorem, který obsahuje všechny řádky mezi“ foo „a“ bar „, které byly uloženy během hlavní smyčky
  13. p zkopírovat vzorový prostor na standardní výstup

Hotovo!

Komentáře

  • Dobrá práce, +1. Obvykle se vyhýbám použití těchto příkazů tr ‚ ing nových řádků do SOH a provádění normálních sed příkazů a poté nové řádky nahrazuji.

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *