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.*
a2
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