Usando o awk para imprimir todas as colunas da enésima até a última


310

Essa linha funcionou até eu ter espaços em branco no segundo campo.

svn status | grep '\!' | gawk '{print $2;}' > removedProjs

existe uma maneira de o awk imprimir tudo em US $ 2 ou mais? (US $ 3, US $ 4 ... até não termos mais colunas?)

Suponho que devo acrescentar que estou fazendo isso em um ambiente Windows com Cygwin.


11
Como um aparte, o grep | awké um antipattern - você querawk '/!/ { print $2 }'
tripleee

3
Unix "corte" é mais fácil ...svn status | grep '\!' | cut -d' ' -f2- > removedProjs
roblogic

Possível duplicado do resto impressão dos campos no awk
acm

@ tripleee: Estou tão feliz que você mencionou isso - estou frustrado em vê-lo em todos os lugares!
Graham Nicholls

Respostas:


490

imprimirá tudo, exceto a primeira coluna:

awk '{$1=""; print $0}' somefile

imprimirá todas, exceto as duas primeiras colunas:

awk '{$1=$2=""; print $0}' somefile

93
pegadinha: Folhas de um espaço à esquerda pendurado sobre :(
raphinesse

5
Eu gosto da abordagem pragmática. não é necessário usar o gato, basta colocar o nome do arquivo após o comando awk.
kon

45
@raphinesse você pode corrigir isso comawk '{$1=""; print substr($0,2)}' input_filename > output_filename
themiurgo

6
Isso não funciona com delimitadores que não são de espaço em branco, substitui-os por um espaço.
Dejan

3
Para delimitadores que não são de espaço em branco, você pode especificar o OFS (Output Field Separator), por exemplo, por vírgula: awk -F, -vOFS=, '{$1=""; print $0}'Você terminará com um delimitador inicial ( $1ainda está incluído, apenas como uma string vazia). Você pode remover isso com sed:awk -F, -vOFS=, '{$1=""; print $0}' | sed 's/^,//'
cherdt 07/07

99

Há uma pergunta duplicada com uma resposta mais simples usando cut:

 svn status |  grep '\!' | cut -d\  -f2-

-despecifica o delimitador (espaço) , -fespecifica a lista de colunas (todas começando com a 2ª)


Você também pode usar "-b" para especificar a posição (do enésimo caractere em diante).
Dakatine

Como uma nota, embora este executa a mesma tarefa como a awkversão, há questões tamponamento linha com cut, que awknão tem: stackoverflow.com/questions/14360640/...
sdaau

24
Agradável e simples, mas vem com uma ressalva: awktrata vários caracteres de espaço adjacentes. como um único separador, enquanto cutnão; também - embora isso não seja um problema no caso em questão - cutaceita apenas um único caractere literal. como delimitador, enquanto awkpermite uma regex.
precisa saber é o seguinte

Com base nisso: stackoverflow.com/a/39217130/8852408 , é provável que esta solução não seja muito eficiente.
FcknGioconda

85

Você pode usar um loop for para percorrer os campos de impressão de $ 2 a $ NF (variável interna que representa o número de campos na linha).

Editar: como "imprimir" acrescenta uma nova linha, você deseja armazenar em buffer os resultados:

awk '{out=""; for(i=2;i<=NF;i++){out=out" "$i}; print out}'

Como alternativa, use printf:

awk '{for(i=2;i<=NF;i++){printf "%s ", $i}; printf "\n"}'

Então eu tentei isso, mas acho que estou perdendo alguma coisa .. aqui está o que eu fiz svn status | grep '\!' | gawk '{for (i = 1; i <= $ NF; i ++) print $ i "";}'> removedProjs
Andy

Como a impressão anexa uma nova linha, convém armazenar em buffer os resultados. Veja minha edição.
VeeArr

1
Eu gosto mais dessa resposta porque mostra como percorrer os campos.
Edward Falk

3
Se desejar que a impressão use um espaço, altere o separador do registro de saída: awk '{ORS = ""; for (i = 2; i <NF; i ++) print $ i} 'somefile
Christian Lescuyer 8/12/12

3
Sempre haverá alguns espaços demais. Isso funciona melhor: '{for(i=11;i<=NF-1;i++){printf "%s ", $i}; print $NF;}'sem espaços à esquerda ou à direita.
Marki

24
awk '{out=$2; for(i=3;i<=NF;i++){out=out" "$i}; print out}'

Minha resposta é baseada na do VeeArr , mas notei que ele começou com um espaço em branco antes de imprimir a segunda coluna (e o resto). Como eu tenho apenas 1 ponto de reputação, não posso comentar, então aqui está uma nova resposta:

comece com "out" como a segunda coluna e adicione todas as outras colunas (se elas existirem). Isso vai bem desde que haja uma segunda coluna.


2
Excelente, você também removeu o $ na frente da variável out, o que também é importante.
Alexis Wilke

15

A maioria das soluções com awk deixa espaço. As opções aqui evitam esse problema.

Opção 1

Uma solução de corte simples (funciona apenas com delimitadores únicos):

command | cut -d' ' -f3-

opção 2

Forçando um awk re-calc às vezes remova o espaço inicial adicionado (OFS) restante removendo os primeiros campos (funciona com algumas versões do awk):

command | awk '{ $1=$2="";$0=$0;} NF=NF'

Opção 3

Imprimir cada campo formatado com printfdará mais controle:

$ in='    1    2  3     4   5   6 7     8  '
$ echo "$in"|awk -v n=2 '{ for(i=n+1;i<=NF;i++) printf("%s%s",$i,i==NF?RS:OFS);}'
3 4 5 6 7 8

No entanto, todas as respostas anteriores alteram todos os FS repetidos entre os campos para OFS. Vamos criar algumas opções que não fazem isso.

Opção 4 (recomendado)

Um loop com sub para remover campos e delimitadores na frente.
E usando o valor de FS em vez de espaço (que pode ser alterado).
É mais portátil e não aciona uma alteração do FS para OFS: NOTA: O ^[FS]*é aceitar uma entrada com espaços à esquerda.

$ in='    1    2  3     4   5   6 7     8  '
$ echo "$in" | awk '{ n=2; a="^["FS"]*[^"FS"]+["FS"]+";
  for(i=1;i<=n;i++) sub( a , "" , $0 ) } 1 '
3     4   5   6 7     8

Opção 5

É bem possível criar uma solução que não adicione espaços em branco extras (iniciais ou finais) e preserve os espaços em branco existentes usando a função gensubdo GNU awk, da seguinte maneira:

$ echo '    1    2  3     4   5   6 7     8  ' |
  awk -v n=2 'BEGIN{ a="^["FS"]*"; b="([^"FS"]+["FS"]+)"; c="{"n"}"; }
          { print(gensub(a""b""c,"",1)); }'
3     4   5   6 7     8 

Também pode ser usado para trocar um grupo de campos com uma contagem n:

$ echo '    1    2  3     4   5   6 7     8  ' |
  awk -v n=2 'BEGIN{ a="^["FS"]*"; b="([^"FS"]+["FS"]+)"; c="{"n"}"; }
          {
            d=gensub(a""b""c,"",1);
            e=gensub("^(.*)"d,"\\1",1,$0);
            print("|"d"|","!"e"!");
          }'
|3     4   5   6 7     8  | !    1    2  !

Obviamente, nesse caso, o OFS é usado para separar ambas as partes da linha, e o espaço em branco à direita dos campos ainda é impresso.

NOTA: [FS]* é usado para permitir espaços à esquerda na linha de entrada.


13

Eu pessoalmente tentei todas as respostas mencionadas acima, mas a maioria delas era um pouco complexa ou simplesmente não estava certa. A maneira mais fácil de fazer isso do meu ponto de vista é:

awk -F" " '{ for (i=4; i<=NF; i++) print $i }'
  1. Onde -F "" define o delimitador para o awk usar. No meu caso, é o espaço em branco, que também é o delimitador padrão do awk. Isso significa que -F "" pode ser ignorado.

  2. Onde NF define o número total de campos / colunas. Portanto, o loop começará do 4º campo até o último campo / coluna.

  3. Onde $ N recupera o valor do enésimo campo. Portanto, imprima $ i imprimirá o campo / coluna atual com base na contagem de loops.


4
Problema, que imprime cada campo em uma linha diferente.
Mvlrj

nada o impede de acrescentar isso no final :-) `| tr '\ n' '' '
koullislp 12/02

3
Um pouco atrasado, mas desajeitado '{for (i = 5; i <= NF; i ++) {printf "% s", $ i}}'
plitter em 8/08/18

8
awk '{ for(i=3; i<=NF; ++i) printf $i""FS; print "" }'

A lauhub propôs esta solução correta, simples e rápida aqui


7

Isso me irritava tanto, sentei-me e escrevi um cutanalisador de especificação de campo semelhante, testado com o GNU Awk 3.1.7.

Primeiro, crie um novo script de biblioteca do Awk chamado pfcut, com por exemplo

sudo nano /usr/share/awk/pfcut

Em seguida, cole o script abaixo e salve. Depois disso, é assim que o uso se parece:

$ echo "t1 t2 t3 t4 t5 t6 t7" | awk -f pfcut --source '/^/ { pfcut("-4"); }'
t1 t2 t3 t4

$ echo "t1 t2 t3 t4 t5 t6 t7" | awk -f pfcut --source '/^/ { pfcut("2-"); }'
t2 t3 t4 t5 t6 t7

$ echo "t1 t2 t3 t4 t5 t6 t7" | awk -f pfcut --source '/^/ { pfcut("-2,4,6-"); }'
t1 t2 t4 t6 t7

Para evitar digitar tudo isso, acho que o melhor que se pode fazer (veja o contrário Carregar automaticamente uma função de usuário na inicialização com o awk? - Unix e Linux Stack Exchange ) é adicionar um alias ao ~/.bashrc; por exemplo, com:

$ echo "alias awk-pfcut='awk -f pfcut --source'" >> ~/.bashrc
$ source ~/.bashrc     # refresh bash aliases

... então você pode simplesmente ligar para:

$ echo "t1 t2 t3 t4 t5 t6 t7" | awk-pfcut '/^/ { pfcut("-2,4,6-"); }'
t1 t2 t4 t6 t7

Aqui está a fonte do pfcutscript:

# pfcut - print fields like cut
#
# sdaau, GNU GPL
# Nov, 2013

function spfcut(formatstring)
{
  # parse format string
  numsplitscomma = split(formatstring, fsa, ",");
  numspecparts = 0;
  split("", parts); # clear/initialize array (for e.g. `tail` piping into `awk`)
  for(i=1;i<=numsplitscomma;i++) {
    commapart=fsa[i];
    numsplitsminus = split(fsa[i], cpa, "-");
    # assume here a range is always just two parts: "a-b"
    # also assume user has already sorted the ranges
    #print numsplitsminus, cpa[1], cpa[2]; # debug
    if(numsplitsminus==2) {
     if ((cpa[1]) == "") cpa[1] = 1;
     if ((cpa[2]) == "") cpa[2] = NF;
     for(j=cpa[1];j<=cpa[2];j++) {
       parts[numspecparts++] = j;
     }
    } else parts[numspecparts++] = commapart;
  }
  n=asort(parts); outs="";
  for(i=1;i<=n;i++) {
    outs = outs sprintf("%s%s", $parts[i], (i==n)?"":OFS); 
    #print(i, parts[i]); # debug
  }
  return outs;
}

function pfcut(formatstring) {
  print spfcut(formatstring);
}

Parece que você quer usar cut, nãoawk
roblogic

5

Imprimindo colunas começando em # 2 (a saída não terá espaço à direita no início):

ls -l | awk '{sub(/[^ ]+ /, ""); print $0}'

1
Bom, você deve adicionar +após o espaço, pois os campos podem ser separados por mais de 1 espaço ( awktrata vários espaços adjacentes como um único separador). Além disso, awkignorará os espaços à esquerda, portanto, você deve iniciar o regex com ^[ ]*. Com espaço como separador, você pode até generalizar a solução; por exemplo, o seguinte retorna tudo do terceiro campo: awk '{sub(/^[ ]*([^ ]+ +){2}/, ""); print $0}'Porém, fica mais complicado com separadores de campos arbitrários.
precisa saber é o seguinte

5

Isso funcionaria?

awk '{print substr($0,length($1)+1);}' < file

Mas deixa algum espaço em branco na frente.


4
echo "1 2 3 4 5 6" | awk '{ $NF = ""; print $0}'

este usa o awk para imprimir tudo, exceto o último campo


3

Isto é o que eu preferi em todas as recomendações:

Imprimir da sexta à última coluna.

ls -lthr | awk '{out=$6; for(i=7;i<=NF;i++){out=out" "$i}; print out}'

ou

ls -lthr | awk '{ORS=" "; for(i=6;i<=NF;i++) print $i;print "\n"}'

2

Se você precisar de colunas específicas impressas com delímetro arbitrário:

awk '{print $3 "  " $4}'

col # 3 col # 4

awk '{print $3 "anything" $4}'

col # 3anythingcol # 4

Portanto, se você tiver espaço em branco em uma coluna, serão duas colunas, mas poderá conectá-lo a qualquer delimitador ou sem ele.


2

Solução Perl:

perl -lane 'splice @F,0,1; print join " ",@F' file

Essas opções de linha de comando são usadas:

  • -n faça um loop em todas as linhas do arquivo de entrada, não imprima automaticamente todas as linhas

  • -l remove as novas linhas antes do processamento e as adiciona novamente depois

  • -amodo de divisão automática - divida as linhas de entrada no array @F. O padrão é dividir em espaço em branco

  • -e executar o código perl

splice @F,0,1 remove de maneira limpa a coluna 0 da matriz @F

join " ",@F une os elementos da matriz @F, usando um espaço entre cada elemento


Solução Python:

python -c "import sys;[sys.stdout.write(' '.join(line.split()[1:]) + '\n') for line in sys.stdin]" < file


1

Se você não deseja reformatar a parte da linha que não é cortada, a melhor solução que posso pensar está escrita na minha resposta em:

Como imprimir todas as colunas após um número específico usando o awk?

Ele corta o que está antes do número do campo fornecido N e imprime todo o restante da linha, incluindo o número do campo N e mantém o espaçamento original (não reformata). Não importa se a sequência do campo também aparece em outro lugar da linha.

Defina uma função:

fromField () { 
awk -v m="\x01" -v N="$1" '{$N=m$N; print substr($0,index($0,m)+1)}'
}

E use-o assim:

$ echo "  bat   bi       iru   lau bost   " | fromField 3
iru   lau bost   
$ echo "  bat   bi       iru   lau bost   " | fromField 2
bi       iru   lau bost 

A saída mantém tudo, incluindo espaços à direita

No seu caso particular:

svn status | grep '\!' | fromField 2 > removedProjs

Se o seu arquivo / fluxo não contiver caracteres de nova linha no meio das linhas (você pode estar usando um Separador de Registros diferente), poderá usar:

awk -v m="\x0a" -v N="3" '{$N=m$N ;print substr($0, index($0,m)+1)}'

O primeiro caso falhará apenas em arquivos / fluxos que contenham o número de caractere hexadecimal raro 1


0

Isso funcionaria se você estivesse usando o Bash e pudesse usar o 'x' quantos elementos você deseja descartar e ignora vários espaços se eles não tiverem escapado.

while read x b; do echo "$b"; done < filename

0

Perl:

@m=`ls -ltr dir | grep ^d | awk '{print \$6,\$7,\$8,\$9}'`;
foreach $i (@m)
{
        print "$i\n";

}

1
Isso não responde à pergunta, que generaliza o requisito de impressão da enésima coluna até o final .
roaima

0

Essa awkfunção retorna substring $0que inclui campos de beginpara end:

function fields(begin, end,    b, e, p, i) {
    b = 0; e = 0; p = 0;
    for (i = 1; i <= NF; ++i) {
        if (begin == i) { b = p; }
        p += length($i);
        e = p;
        if (end == i) { break; }
        p += length(FS);
    }
    return substr($0, b + 1, e - b);
}

Para começar tudo a partir do campo 3:

tail = fields(3);

Para obter uma seção $0que abrange os campos 3 a 5:

middle = fields(3, 5);

b, e, p, ium absurdo na lista de parâmetros de função é apenas uma awkmaneira de declarar variáveis ​​locais.


0

Quero estender as respostas propostas para a situação em que os campos são delimitados por possivelmente vários espaços em branco - a razão pela qual o OP não está usandocut , suponho.

Eu sei que o OP perguntou sobre awk, mas uma sedabordagem funcionaria aqui (exemplo com colunas de impressão do quinto ao último):

  • abordagem pura sed

    sed -r 's/^\s*(\S+\s+){4}//' somefile

    Explicação:

    • s/// é usado da maneira padrão para executar a substituição
    • ^\s* corresponde a qualquer espaço em branco consecutivo no início da linha
    • \S+\s+ significa uma coluna de dados (caracteres que não são de espaço em branco seguidos por caracteres de espaço em branco)
    • (){4} significa que o padrão é repetido 4 vezes.
  • sed e cut

    sed -r 's/^\s+//; s/\s+/\t/g' somefile | cut -f5-

    apenas substituindo espaços em branco consecutivos por uma única guia;

  • tr and cut: trtambém pode ser usado para espremer caracteres consecutivos com a -sopção

    tr -s [:blank:] <somefile | cut -d' ' -f5-

-1

Os exemplos do awk parecem complexos aqui, aqui está uma sintaxe simples do shell Bash:

command | while read -a cols; do echo ${cols[@]:1}; done

Onde 1é o seu n º contagem coluna 0.


Exemplo

Dado este conteúdo do arquivo ( in.txt):

c1
c1 c2
c1 c2 c3
c1 c2 c3 c4
c1 c2 c3 c4 c5

aqui está a saída:

$ while read -a cols; do echo ${cols[@]:1}; done < in.txt 

c2
c2 c3
c2 c3 c4
c2 c3 c4 c5

-1

Eu não estava feliz com nenhuma das awksoluções apresentadas aqui, porque queria extrair as primeiras colunas e depois imprimir o restante, então procurei perl. O código a seguir extrai as duas primeiras colunas e exibe o restante como está:

echo -e "a  b  c  d\te\t\tf g" | \
  perl -ne 'my @f = split /\s+/, $_, 3; printf "first: %s second: %s rest: %s", @f;'

A vantagem em comparação com a perlsolução de Chris Koknat é que, na verdade, apenas os primeiros n elementos são separados da string de entrada; o restante da string não está dividido e, portanto, permanece completamente intacto. Meu exemplo demonstra isso com uma mistura de espaços e guias.

Para alterar a quantidade de colunas que devem ser extraídas, substitua o 3no exemplo por n + 1.


-1
ls -la | awk '{o=$1" "$3; for (i=5; i<=NF; i++) o=o" "$i; print o }'

de esta resposta não é ruim, mas o espaçamento natural é ido.
Por favor, compare-o com este:

ls -la | cut -d\  -f4-

Então você veria a diferença.

Mesmo ls -la | awk '{$1=$2=""; print}'que é baseado na resposta melhor votada até agora não preserva a formatação.

Assim, eu usaria o seguinte e também permite colunas seletivas explícitas no início:

ls -la | cut -d\  -f1,4-

Observe que todo espaço também conta para colunas; por exemplo, abaixo, as colunas 1 e 3 estão vazias, 2 é INFO e 4 é:

$ echo " INFO  2014-10-11 10:16:19  main " | cut -d\  -f1,3

$ echo " INFO  2014-10-11 10:16:19  main " | cut -d\  -f2,4
INFO 2014-10-11
$

-1

Se você deseja texto formatado, encadeie seus comandos com eco e use $ 0 para imprimir o último campo.

Exemplo:

for i in {8..11}; do
   s1="$i"
   s2="str$i"
   s3="str with spaces $i"
   echo -n "$s1 $s2" | awk '{printf "|%3d|%6s",$1,$2}'
   echo -en "$s3" | awk '{printf "|%-19s|\n", $0}'
done

Impressões:

|  8|  str8|str with spaces 8  |
|  9|  str9|str with spaces 9  |
| 10| str10|str with spaces 10 |
| 11| str11|str with spaces 11 |

-9

Por causa de uma resposta incorreta e votada com 340 votos, acabei de perder 5 minutos da minha vida! Alguém tentou esta resposta antes de votar novamente? Aparentemente não. Completamente inútil.

Eu tenho um log em que, após US $ 5 com um endereço IP, pode haver mais texto ou nenhum texto. Preciso de tudo, desde o endereço IP até o final da linha, caso haja algo após $ 5. No meu caso, isso é realmente um programa awk, não um on-line do awk; portanto, o awk deve resolver o problema. Quando tento remover os 4 primeiros campos usando a resposta mais votada, mas completamente errada:

echo "  7 27.10.16. Thu 11:57:18 37.244.182.218" | awk '{$1=$2=$3=$4=""; printf "[%s]\n", $0}'

cospe uma resposta errada e inútil (acrescentei [..] para demonstrar):

[    37.244.182.218 one two three]

Existem até algumas sugestões para combinar substr com esta resposta errada. Como essa complicação é uma melhoria.

Em vez disso, se as colunas tiverem largura fixa até que o ponto de corte e o awk sejam necessários, a resposta correta é:

echo "  7 27.10.16. Thu 11:57:18 37.244.182.218" | awk '{printf "[%s]\n", substr($0,28)}'

que produz a saída desejada:

[37.244.182.218 one two three]
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.