Imprimindo tudo exceto o primeiro campo com awk

108

Eu tenho um arquivo parecido com este:

AE  United Arab Emirates
AG  Antigua & Barbuda
AN  Netherlands Antilles
AS  American Samoa
BA  Bosnia and Herzegovina
BF  Burkina Faso
BN  Brunei Darussalam

E eu gostaria de inverter a ordem, imprimindo primeiro tudo, exceto $ 1 e depois $ 1:

United Arab Emirates AE

Como posso fazer o truque "tudo exceto o campo 1"?

cfischer
fonte
2
Olá @cfisher, pode ser feito sem um loop ang sem o espaço extra.
Juan Diego Godoy Robles,

Respostas:

91

A atribuição $1funciona, mas deixará um espaço inicial:awk '{first = $1; $1 = ""; print $0, first; }'

Você também pode encontrar o número de colunas NFe usá-lo em um loop.

Ben Jackson
fonte
2
Para os totalmente preguiçosos; aqui está o código klashxx ' .
Serge Stroobandt
1
Ótimo. Livrou-se do espaço principal com sed: awk {'first = $1; $1=""; print $0'}|sed 's/^ //g'
Thyag
O espaço é facilmente removido com o VIM pressionando 'Ctrl + V Gd' no modo normal
Santi
107

$1=""deixa um espaço como Ben Jackson mencionou, então use um forloop:

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

Portanto, se sua string era "um, dois, três", a saída será:

dois
tres

Se quiser o resultado em uma linha, você pode fazer o seguinte:

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

Isso lhe dará: "dois três"

7winkie
fonte
4
e um espaço extra à direita
NeronLeVelu
2
melhor usar: awk '{for(i=2;i<=NF;i++){ printf("%s",( (i>2) ? OFS : "" ) $i) } ; print ;}' que: imprime os campos 2 em NF, adiciona o separador de campo de saída conforme necessário (ou seja, exceto antes de $ 2). A última impressão adiciona uma nova linha final para encerrar a impressão da linha atual. Esse funcionará se você alterar FS / OFS (ou seja, não será sempre "espaço")
Olivier Dulac
O segundo funcionou muito bem para mim. O primeiro, nem tanto. Não tenho certeza do porquê. Cortou todo o texto.
vozes de
72

Use o cutcomando com a --complementopção:

$ echo a b c | cut -f 1 -d ' '
a
$ echo a b c | cut -f 1,2 -d ' '
a b
$ echo a b c | cut -f 1 -d ' ' --complement
b c
zeleniy
fonte
2
Embora não tenha respondido à pergunta específica do awk, achei isso mais útil, pois o awk removia espaços duplicados e o cut não.
Fmstrat de
19
echo a b c | cut -d' ' -f 2- é uma alternativa
Luis
2
Legal - a solução @Luis funciona no Mac, que não suporta --complement
metadaddy
21

Talvez a maneira mais concisa:

$ awk '{$(NF+1)=$1;$1=""}sub(FS,"")' infile
United Arab Emirates AE
Antigua & Barbuda AG
Netherlands Antilles AN
American Samoa AS
Bosnia and Herzegovina BA
Burkina Faso BF
Brunei Darussalam BN

Explicação:

$(NF+1)=$1: Gerador de um "novo" último campo.

$1="": Defina o primeiro campo original como nulo

sub(FS,""): Após as duas primeiras ações, {$(NF+1)=$1;$1=""}elimine o primeiro separador de campo usando sub. A impressão final está implícita.

Juan Diego Godoy Robles
fonte
13
awk '{sub($1 FS,"")}7' YourFile

Remova o primeiro campo e o separador e imprima o resultado ( 7é um valor diferente de zero, portanto imprimindo $ 0).

NeronLeVelu
fonte
Melhor resposta! Votos positivos. Qual a diferença de apenas usar 1? Gostaria de saber o uso desse padrão e queria entender isso. obrigado!
Abhijeet Rastogi
10
awk '{ saved = $1; $1 = ""; print substr($0, 2), saved }'

Definir o primeiro campo como ""deixa uma única cópia de OFSno início de $0. Supondo que OFSseja apenas um único caractere (por padrão, é um único espaço), podemos removê-lo com substr($0, 2). Em seguida, acrescentamos a cópia salva de $1.

duvidosojim
fonte
6

Se você está aberto a uma solução Perl ...

perl -lane 'print join " ",@F[1..$#F,0]' file

é uma solução simples com um separador de entrada / saída de um espaço, que produz:

United Arab Emirates AE
Antigua & Barbuda AG
Netherlands Antilles AN
American Samoa AS
Bosnia and Herzegovina BA
Burkina Faso BF
Brunei Darussalam BN

Este próximo é um pouco mais complexo

perl -F`  ` -lane 'print join "  ",@F[1..$#F,0]' file

e assume que o separador de entrada / saída tem dois espaços:

United Arab Emirates  AE
Antigua & Barbuda  AG
Netherlands Antilles  AN
American Samoa  AS
Bosnia and Herzegovina  BA
Burkina Faso  BF
Brunei Darussalam  BN

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

  • -n fazer um loop em cada linha do arquivo de entrada, não imprimir automaticamente todas as linhas

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

  • -amodo autosplit - divide as linhas de entrada no array @F. O padrão é divisão em espaços em branco

  • -F modificador autosplit, neste exemplo divide-se em '' (dois espaços)

  • -e execute o seguinte código perl

@Fé a matriz de palavras em cada linha, indexada começando com 0
$#Fé o número de palavras em @F
@F[1..$#F]é uma fatia da matriz do elemento 1 até o último elemento
@F[1..$#F,0]é uma fatia da matriz do elemento 1 até o último elemento mais o elemento 0

Chris Koknat
fonte
1
Eu o executei e tinha um número extra no final, então usei esta versão: perl -lane 'shift @F; imprimir juntar "", @F '
Hans Poo
2

O separador de campo no gawk (pelo menos) pode ser uma string, bem como um caractere (também pode ser um regex). Se seus dados forem consistentes, isso funcionará:

awk -F "  " '{print $2,$1}' inputfile

São dois espaços entre as aspas duplas.

Pausado até novo aviso.
fonte
A melhor resposta para a situação em questão, mas, tecnicamente, isso não responde à questão de como imprimir tudo, exceto o primeiro campo.
Dan Molding,
@DanMoulding: Enquanto o arquivo é consistente no uso de dois espaços para separar o código do país e não há outras ocorrências de dois espaços em conjunto, a minha resposta faz endereço a questão.
Pausado até novo aviso.
2
As pessoas que chegam a esta pergunta chegam aqui porque querem saber como imprimir tudo, exceto o primeiro campo (veja o título da pergunta). Foi assim que cheguei aqui. Sua resposta mostra como imprimir o primeiro campo seguido do segundo campo. Embora esta seja provavelmente a melhor solução para a situação particular do OP, ela não resolve o problema geral de como imprimir tudo, exceto o primeiro campo.
Dan Molding
2

awk '{ tmp = $1; sub(/^[^ ]+ +/, ""); print $0, tmp }'

Arkku
fonte
2

Vamos mover todos os registros para o próximo e definir o último como o primeiro:

$ awk '{a=$1; for (i=2; i<=NF; i++) $(i-1)=$i; $NF=a}1' file
United Arab Emirates AE
Antigua & Barbuda AG
Netherlands Antilles AN
American Samoa AS
Bosnia and Herzegovina BA
Burkina Faso BF
Brunei Darussalam BN

Explicação

  • a=$1 salve o primeiro valor em uma variável temporária.
  • for (i=2; i<=NF; i++) $(i-1)=$i salve o valor do campo enésimo no campo (N-1).
  • $NF=asalve o primeiro valor ( $1) no último campo.
  • {}1verdadeira condição para fazer awkexecutar a ação padrão: {print $0}.

Dessa forma, se você tiver outro separador de campo, o resultado também será bom:

$ cat c
AE-United-Arab-Emirates
AG-Antigua-&-Barbuda
AN-Netherlands-Antilles
AS-American-Samoa
BA-Bosnia-and-Herzegovina
BF-Burkina-Faso
BN-Brunei-Darussalam

$ awk 'BEGIN{OFS=FS="-"}{a=$1; for (i=2; i<=NF; i++) $(i-1)=$i; $NF=a}1' c
United-Arab-Emirates-AE
Antigua-&-Barbuda-AG
Netherlands-Antilles-AN
American-Samoa-AS
Bosnia-and-Herzegovina-BA
Burkina-Faso-BF
Brunei-Darussalam-BN
fedorqui 'então pare de prejudicar'
fonte
1

Uma primeira tentativa parece funcionar para o seu caso particular.

awk '{ f = $1; i = $NF; while (i <= 0); gsub(/^[A-Z][A-Z][ ][ ]/,""); print $i, f; }'
Wesley Rice
fonte
1

Opção 1

Existe uma solução que funciona com algumas versões do awk:

awk '{ $(NF+1)=$1;$1="";$0=$0;} NF=NF ' infile.txt

Explicação:

       $(NF+1)=$1                          # add a new field equal to field 1.
                  $1=""                    # erase the contents of field 1.
                        $0=$0;} NF=NF      # force a re-calc of fields.
                                           # and use NF to promote a print.

Resultado:

United Arab Emirates AE
Antigua & Barbuda AG
Netherlands Antilles AN
American Samoa AS
Bosnia and Herzegovina BA
Burkina Faso BF
Brunei Darussalam BN

No entanto, isso pode falhar com versões anteriores do awk.


opção 2

awk '{ $(NF+1)=$1;$1="";sub(OFS,"");}1' infile.txt

Isso é:

awk '{                                      # call awk.
       $(NF+1)=$1;                          # Add one trailing field.
                  $1="";                    # Erase first field.
                        sub(OFS,"");        # remove leading OFS.
                                    }1'     # print the line.

Observe que o que precisa ser apagado é o OFS, não o FS. A linha é recalculada quando o campo $ 1 é atribuído. Isso muda todas as execuções de FS para um OFS.


Mas mesmo essa opção ainda falha com vários delimitadores, como é claramente mostrado ao alterar o OFS:

awk -v OFS=';' '{ $(NF+1)=$1;$1="";sub(OFS,"");}1' infile.txt

Essa linha produzirá:

United;Arab;Emirates;AE
Antigua;&;Barbuda;AG
Netherlands;Antilles;AN
American;Samoa;AS
Bosnia;and;Herzegovina;BA
Burkina;Faso;BF
Brunei;Darussalam;BN

Isso revela que as execuções do FS estão sendo alteradas para um OFS.
A única maneira de evitar isso é evitar o recálculo do campo.
Uma função que pode evitar o recálculo é sub.
O primeiro campo pode ser capturado e removido de $ 0 com sub e, em seguida, ambos reimpressos.

Opção 3

awk '{ a=$1;sub("[^"FS"]+["FS"]+",""); print $0, a;}' infile.txt
       a=$1                                   # capture first field.
       sub( "                                 # replace: 
             [^"FS"]+                         # A run of non-FS
                     ["FS"]+                  # followed by a run of FS.
                            " , ""            # for nothing.
                                  )           # Default to $0 (the whole line.
       print $0, a                   # Print in reverse order, with OFS.


United Arab Emirates AE
Antigua & Barbuda AG
Netherlands Antilles AN
American Samoa AS
Bosnia and Herzegovina BA
Burkina Faso BF
Brunei Darussalam BN

Mesmo se mudarmos o FS, o OFS e / ou adicionarmos mais delimitadores, funciona.
Se o arquivo de entrada for alterado para:

AE..United....Arab....Emirates
AG..Antigua....&...Barbuda
AN..Netherlands...Antilles
AS..American...Samoa
BA..Bosnia...and...Herzegovina
BF..Burkina...Faso
BN..Brunei...Darussalam

E o comando muda para:

awk -vFS='.' -vOFS=';' '{a=$1;sub("[^"FS"]+["FS"]+",""); print $0,a;}' infile.txt

A saída será (ainda preservando os delimitadores):

United....Arab....Emirates;AE
Antigua....&...Barbuda;AG
Netherlands...Antilles;AN
American...Samoa;AS
Bosnia...and...Herzegovina;BA
Burkina...Faso;BF
Brunei...Darussalam;BN

O comando pode ser expandido para vários campos, mas apenas com awks modernos e com a opção --re-interval ativa. Este comando no arquivo original:

awk -vn=2 '{a=$1;b=$2;sub("([^"FS"]+["FS"]+){"n"}","");print $0,a,b;}' infile.txt

Irá produzir isto:

Arab Emirates AE United
& Barbuda AG Antigua
Antilles AN Netherlands
Samoa AS American
and Herzegovina BA Bosnia
Faso BF Burkina
Darussalam BN Brunei
Comunidade
fonte
1

Se você estiver aberto a outra solução Perl:

perl -ple 's/^(\S+)\s+(.*)/$2 $1/' file
Kjetil S.
fonte
0

Existe uma opção sed também ...

 sed 's/\([^ ]*\)  \(.*\)/\2 \1/' inputfile.txt

Explicado ...

Swap
\([^ ]*\) = Match anything until we reach a space, store in $1
\(.*\)    = Match everything else, store in $2
With
\2        = Retrieve $2
\1        = Retrieve $1

Mais detalhadamente explicado ...

s    = Swap
/    = Beginning of source pattern
\(   = start storing this value
[^ ] = text not matching the space character
*    = 0 or more of the previous pattern
\)   = stop storing this value
\(   = start storing this value
.    = any character
*    = 0 or more of the previous pattern
\)   = stop storing this value
/    = End of source pattern, beginning of replacement
\2   = Retrieve the 2nd stored value
\1   = Retrieve the 1st stored value
/    = end of replacement
ZeBadger
fonte
0

Ainda outra maneira ...

... isso reúne os campos 2 a NF com o FS e produz uma linha por linha de entrada

awk '{for (i=2;i<=NF;i++){printf $i; if (i < NF) {printf FS};}printf RS}'

Eu uso isso com o git para ver quais arquivos foram modificados em meu diretório de trabalho:

git diff| \
    grep '\-\-git'| \
    awk '{print$NF}'| \
    awk -F"/" '{for (i=2;i<=NF;i++){printf $i; if (i < NF) {printf FS};}printf RS}'
Rondó
fonte
-3

Outra maneira fácil de usar o comando cat

cat filename | awk '{print $2,$3,$4,$5,$6,$1}' > newfilename
Escorpião
fonte
Eu votei negativamente porque esta não é uma abordagem dinâmica. Com isso, você precisa saber o número de argumentos e assumir que seus dados são consistentes. Os dados quase nunca são consistentes e sua abordagem deve levar isso em consideração na maior parte do tempo.
xh3b4sd