rendere grep output senza trascinare newline


8

Considera questo frammento:

X=$(grep -m1 'some-pattern' some-file | sed -n 's/.* //p')

Voglio mettere l'ultima parola in una variabile se una condizione del modello viene soddisfatta per le righe in un file di testo arbitrario

Il mio problema è che la variabile Xha CR o LF o CRLF alla fine, a seconda del file sorgente, di cui voglio liberarmi, poiché interferisce con le operazioni successive che intendo fare.
Ho anche provato qualcosa del tipo:

X=$(grep -m1 'some-pattern' some-file | sed -n 's/.* \([A-Za-z]\+\)/\1/p')

aspettandosi quindi che l' sedoutput sia limitato [A-Za-z]+ma ci sono ancora questi fastidiosi byte all'interno della variabile X.

Come faccio a sbarazzarsi di esso, senza usare troppo codice come vedere cosa byte sono alla fine con xxdallora cutesso e simili complicazioni?

Risposte:


4

Sembra che awksarebbe una scelta migliore per le tue esigenze, poiché questi problemi non esistono a causa del fatto che può usare campi e record:

x=$(awk '/some-pattern/ { sub(/\r$/, "") ; printf("%s", $NF) ; exit }' some-file)

La sostituzione evita il problema con i finali di riga CRLF.

sub(/\r$/, "")rimuove il CR finale, se esiste. Come awkconsidera \ncome il separatore di record (riga), non è necessario mettere a nudo esso, in quanto non è nei dati essere guardato.

printf("%s", $NF)stampa il campo finale ( $NF) senza una nuova riga finale ( printe alcune altre awkfunzioni aggiungono una nuova riga per impostazione predefinita).

exitsuccede dopo le prime due azioni - questo è l'equivalente di m1nella tua grepriga di comando. Questo assicura che awkesca dopo l'esecuzione dei due precedenti comandi - e poiché questi comandi vengono emessi su una corrispondenza e awk valuta i dati in modo FIFO, questo stamperà solo la prima corrispondenza.


Grazie, sembra elegante ma sfortunatamente CRLF è ancora dentroX
zetah

:) Ora non sembra più elegante e non va ancora bene
zetah

@zetah - Non ci sarà un CR, ma ci sarà un LF. Ho avuto difficoltà a capire cosa vuoi dalla domanda, spero che la mia modifica faccia quello che vuoi.
Chris Down

OK, questa volta lo fa bene - emette l'ultima parola in una riga se quella linea soddisfa alcune condizioni del modello - non lo so, forse è chiaro per me perché ho questo problema, e quindi difficile da spiegare come madrelingua inglese . Ad ogni modo, aspetterò un po 'di più se qualcuno invece risolve questo grep/sedproblema con una soluzione awk(che non capisco), e in caso contrario lo userò. Grazie
zetah

@zetah - Aggiungerò una spiegazione in modo che tu possa capirla meglio, un secondo.
Chris Down

7

L' ``o $()rimuoverà la nuova riga dalla fine, ma per fare questo a livello di codice, utilizzare tr.

grep -m1 'some-pattern' some-file | sed -n 's/.* //p' | tr -d '\012\015'

Ciò rimuoverà il ritorno a capo e / o la nuova riga dalla stringa.

Quale potrebbe essere il problema è come si genera il risultato. Ad esempio, per impostazione predefinita, echoaggiunge una nuova riga. Potresti voler usare echo -no printf.


Ciò rimuoverà anche i ritorni a capo che possono verificarsi in tutta la stringa, che potrebbero non essere desiderati.
Chris Down

Sì, mentre è possibile avere un ritorno a capo incorporato in una sola riga, è estremamente raro. La -m1farà in modo che vi sia una sola uscita linea, che con ogni probabilità, avrebbe il ritorno a capo alla fine.
Arcege,

ah tr... interessante, funziona sia su file LF che CRLF. Vorrei pensare \010\013per qualche motivo, e \f\rfunziona anche correttamente. Informazioni sul risultato: in realtà non inserisco l'output in variabile ma come variabile racchiusa $()in un modello per la grepcorrispondenza - some pipe | grep -o " $(...) ". Grazie per i commenti
zetah

3

Preferisco così

grep -m1 'some-pattern' some-file | sed -n 's/.* //p' | tr -d '\n'

2

Questo funziona per me:

grep -m1 'some-pattern' some-file | sed -n 's/.* //p' | tr -d "\n" | tr -d "\r"

0

Perché non lasciare semplicemente sedfare la [\r\f]pulizia:

# using Bash's $'string' idiom (that decodes ANSI C escape sequences)
# cf. http://wiki.bash-hackers.org/syntax/quoting#ansi_c_like_strings
- X="$(grep -m1 'some-pattern' some-file | sed -n 's/.* //p')"
+ X="$(grep -m1 'some-pattern' some-file | sed -n -e $'s/[\r\f]*$//' -e 's/.* //p')"

Il tuo secondo approccio manca di una regex finale per catturare il CR finale \r.

# sample code to remove trailing \r with sed
# cf. http://en.wikipedia.org/wiki/Regular_expression#POSIX_character_classes
printf 'a b c\r' | sed -n 's/^.* \([[:alpha:]]\{1,\}\)/\1/p' | od -c
printf 'a b c\r' | sed -n 's/^.* \([[:alpha:]]\{1,\}\)[[:space:]]*/\1/p' | od -c

# keeps trailing space after c
printf 'a b c \r' | sed -n 's/^.* \([[:alpha:] ]\{1,\}\)[[:space:]]*/\1/p' | od -b

0

La versione normale di grep (incluso grep -P) genera sempre un feed di linea con la sua corrispondenza, quindi se hai solo un risultato (o vuoi solo che venga rimosso il feed di riga aggiunto finale), è sufficiente rimuovere semplicemente il carattere finale dell'output, che è possibile eseguire eseguendo il piping head -c-1.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.