Como usar um comando shell para mostrar apenas a primeira coluna e a última coluna em um arquivo de texto?

21

Eu preciso de ajuda para descobrir como usar o comando sed para mostrar apenas a primeira coluna e a última coluna em um arquivo de texto. Aqui está o que eu tenho até agora para a coluna 1:

cat logfile | sed 's/\|/ /'|awk '{print $1}'

Minha fraca tentativa de mostrar a última coluna também foi:

cat logfile | sed 's/\|/ /'|awk '{print $1}{print $8}'

No entanto, isso leva a primeira coluna e a última coluna e mescla-as em uma lista. Existe uma maneira de imprimir a primeira coluna e as últimas colunas claramente com os comandos sed e awk?

Exemplo de entrada:

foo|dog|cat|mouse|lion|ox|tiger|bar
    
por user70573 13.06.2014 / 06:21

6 respostas

36

Quase lá. Basta colocar as duas referências de coluna uma ao lado da outra.

cat logfile | sed 's/|/ /' | awk '{print $1, $8}'

Observe também que você não precisa de cat aqui.

sed 's/|/ /' logfile | awk '{print $1, $8}'

Observe também que você pode informar a awk que os separadores de coluna são | , em vez de espaços em branco, portanto você não precisa de sed .

awk -F '|' '{print $1, $8}' logfile

De acordo com sugestões por Caleb , se você quiser uma solução que ainda produza o último campo, mesmo se não houver exatamente oito, você poderá usar $NF .

awk -F '|' '{print $1, $NF}' logfile

Além disso, se desejar que a saída retenha os separadores | , em vez de usar um espaço, você poderá especificar os separadores de campo de saída. Infelizmente, é um pouco mais desajeitado do que apenas usar o -F flag, mas aqui estão três abordagens.

  • Você pode atribuir os separadores de campos de entrada e saída em awk em si, no bloco BEGIN.

    awk 'BEGIN {FS = OFS = "|"} {print $1, $8}' logfile
    
  • Você pode atribuir essas variáveis ao chamar awk da linha de comando, por meio do sinal -v .

    awk -v 'FS=|' -v 'OFS=|' '{print $1, $8}' logfile
    
  • ou simplesmente:

    awk -F '|' '{print $1 "|" $8}' logfile
    
por 13.06.2014 / 06:43
12

Basta substituir do primeiro ao último | com um | (ou espaço, se preferir):

sed 's/|.*|/|/'

Observe que, embora não haja sed implementação em que | seja especial (desde que as expressões regulares estendidas não estejam ativadas por -E ou -r em algumas implementações), \| em si é especial em alguns como GNU sed . Então você deve não escapar | se você pretende que ele corresponda ao caractere | .

Se estiver substituindo por espaço e se a entrada já contiver linhas com apenas um | , você terá que tratar isso especialmente porque |.*| não corresponderá a elas. Isso poderia ser:

sed 's/|\(.*|\)\{0,1\}/ /'

(isto é, tornar a parte .*| opcional) ou:

sed 's/|.*|/ /;s/|/ /'

ou:

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

Se você quiser o primeiro e o oitavo campos, independentemente do número de campos na entrada, então é apenas:

cut -d'|' -f1,8


(todos esses funcionariam com qualquer utilitário compatível com POSIX, assumindo o texto válido das formas de entrada (em particular, os sed geralmente não funcionarão se a entrada tiver bytes ou seqüências de bytes que não formam caracteres válidos na localidade atual, como por exemplo printf 'unix|St1phane|Chazelas\n' | sed 's/|.*|/|/' em um locale UTF-8)).

    
por 13.06.2014 / 09:15
7

Você está usando awk mesmo assim:

awk '{ print $1, $NF }' file
    
por 13.06.2014 / 06:41
3

Se você se sentir sem jeito e sed-less, você pode conseguir a mesma coisa com o coreutils:

paste <(           cut -d'|' -f1  file) \ 
      <(rev file | cut -d'|' -f1 | rev)
    
por 13.06.2014 / 10:14
2

Parece que você está tentando obter o primeiro e último campos de texto delimitados por | .

Assumi que seu arquivo de log contém o texto abaixo,

foo|dog|cat|mouse|lion|ox|tiger|bar
bar|dog|cat|mouse|lion|ox|tiger|foo

E você quer a saída como

foo bar
bar foo

Se sim, então vem o comando para o seu

Através do GNU sed,

sed -r 's~^([^|]*).*\|(.*)$~ ~' file

Exemplo:

$ echo 'foo|dog|cat|mouse|lion|ox|tiger|bar' | sed -r 's~^([^|]*).*\|(.*)$~ ~'
foo bar
    
por 13.06.2014 / 07:45
1

Você provavelmente deveria fazer isso com sed - eu gostaria mesmo assim - mas, apenas porque ninguém escreveu este ainda:

while IFS=\| read col1 cols
do  printf %10s%-s\n "$col1 |" " ${cols##*|}"
done <<\INPUT
foo|dog|cat|mouse|lion|ox|tiger|bar
INPUT

OUTPUT

     foo | bar
    
por 05.08.2014 / 02:23

Tags