podemos imprimir a última palavra de cada linha no linux usando o comando sed?

9

suponha que, se houver um arquivo composto pelas seguintes linhas, se elas estiverem

12345 567 7878 66

   er3 t45t y6y46y 


 4y6 y656y y5y

   46y6 65y7 y66uyuy

 yy46y6y

A saída deve se parecer com:

66.

y6y46y

y5y

y66uyuyy

y46y6y

Eu tentei o sed 's/.* //g'nome do arquivo de comando e vários outros sedcomandos, mas não está funcionando.

Posso saber qual é o sedcomando exato ?

Rajeev Nukala
fonte
É necessário usar sed?
precisa saber é o seguinte

Respostas:

8
awk '{print $NF}'
sed 's/[[:blank:]]*$//;s/.*[[:blank:]]//'

Isso ainda imprimiria uma linha vazia para cada linha em branco. Para evitá-lo:

awk 'NF{print $NF}'
sed 's/[[:blank:]]*$//;s/.*[[:blank:]]//;/./!d'
Stéphane Chazelas
fonte
Alternativa expressão única: sed -n 's/.*[[:blank:]]\+\([^[:blank:]]\+\)[[:blank:]]*$/\1/p'.
jimmij
@ jimmij - que não funcionará se a última sequência que não estiver em branco também for a primeira e não houver espaços em branco antes dela. Além disso, você também pode simplesmente agir .*na cauda - você descarta qualquer coisa, exceto espaços em branco finais de qualquer maneira com .*[^[:blank:]].
mikeserv
6

A awkvariável $NFé o último campo de cada registro ; você pode usá-lo para imprimir apenas os últimos campos do seu arquivo da seguinte forma:

awk '{print $NF}' file
jasonwryan
fonte
4

Podes tentar :

  • sed 's/.* //'
  • awk '{print $NF}'
Uriel
fonte
4

Você está quase lá. Basta especificar a última palavra:

sed 's/^.* \([^ ][^ ]*\)/\1/g'

O que faz:

  1. '^. *' exclui tudo dentro do início da linha e qualquer espaço.
  2. '\ (...) \' corresponde a um padrão e o retorna como \ 1.
  3. '[^]' corresponde a qualquer coisa sem espaço.

(Editado para adicionar uma solução melhor. Obrigado Hildred!)

Tom contínuo
fonte
1
Aqui está uma expressão mais curta: sed -r 's/.* ([^ ]+)/\1/g'se expressões regulares estendidas forem permitidas, o que geralmente é o caso.
Mkalkov
Versão mais curta, usando a substituição do que você não deseja manter, e não do que você deseja manter:sed 's/.* //'
Uriel
2

Você poderia usar algum padrão adequado de, em grepvez de sed, por exemplo:

grep -o "[a-Z0-9]*$"

Neste exemplo, o [...]contém intervalos de caracteres considerados apropriados para uma "palavra" (alfanuméricos, nesse caso, outros símbolos podem ser adicionados, alguns dos quais precisam ser escapados).

Dalker
fonte
2
Isso pressupõe que não haja espaço em branco no final da linha. a-Zcomo um intervalo não faz muito sentido, mesmo em locais baseados em ASCII. Observe que -oé uma extensão GNU.
Stéphane Chazelas 27/01
0

Se você qualificar palavra para significar qualquer sequência de 1 ou mais caracteres não em branco , a resposta é definitivamente sim, e também é muito simples. Isto porque [[:blank:]]*e [^[:blank:]]*são complementos boolean e - fornecidos todos os caracteres em uma seqüência estão completos - [[:blank:]]*U [^[:blank:]]*pode descrever qualquer seqüência possível, da mesma maneira .*faz.

Se um caractere incompleto ou uma sequência de bytes inválida existir em uma sequência, não será possível descrevê-la com êxito da cabeça para a cauda - como às vezes pode ocorrer ao interpretar uma sequência na codificação incorreta. Para garantir um caractere completo por byte em qualquer sequência, o código de idioma C pode ser forçado como:

LC_ALL=C sed ...

... o que evitaria quaisquer problemas que descrevessem a corda da cabeça à cauda com um padrão abrangente, como .*ou([ ]*[^ ]*)*

Um padrão totalmente complementar pode repetir quantas vezes for necessário, da esquerda para a direita, o comprimento de qualquer string para aterrissar na última ocorrência possível, sem nenhuma interrupção no padrão. Definitivamente, essa é uma linguagem comum.

BRE:

sed 's/\(\([^[:blank:]]*\)[[:blank:]]*\)*/\2/'

ANTES:

sed -E 's/(([^[:blank:]]*)[[:blank:]]*)*/\2/'

Ambas as versões ainda imprimirão linhas em branco, e isso ocorre porque a *estrela Kleene corresponde a zero ou mais ocorrências de um padrão. Primeiro, corresponde a zero ou mais caracteres em branco, zero ou mais caracteres em branco e, em seguida, zero ou mais ocorrências das correspondências agrupadas até corresponder à sequência na íntegra.

Tendo correspondido tudo isso, a mágica acontece na substituição - as referências retornadas por grupos \1e \2são as últimas ocorrências de cada uma. Portanto, quando a substituição é feita, toda a cadeia é substituída apenas pela última ocorrência em uma linha de zero ou mais caracteres não em branco - ou do subgrupo \2.

É claro que isso funciona para qualquer sequência possível - mesmo uma vazia - o que significa que os dois formulários imprimirão caracteres de nova linha para linhas que contêm apenas caracteres em branco ou nenhum. Para lidar com isso, há algumas coisas que você pode fazer, mas primeiro vamos facilitar a digitação da classe de personagem:

b='[:blank:]'

Agora, para imprimir apenas se uma linha contiver um ou mais caracteres que não estão em branco, você pode:

BRE:

sed -n "s/\(\([^$b]*\)[$b]*\)*/\2/;/./p"

ANTES:

sed -En "/[^$b]/s/(([^$b]*)[$b]*)*/\2/p"
  1. Caso BRE - a substituição é sempre realizada e apenas os espaços do padrão com pelo menos um caractere restante são impressos.
  2. Caso ERE - a substituição só é tentada em um espaço padrão contendo pelo menos um caracter não em branco.

Qualquer um dos formulários funcionará com qualquer um dos métodos - desde que a sintaxe esteja correta.

O -ncomutador desativa a impressão automática do espaço do padrão e o psinalizador para os comandos s///ubstitution ou /address /imprime seus resultados somente se for bem-sucedido.

Essa mesma lógica pode ser aplicada para obter qualquer {num}ocorrência, como:

BRE:

sed -n "s/\([$b]*\([^$b]\{1,\}\)\)\{num\}.*/\2/p"

ANTES:

sed -En "s/([$b]*([^$b]+)){num}.*/\2/p"

... em que os numdois regexps podem ser substituídos por um número para imprimir apenas a {num}ocorrência especificada de uma sequência de caracteres que não estão em branco. Uma forma ligeiramente diferente é usada aqui para garantir que a contagem não seja inclinada para o espaço inicial em uma sequência.

Observe que o -Eswitch ERE para sedé suportado nas versões BSD e GNU, embora ainda não seja a sintaxe padrão do POSIX.

mikeserv
fonte
Explicações legais, truques legais, mas observe que não funcionará com implementações tradicionais sed (como Solaris / usr / bin / sed) e será mais caro que a abordagem mais simples (esgota a memória com linhas de entrada com mais de 25 caracteres com o sed_su3da caixa de ferramentas do Heirloom, por exemplo). Portanto, apesar de gostar da resposta, não recomendaria essa abordagem.
Stéphane Chazelas
Também não parece funcionar no FreeBSD.
Stéphane Chazelas 02/02
@ StéphaneChazelas - sim, o desempenho é realmente péssimo para algo assim, mas pode ser muito eficaz para escolher ocorrências numeradas. E para um final de linha, o caso s/.* \([^[:blank:]]\{1,\}\).*/\1/é muito melhor, mas é mais difícil quando várias linhas estão envolvidas. Outro dia, no entanto, eu descobri que 's/\(\n\)*/\1/g;s/\n\(\n.*\)*/&&/[num];s///[samenum]pode efetivamente sustentar isso. De qualquer forma, contanto que não haja erro evidente na lógica, então eu estou feliz - eu apenas pensei que devia ter perdido alguma coisa.
mikeserv
@ StéphaneChazelas - oh, e sobre os mais velhos sed- isso é um pouco estranho - deve parecer de acordo com o padrão. O xrat diz ... Os desenvolvedores de padrão consideraram o comportamento histórico comum, que suportava "\n*", mas não "\n\{min,max\}", "\(...\)*", ou "\(...\)\{min,max\}", como resultado não intencional de uma implementação específica, e suportava expressões de duplicação e intervalo após subexpressões e referências anteriores.
mikeserv
@ StéphaneChazelas - E o padrão diz ... Se a subexpressão referenciada pela referência anterior corresponder a mais de uma sequência devido a um asterisco ( '*' )ou uma expressão de intervalo (consulte o item (5)), a referência posterior corresponderá à última (mais à direita) ) dessas cadeias. Eu tenho certeza que testei isso com minised- embora certamente estivesse testando algo estranho com minisedoutro dia.
mikeserv
-1

Sim. O comando sed a seguir remove primeiro todos os espaços em branco à direita ( s/ *$//) e depois tudo, inclusive o último espaço em branco ( s/.* //). Provavelmente vale a pena substituir o espaço [[:blank:]]em branco literal por para capturar guias e outros caracteres semelhantes ao espaço.

$ echo "  aaa bbb cc   " | sed -e 's/ *$//' -e 's/.* //'
cc
$ echo "  aaa bbb cc" | sed -e 's/ *$//' -e 's/.* //'
cc
$ echo "aaa bbb cc   " | sed -e 's/ *$//' -e 's/.* //'
cc
$ echo "aaa bbb cc" | sed -e 's/ *$//' -e 's/.* //'
cc
$ echo "  cc  " | sed -e 's/ *$//' -e 's/.* //'
cc
$ echo "cc" | sed -e 's/ *$//' -e 's/.* //'
cc
mkalkov
fonte
-1
cat file_name | rev | cut -f1 -d ' ' | rev
ASIT
fonte