web-dev-qa-db-fra.com

Tout imprimer sauf le premier champ avec awk

J'ai un fichier qui ressemble à ceci:

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

Et je voudrais inverser la commande, en imprimant d’abord tout sauf les $ 1 puis $ 1:

United Arab Emirates AE

Comment puis-je faire le tour "tout sauf le champ 1"?

94
cfischer

Assigner $1 _ fonctionne mais il laissera un espace de tête: awk '{first = $1; $1 = ""; print $0, first; }'

Vous pouvez également trouver le nombre de colonnes dans NF et l'utiliser dans une boucle.

79
Ben Jackson

$1="" laisse un espace comme mentionné par Ben Jackson, utilisez donc une boucle for:

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

Donc, si votre chaîne était "un deux trois", le résultat sera:

deux
Trois

Si vous voulez le résultat sur une ligne, vous pouvez procéder comme suit:

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

Cela vous donnera: "deux trois"

100
7winkie

Utilisez la commande cut avec le --complement option:

$ 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
61
zeleniy

Peut-être la manière la plus concise:

$ 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

Explication:

$(NF+1)=$1: générateur d'un dernier "nouveau" champ.

$1="": Définissez le premier champ d'origine sur null

sub(FS,""): après les deux premières actions {$(NF+1)=$1;$1=""}, supprimez le premier séparateur de champs à l'aide de sub. L'impression finale est implicite.

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

Supprimez le premier champ et le séparateur, puis imprimez le résultat (7 est une valeur non nulle, donc $ 0).

13
NeronLeVelu
awk '{ saved = $1; $1 = ""; print substr($0, 2), saved }'

Définir le premier champ sur "" Laisse une copie unique de OFS au début de $0. En supposant que OFS ne soit qu'un seul caractère (par défaut, il s’agit d’un seul espace), nous pouvons le supprimer avec substr($0, 2). Ensuite, nous ajoutons la copie sauvegardée de $1.

9
dubiousjim

Si vous êtes ouvert à une solution Perl ...

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

est une solution simple avec un séparateur d'entrée/sortie d'un espace, qui produit:

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

Le suivant est légèrement plus complexe

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

et suppose que le séparateur d'entrée/sortie est constitué de deux espaces:

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

Ces options de ligne de commande sont utilisées:

  • -n boucle autour de chaque ligne du fichier d'entrée, ne pas imprimer automatiquement chaque ligne

  • -l supprime les nouvelles lignes avant le traitement et les rajoute ensuite

  • -a mode autosplit - divise les lignes d’entrée dans le tableau @F. Fractionnement par défaut sur les espaces

  • -F modificateur autosplit, dans cet exemple, divise sur '' (deux espaces)

  • -e exécuter le code Perl suivant

@F est le tableau de mots de chaque ligne, indexé à partir de 0
$#F est le nombre de mots dans @F
@F[1..$#F] est une tranche de l’élément 1 du dernier élément
@F[1..$#F,0] est une découpe de l’élément 1 du dernier élément plus l’élément 0

5
Chris Koknat

Le séparateur de champ dans gawk (au moins) peut être une chaîne, ainsi qu'un caractère (il peut également s'agir d'une expression régulière). Si vos données sont cohérentes, cela fonctionnera:

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

C'est deux espaces entre les guillemets.

2
Dennis Williamson

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

2
Arkku

Passons tous les enregistrements au suivant et définissons le dernier comme premier:

$ 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

Explication

  • a=$1 Enregistrez la première valeur dans une variable temporaire.
  • for (i=2; i<=NF; i++) $(i-1)=$i enregistre la valeur du Nième champ dans le (N-1) ème champ.
  • $NF=a Enregistrez la première valeur ($1) Dans le dernier champ.
  • {}1 True condition pour que awk effectue l'action par défaut: {print $0}.

De cette façon, si vous avez un autre séparateur de champs, le résultat est également bon:

$ 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
2
fedorqui

Si vous êtes ouvert à une autre solution Perl:

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

Un premier essai semble fonctionner dans votre cas particulier.

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

Option 1

Il existe une solution qui fonctionne avec certaines versions de awk:

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

Explication:

       $(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.

Résultat:

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

Cependant, cela pourrait échouer avec les anciennes versions de awk.


Option 2

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

C'est:

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

Notez que ce qui doit être effacé est l’OFS, pas le FS. La ligne est recalculée lorsque le champ $ 1 est assigné. Cela change toutes les exécutions de FS en un OFS.


Mais même cette option échoue toujours avec plusieurs délimiteurs, comme le montre clairement le changement de l'OFS:

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

Cette ligne affichera:

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

Cela révèle que les exécutions de FS sont remplacées par un seul OFS.
Le seul moyen d'éviter cela est d'éviter le nouveau calcul du champ.
Une fonction qui peut éviter de re-calculer est sub.
Le premier champ pourrait être capturé, puis retiré de $ 0 avec un sous-objet, puis tous les deux réimprimés.

Option 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

Même si nous changeons le FS, le OFS et/ou ajoutons plus de délimiteurs, cela fonctionne.
Si le fichier d'entrée est modifié en:

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

Et la commande devient:

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

La sortie sera (en conservant toujours les délimiteurs):

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

La commande peut être étendue à plusieurs champs, mais uniquement avec les awks modernes et avec l'option active --re-interval. Cette commande sur le fichier d'origine:

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

Va afficher ceci:

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

Encore une autre façon ...

... cela rejoint les champs 2 à NF avec le FS) et génère une ligne par ligne d'entrée

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

J'utilise cela avec git pour voir quels fichiers ont été modifiés dans mon répertoire de travail:

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

Il y a une option sed aussi ...

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

A expliqué ...

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

Plus en détail expliqué ...

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
0
ZeBadger