GNU/Linux >> Znalost Linux >  >> Linux

Extrahovat regulární výraz shodný s „sed“ bez vytištění okolních znaků?

Všem „sedným“ doktorům:

Jak můžete získat ‚sed‘ k extrahování regulárního výrazu, který se shodoval v
řádku?

Jinými slovy, chci odstranit pouze řetězec odpovídající regulárnímu
výrazu se všemi neodpovídajícími znaky z obsahujícího řádku.

Zkusil jsem použít funkci zpětné reference, jak je uvedeno níže

regular expression to be isolated 
         gets `inserted` 
              here     
               |
               v  
 sed -n 's/.*( ).*/1/p 

toto funguje pro některé výrazy jako

 sed -n 's/.*(CONFIG_[a-zA-Z0-9_]*).*/1/p 

který úhledně extrahuje všechny názvy maker začínající na ‚CONFIG_ ….‘ (nachází se v nějakém souboru ‚*.h‘) a všechny je vytiskne řádek po řádku

          CONFIG_AT91_GPIO
          CONFIG_DRIVER_AT91EMAC
                   .
                   .   
          CONFIG_USB_ATMEL
          CONFIG_USB_OHCI_NEW
                   .
                 e.t.c. 

ALE výše uvedené se rozpadá na něco jako

  sed -n 's/.*([0-9][0-9]*).*/1/p 

toto vždy vrátí jednotlivé číslice jako

                 7
                 9
                 .
                 .  
                 6

spíše než extrahování souvislého číselného pole, jako je.

              8908078
              89670890  
                 .
                 .  
                 .
               23019   
                 .
               e.t.c.  

P.S.:Byl bych vděčný za zpětnou vazbu o tom, jak je toho dosaženo v 'sed'.
Vím, jak to udělat pomocí 'grep' a 'awk'
Rád bych zjistil, zda moje – i když omezené – porozumění
'sed' má v sobě díry a jestli existuje způsob, jak to udělat v 'sed', které jsem
jednoduše přehlédl.

Přijatá odpověď:

Když regulární výraz obsahuje skupiny, může existovat více než jeden způsob, jak s ním spojit řetězec:regulární výrazy se skupinami jsou nejednoznačné. Vezměme si například regulární výraz ^.*([0-9][0-9]*)$ a řetězec a12 . Jsou dvě možnosti:

  • Odpovídají a proti .* a 2 proti [0-9]*; 1 odpovídá [0-9] .
  • Shoda a1 proti .* a prázdný řetězec proti [0-9]*; 2 odpovídá [0-9] .

Sed, stejně jako všechny ostatní nástroje regulárních výrazů, používá pravidlo nejstarší nejdelší shody:nejprve se pokusí porovnat první část s proměnnou délkou s co nejdelším řetězcem. Pokud najde způsob, jak porovnat zbytek řetězce se zbytkem regulárního výrazu, dobře. Jinak se sed pokusí o další nejdelší shodu pro první část s proměnnou délkou a zkusí to znovu.

Zde je shoda s nejdelším řetězcem jako první a1 proti .* , takže skupina odpovídá pouze 2 . Pokud chcete, aby skupina začala dříve, některé motory regulárních výrazů vám umožní vytvořit .* méně chamtivý, ale sed takovou funkci nemá. Musíte tedy odstranit nejednoznačnost s nějakou další kotvou. Zadejte, že úvodní .* nemůže končit číslicí, takže první číslice skupiny je první možná shoda.

  • Pokud skupina číslic nemůže být na začátku řádku:

    sed -n 's/^.*[^0-9]([0-9][0-9]*).*/1/p'
    
  • Pokud skupina číslic může být na začátku řádku a váš sed podporuje ? operátor pro volitelné části:

    sed -n 's/^(.*[^0-9])?([0-9][0-9]*).*/1/p'
    
  • Pokud může být skupina číslic na začátku řádku, držte se standardních konstrukcí regulárních výrazů:

    sed -n -e 's/^.*[^0-9]([0-9][0-9]*).*/1/p' -e t -e 's/^([0-9][0-9]*).*/1/p'
    

Mimochodem, je to stejné pravidlo nejstarší shody, které vytváří [0-9]* shodují se číslice za první, nikoli za následujícím .* .

Všimněte si, že pokud je na řádku více sekvencí číslic, váš program vždy extrahuje poslední sekvenci číslic, opět kvůli pravidlu nejstarší nejdelší shody aplikovanému na počáteční .* . Pokud chcete extrahovat první posloupnost číslic, musíte určit, že to, co následuje před, je posloupnost nečíslic.

sed -n 's/^[^0-9]*([0-9][0-9]*).*$/1/p'

Obecněji řečeno, abyste extrahovali první shodu regulárního výrazu, musíte spočítat negaci tohoto regulárního výrazu. I když je to vždy teoreticky možné, velikost negace roste exponenciálně s velikostí regulárního výrazu, který negujete, takže je to často nepraktické.

Související:Nelze povolit podporu SMART pro externí pevný disk?

Zvažte svůj další příklad:

sed -n 's/.*(CONFIG_[a-zA-Z0-9_]*).*/1/p'

Tento příklad ve skutečnosti vykazuje stejný problém, ale na typických vstupech ho nevidíte. Pokud jej krmíte hello CONFIG_FOO_CONFIG_BAR , pak příkaz výše vytiskne CONFIG_BAR , nikoli CONFIG_FOO_CONFIG_BAR .

Existuje způsob, jak vytisknout první shodu se sedem, ale je to trochu složitější:

sed -n -e 's/(CONFIG_[a-zA-Z0-9_]*).*/n1/' -e T -e 's/^.*n//' -e p

(Za předpokladu, že váš sed podporuje n znamená nový řádek v s náhradní text.) Funguje to, protože sed hledá nejbližší shodu regulárního výrazu a my se nesnažíme najít shodu s tím, co předchází CONFIG_… bit. Protože uvnitř řádku není žádný nový řádek, můžeme jej použít jako dočasnou značku. T příkaz říká vzdát se, pokud předchozí s příkaz se neshodoval.

Když nemůžete přijít na to, jak něco udělat v sed, obraťte se na awk. Následující příkaz vytiskne nejstarší nejdelší shodu regulárního výrazu:

awk 'match($0, /[0-9]+/) {print substr($0, RSTART, RLENGTH)}'

A pokud chcete, aby to bylo jednoduché, použijte Perl.

perl -l -ne '/[0-9]+/ && print $&'       # first match
perl -l -ne '/^.*([0-9]+)/ && print $1'  # last match

Linux
  1. Odstraňte prvních pět znaků na libovolném řádku textového souboru v Linuxu pomocí sed

  2. Co je špatného s mým předběžným regulárním výrazem v GNU sed?

  3. Jak mohu použít grep k porovnávání, ale bez vytištění shod?

  1. Seskupení regulárních výrazů odpovídá knihovně regulárních výrazů C++ 11

  2. Použití find a tar se soubory se speciálními znaky v názvu

  3. Proč sed ve výchozím nastavení nepoužívá rozšířený režim regulárních výrazů?

  1. Potřebujete uniknout z regulárních znaků v Sed, aby byly interpretovány jako regulární znaky?

  2. Počítání znaků každého řádku s Wc?

  3. Přidání slova na konec řádku se Sedem?