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"?
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.
$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"
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
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.
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).
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
.
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
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.
awk '{ tmp = $1; sub(/^[^ ]+ +/, ""); print $0, tmp }'
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
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
Si vous êtes ouvert à une autre solution Perl:
Perl -ple 's/^(\S+)\s+(.*)/$2 $1/' file
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; }'
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.
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.
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
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}'
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