Imprimer tout sauf le premier champ avec awk


108

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 j'aimerais inverser la commande, en imprimant d'abord tout sauf 1 $ puis 1 $:

United Arab Emirates AE

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


2
Salut @cfisher, cela peut être fait sans une boucle ang sans espace supplémentaire.
Juan Diego Godoy Robles

Réponses:


91

L'attribution $1fonctionne mais cela laissera un espace de premier plan:awk '{first = $1; $1 = ""; print $0, first; }'

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


2
Pour les totalement paresseux; voici le code de klashxx .
Serge Stroobandt

1
Génial. Débarrassez-vous de l'espace de tête avec sed: awk {'first = $1; $1=""; print $0'}|sed 's/^ //g'
Thyag

L'espace est facilement supprimé avec VIM en appuyant sur 'Ctrl + V Gd' en mode normal
Santi

107

$1=""laisse un espace comme Ben Jackson l'a mentionné, alors utilisez une forboucle:

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

Donc, si votre chaîne était "un deux trois", la sortie 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"


4
et un espace de fuite supplémentaire
NeronLeVelu

2
mieux utiliser: awk '{for(i=2;i<=NF;i++){ printf("%s",( (i>2) ? OFS : "" ) $i) } ; print ;}' qui: imprime les champs 2 à NF, ajoute le séparateur de champ de sortie si nécessaire (c'est-à-dire, sauf avant $ 2). La dernière impression ajoute une dernière nouvelle ligne pour mettre fin à l'impression de la ligne en cours. Celui-ci fonctionnera si vous changez FS / OFS (c'est-à-dire que ce ne sera pas toujours "espace")
Olivier Dulac

Le second a vraiment bien fonctionné pour moi. Le premier, pas tellement. Je ne sais pas vraiment pourquoi. Il a découpé tout le texte en dés.
voix

72

Utilisez la cutcommande avec l' --complementoption:

$ 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

2
Tout en ne répondant pas à la question spécifique à awk, j'ai trouvé cela très utile car awk supprimait les espaces en double, et couper ne le fait pas.
Fmstrat

19
echo a b c | cut -d' ' -f 2- est une alternative
Luis

2
Nice - La solution @Luis fonctionne sur le Mac, qui ne prend pas en charge --complement
metadaddy

21

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 "nouveau" dernier 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 champ en utilisant sub. L'impression finale est implicite.


13
awk '{sub($1 FS,"")}7' YourFile

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


Meilleure réponse! J'ai voté pour. En quoi est-ce différent de simplement utiliser 1? Je me demande l'utilisation de ce modèle et je voulais comprendre cela. Merci!
Abhijeet Rastogi le

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

Définir le premier champ sur ""laisse une seule copie de OFSau début de $0. En supposant qu'il OFSne s'agit que d'un seul caractère (par défaut, c'est un seul espace), nous pouvons le supprimer avec substr($0, 2). Ensuite, nous ajoutons la copie enregistrée de $1.


6

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

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

est une solution simple avec un séparateur 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

Ce prochain est un peu plus complexe

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

et suppose que le séparateur d'entrée / sortie est composé 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, n'imprime pas automatiquement chaque ligne

  • -l supprime les nouvelles lignes avant le traitement et les rajoute par la suite

  • -amode autosplit - divise les lignes d'entrée dans le tableau @F. Par défaut, diviser sur les espaces

  • -F modificateur autosplit, dans cet exemple se divise sur «» (deux espaces)

  • -e exécuter le code Perl suivant

@Fest le tableau de mots dans chaque ligne, indexé commençant par 0
$#Fest le nombre de mots dans @F
@F[1..$#F]est une tranche de tableau de l'élément 1 au dernier élément
@F[1..$#F,0]est une tranche de tableau de l'élément 1 au dernier élément plus l'élément 0


1
Je l'ai lancé et j'avais un numéro supplémentaire à la fin, j'ai donc utilisé cette version: perl -lane 'shift @F; print join "", @F '
Hans Poo

2

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

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

Cela fait deux espaces entre les guillemets.


Meilleure réponse à la situation actuelle, mais, techniquement, cela ne répond pas à la question de savoir comment tout imprimer sauf le premier champ.
Dan Moulding

@DanMoulding: Tant que le fichier est cohérent dans l'utilisation de deux espaces pour séparer le code du pays et qu'il n'y a pas d'autres occurrences de deux espaces ensemble, ma réponse répond à la question.
Suspendu jusqu'à nouvel ordre.

2
Les personnes qui se posent sur cette question arrivent ici parce qu'elles veulent savoir comment tout imprimer sauf le premier champ (voir le titre de la question). C'est comme ça que j'ai atterri ici. Votre réponse montre comment imprimer le premier champ suivi du deuxième champ. Bien que ce soit probablement la meilleure solution à la situation particulière de l'OP, cela ne résout pas le problème général de la façon d'imprimer tout sauf le premier champ.
Dan Moulding

2

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


2

Déplaçons tous les enregistrements vers le 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 enregistrer la Nième valeur de champ dans le (N-1) ième champ.
  • $NF=aenregistrez la première valeur ( $1) dans le dernier champ.
  • {}1véritable condition de faire awkexécuter l'action par défaut: {print $0}.

De cette façon, si vous avez un autre séparateur de champ, 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

1

Une première tentative semble fonctionner pour votre cas particulier.

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

1

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 peut é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é, c'est l'OFS, pas le FS. La ligne est recalculée lorsque le champ $ 1 est attribué. Cela change toutes les exécutions de FS en un seul OFS.


Mais même cette option échoue toujours avec plusieurs délimiteurs, comme le montre clairement la modification 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 séries de FS sont changées en un seul OFS.
Le seul moyen d'éviter cela est d'éviter le recalcul du champ.
Une fonction qui peut éviter le recalcul est sub.
Le premier champ pourrait être capturé, puis supprimé de 0 $ avec sous, puis 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 modifions le FS, l'OFS et / ou ajoutons plus de délimiteurs, cela fonctionne.
Si le fichier d'entrée est changé 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 se transforme en:

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

La sortie sera (toujours en préservant 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 --re-intervalle active. Cette commande sur le fichier d'origine:

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

Sortira 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

Si vous êtes ouvert à une autre solution Perl:

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

0

Il y a aussi une option sed ...

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

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

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 ceci 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}'

-3

Un autre moyen simple d'utiliser la commande cat

cat filename | awk '{print $2,$3,$4,$5,$6,$1}' > newfilename

J'ai voté contre parce que ce n'est pas une approche dynamique. Avec cela, vous devez connaître le nombre d'arguments et supposer que vos données sont cohérentes. Les données ne sont presque jamais cohérentes et votre approche doit en tenir compte la plupart du temps.
xh3b4sd
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.