La soluzione:
echo 'foo_abc_bar' | sed -r 's/(foo)_((abc)_)?(bar)/\1=\3=\4/g'
Perché i tuoi tentativi precedenti non ha funzionato:
.*
è avido, così per la regex (foo).*(abc)?.*(bar)
tentativo di abbinare il 'foo_abc_bar'
(foo)
corrisponderà 'foo'
, e quindi .*
corrisponderà inizialmente al resto della stringa ('_abc_bar'
). La regex continuerà fino a raggiungere il gruppo richiesto (bar)
e ciò non riuscirà, a quel punto la regex tornerà indietro rinunciando ai caratteri che erano stati abbinati dallo .*
. Ciò avverrà fino a quando il primo .*
corrisponde solo a '_abc_'
, a quel punto il gruppo finale può corrispondere a 'bar'
. Pertanto, anziché lo 'abc'
nella stringa corrispondente al gruppo di acquisizione, viene confrontato con il codice non acquisibile .*
.
Spiegazione della mia soluzione:
La prima cosa e più importante è quello di sostituire il .*
con _
, non v'è alcuna necessità di adattarsi a qualsiasi stringa arbitraria se si sa cosa il separatore sarà. La prossima cosa che dobbiamo fare è capire esattamente quale parte della stringa è opzionale. Se le stringhe 'foo_abc_bar'
e 'foo_bar'
sono entrambe valide, lo 'abc_'
nel mezzo è facoltativo. Possiamo metterlo in un gruppo opzionale usando (abc_)?
. L'ultimo passaggio consiste nell'assicurarsi di avere ancora la stringa 'abc'
in un gruppo di acquisizione, che possiamo eseguire avvolgendo quella parte in un altro gruppo, quindi finiamo con ((abc)_)?
. Abbiamo quindi bisogno di regolare la sostituzione perché c'è un gruppo in più, quindi invece di \1=\2=\3
usiamo \1=\3=\4
, \2
sarebbe la stringa 'abc_'
(se corrisponde). Si noti che nella maggior parte delle implementazioni di espressioni regolari si poteva anche usare un gruppo non catturante e continuare a usare \1=\2=\3
, ma sed non supporta i gruppi non di cattura.
Un'alternativa:
penso che l'espressione regolare di cui sopra è la soluzione migliore, perché è più esplicita (che corrisponderà solo le stringhe esatte che vi interessano). Tuttavia si potrebbe anche evitare il problema descritto sopra usando la ripetizione lazy (corrisponde al minor numero possibile di caratteri) invece di una ripetizione avida (corrisponde al maggior numero possibile di caratteri).È possibile farlo modificando il .*
-.*?
, così la vostra espressione sarebbe simile a questa:
echo 'foo_abc_bar' | sed -r 's/(foo).*?(abc).*?(bar)/\1=\2=\3/g'
fonte
2013-05-23 16:36:51
Perché stai usando '. *' Nell'espressione, i caratteri di sottolineatura possono essere sostituiti con qualsiasi stringa arbitraria? –