faça a saída grep sem seguir a nova linha

8

Considere este trecho:

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

Quero colocar a última palavra em uma variável se alguma condição de padrão for correspondida para linhas no arquivo de texto arbitrário

Meu problema é que a variável Xpossui CR ou LF ou CRLF no final, dependendo do arquivo de origem, do qual quero me livrar, pois interfere nas operações posteriores que pretendo fazer.
Eu até tentei algo como:

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

portanto, esperamos que a sedsaída seja limitada, [A-Za-z]+mas ainda existem esses bytes incômodos dentro da variável X.

Como posso me livrar dele, sem usar muita código como ver o que bytes estão no final com xxd, em seguida, cutele e semelhantes complicações?

zetah
fonte

Respostas:

4

Parece que awkseria uma escolha melhor para as suas necessidades, pois esses problemas não existem devido ao fato de poder usar campos e registros:

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

A substituição evita seu problema com as terminações de linha CRLF.

sub(/\r$/, "")remove o CR à direita, se existir. Como awktrata \no separador de registro (linha), você não precisa removê-lo, pois ele não está nos dados analisados.

printf("%s", $NF)imprime o campo final ( $NF) sem nova linha à direita ( printe algumas outras awkfunções anexam uma nova linha por padrão).

exitacontece após as duas primeiras ações - isso é o equivalente m1na sua greplinha de comando. Isso garante que as awksaídas sejam executadas após a execução dos dois comandos anteriores - e, como esses comandos são emitidos em uma correspondência, e o awk avalia os dados de maneira FIFO, isso imprimirá apenas a primeira correspondência.

Chris Down
fonte
Graças, parece elegante, mas infelizmente CRLF ainda está dentroX
zetah
:) Agora ele não parece mais elegante e ainda não é bom
zetah
@zetah - Não haverá um CR, mas haverá um LF. Eu tive dificuldade para entender o que você quer da pergunta, espero que minha edição faça o que você quer.
Chris Baixo
OK, desta vez é bom - produza a última palavra em uma linha, se essa linha satisfizer alguma condição padrão - não sei, talvez esteja claro para mim porque eu tenho esse problema e difícil de explicar como um falante de inglês não nativo . De qualquer forma, vou esperar um pouco mais se alguém resolver isso com uma grep/sedsolução awk(o que eu não entendo) e, caso contrário, eu a usarei. Graças
zetah
@ zetah - vou adicionar uma explicação para que você possa entender melhor, um segundo.
Chris Baixo
7

O ``ou $()removerá a nova linha do final, mas, para fazer isso programaticamente, use tr.

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

Isso removerá o retorno de carro e / ou a nova linha da string.

O que pode ser o problema é como você gera o resultado. Por exemplo, por padrão, echoadiciona uma nova linha. Você pode querer usar echo -nou printf.

Arcege
fonte
Isso também removerá retornos de carro que podem ocorrer em toda a cadeia, o que pode não ser o desejado.
Chris Baixo
Sim, embora seja possível ter um retorno de carro incorporado em uma única linha, é extremamente raro. A -m1irá assegurar que existe apenas uma linha de saída, que com toda a probabilidade, teria o símbolo de retorno no final.
Arcege 3/03/12
ah tr... interessante, funciona em arquivos LF e CRLF. Eu pensaria \010\013por algum motivo, e também \f\rfunciona corretamente. Sobre o resultado: Na verdade, não coloco a saída na variável, mas como variável incluída $()no padrão para grepcorrespondência - some pipe | grep -o " $(...) ". Obrigado por comentários
zetah
3

Eu prefiro assim

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

Isso funciona para mim:

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

Por que não deixar simplesmente sedfazer a [\r\f]limpeza:

# 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')"

Sua segunda abordagem não possui um regex final para capturar o CR à direita \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
Chade
fonte
0

A versão normal do grep (incluindo grep -P) sempre gera um feed de linha com sua correspondência, portanto, se você tiver apenas um resultado (ou desejar que o feed de linha adicionado final seja removido), basta remover o caractere final da saída, o que você pode fazer através da canalização head -c-1.

Jon
fonte