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"?

cfischer
la source
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.

Ben Jackson
la source
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"

7winkie
la source
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
zeleniy
la source
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.

Juan Diego Godoy Robles
la source
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).

NeronLeVelu
la source
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.

dubiousjim
la source
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

Chris Koknat
la source
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.

Suspendu jusqu'à nouvel ordre.
la source
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 }'

Arkku
la source
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
fedorqui 'Alors arrêtez de nuire'
la source
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; }'
Wesley Rice
la source
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
Communauté
la source
1

Si vous êtes ouvert à une autre solution Perl:

perl -ple 's/^(\S+)\s+(.*)/$2 $1/' file
Kjetil S.
la source
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
ZeBadger
la source
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}'
Rondo
la source
-3

Un autre moyen simple d'utiliser la commande cat

cat filename | awk '{print $2,$3,$4,$5,$6,$1}' > newfilename
Scorpion
la source
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