Réorganiser les colonnes à l'aide de la coupe

135

J'ai un fichier au format suivant

Colonne1 Colonne2
str1 1
str2 2
str3 3

Je veux que les colonnes soient réorganisées. J'ai essayé ci-dessous la commande

couper -f2,1 fichier.txt

La commande ne réorganise pas les colonnes. Une idée pourquoi ça ne marche pas?

Je vous remercie.

Booléen
la source

Réponses:

148

Pour la cut(1)page de manuel:

Utilisez un et un seul parmi -b, -c ou -f. Chaque LISTE est composée d'une plage ou de plusieurs plages séparées par des virgules. L'entrée sélectionnée est écrite dans le même ordre où elle est lue et est écrite exactement une fois.

Il atteint d'abord le champ 1, de sorte qu'il est imprimé, suivi du champ 2.

Utilisez à la awkplace:

awk '{ print $2 " " $1}' file.txt
Ignacio Vazquez-Abrams
la source
12
Il est dommage de cutne pas prendre en charge cette commande de réorganisation intuitive. Quoi qu'il en soit, une autre astuce: vous pouvez utiliser awkles options -FSet -OFSpour utiliser des séparateurs de champs d'entrée et de sortie personnalisés (comme -det --output-delimiterpour cut).
malana
12
Désolé, FSest une option, OFSest une variable. egawk -v OFS=";" -F"\t" '{print $2,$1}'
malana
2
Note aux utilisateurs Windows de Git Bash: si vous avez une sortie étrange de la commande ci-dessus, ressemblant à des colonnes se remplaçant, le retour chariot est à blâmer. Changez EOL dans votre fichier de CRLF à LF.
jakub.g
1
Alternativement, si vous ne voulez pas changer le fichier d'entrée, vous pouvez le faire passer | sed 's/\r//' | avant de passer àawk
jakub.g
2
Celui-ci est très simple mais pourrait être utile pour certains, remplacez simplement l'espace par \ t pour réorganiser par onglets, et au cas où vous voudriez plus de colonnes, vous pouvez le faire comme par exempleawk '{print $4 "\t" $2 "\t" $6 "\t" $7}' file
FatihSarigol
64

Vous pouvez également combiner cutet paste:

paste <(cut -f2 file.txt) <(cut -f1 file.txt)

via les commentaires: Il est possible d'éviter les bashismes et de supprimer une instance de coupe en faisant:

paste file.txt file.txt | cut -f2,3
Justin Kaeser
la source
3
Je ne sais pas si cela est qualifié de "intelligemment", mais: f = file.txt paste <(cut -f2 $ f) <(cut -f1 $ f). De plus, je note que cette méthode est la plus simple lorsque vous avez beaucoup de colonnes et que vous souhaitez en déplacer de gros blocs.
Michael Rusch
ne fonctionne pas avec des cellules de longueurs variables dans la même colonne
kraymer
2
@kraymer Que voulez-vous dire? cutfonctionne très bien pour les colonnes de longueur variable tant que vous avez un séparateur de colonne unique.
tripleee
1
Pour éliminer le fichier redondant, vous pouvez probablement utiliser tee:
JJW5432
2
Il est possible d'éviter les bashisms et de supprimer une instance de cuten faisant: paste file.txt file.txt | cut -f2,3
agc
7

en utilisant juste la coque,

while read -r col1 col2
do
  echo $col2 $col1
done <"file"
ghostdog74
la source
Ceci est très souvent inefficace. En règle générale, vous constaterez que le script Awk correspondant est beaucoup plus rapide, par exemple. Vous devez également faire attention à citer les valeurs "$col2"et "$col1"- il pourrait y avoir des métacaractères shell ou d'autres manigances dans les données.
tripleee
7

Vous pouvez utiliser Perl pour cela:

perl -ane 'print "$F[1] $F[0]\n"' < file.txt
  • -e option signifie exécuter la commande après
  • -n signifie lire ligne par ligne (ouvrir le fichier, dans ce cas STDOUT, et boucler sur les lignes)
  • -a signifie diviser ces lignes en un vecteur appelé @F ("F" - comme Field). Perl indexe les vecteurs à partir de 0 contrairement à cut qui indexe les champs à partir de la forme 1.
  • Vous pouvez ajouter -F pattern (sans espace entre -F et pattern ) pour utiliser pattern comme séparateur de champ lors de la lecture du fichier au lieu de l'espace blanc par défaut

L'avantage d'exécuter perl est que (si vous connaissez Perl) vous pouvez faire beaucoup plus de calculs sur F que de réorganiser les colonnes.

Rencontré
la source
perlrun (1) revendique -a définit implicitement -n mais si je lance sans -n set, il ne semble pas boucler. impair.
Trenton le
Quelle version? perl -ae printfonctionne comme catpour moi
pwes
5

Utilisation join:

join -t $'\t' -o 1.2,1.1 file.txt file.txt

Remarques:

  • -t $'\t'Dans GNU join le plus intuitif -t '\t' sans les $échecs, ( coreutils v8.28 et antérieurs?); c'est probablement un bogue qu'une solution de contournement comme $devrait être nécessaire. Voir: unix join separator char .

  • joina besoin de deux noms de fichiers, même s'il n'y a qu'un seul fichier en cours de traitement. Utiliser deux fois le même nom permet d' joineffectuer l'action souhaitée.

  • Pour les systèmes à faibles ressources, il joinoffre une empreinte plus petite que certains des outils utilisés dans d'autres réponses:

    wc -c $(realpath `which cut join sed awk perl`) | head -n -1
      43224 /usr/bin/cut
      47320 /usr/bin/join
     109840 /bin/sed
     658072 /usr/bin/gawk
    2093624 /usr/bin/perl
agc
la source
3

Je viens de travailler sur quelque chose de très similaire, je ne suis pas un expert mais je pensais partager les commandes que j'ai utilisées. J'avais un csv multi-colonnes dont je n'avais besoin que de 4 colonnes, puis je devais les réorganiser.

Mon fichier était pipe '|' délimité mais qui peut être échangé.

LC_ALL=C cut -d$'|' -f1,2,3,8,10 ./file/location.txt | sed -E "s/(.*)\|(.*)\|(.*)\|(.*)\|(.*)/\3\|\5\|\1\|\2\|\4/" > ./newcsv.csv

Certes, c'est vraiment rugueux et prêt, mais il peut être modifié pour convenir!

Chris Rymer
la source
Cela ne répond pas à la question posée. Dans l'esprit du débordement de pile, veuillez prendre le temps de répondre à un problème avant de publier.
Bill Gale le
0

Utilisation de sed

Utilisez sed avec les sous-expressions imbriquées des expressions régulières de base pour capturer et réorganiser le contenu de la colonne. Cette approche est la mieux adaptée lorsqu'il y a un nombre limité de coupes pour réorganiser les colonnes, comme dans ce cas.

L'idée de base est d'entourer les parties intéressantes du motif de recherche avec \(et \), qui peuvent être lues dans le motif de remplacement avec \##représente la position séquentielle de la sous-expression dans le motif de recherche.

Par exemple:

$ echo "foo bar" | sed "s/\(foo\) \(bar\)/\2 \1/"

donne:

bar foo

Le texte en dehors d'une sous-expression est analysé mais pas conservé pour la lecture dans la chaîne de remplacement.

Bien que la question n'ait pas abordé les colonnes à largeur fixe, nous en discuterons ici car c'est une mesure digne de toute solution posée. Pour simplifier, supposons que le fichier soit délimité par des espaces bien que la solution puisse être étendue pour d'autres délimiteurs.

Espaces réduits

Pour illustrer l'utilisation la plus simple, supposons que plusieurs espaces peuvent être réduits en espaces simples et que les valeurs de la deuxième colonne se terminent par EOL (et non par un espace).

Fichier:

bash-3.2$ cat f
Column1    Column2
str1       1
str2       2
str3       3
bash-3.2$ od -a f
0000000    C   o   l   u   m   n   1  sp  sp  sp  sp   C   o   l   u   m
0000020    n   2  nl   s   t   r   1  sp  sp  sp  sp  sp  sp  sp   1  nl
0000040    s   t   r   2  sp  sp  sp  sp  sp  sp  sp   2  nl   s   t   r
0000060    3  sp  sp  sp  sp  sp  sp  sp   3  nl 
0000072

Transformer:

bash-3.2$ sed "s/\([^ ]*\)[ ]*\([^ ]*\)[ ]*/\2 \1/" f
Column2 Column1
1 str1
2 str2
3 str3
bash-3.2$ sed "s/\([^ ]*\)[ ]*\([^ ]*\)[ ]*/\2 \1/" f | od -a
0000000    C   o   l   u   m   n   2  sp   C   o   l   u   m   n   1  nl
0000020    1  sp   s   t   r   1  nl   2  sp   s   t   r   2  nl   3  sp
0000040    s   t   r   3  nl
0000045

Préservation des largeurs de colonne

Étendons maintenant la méthode à un fichier avec des colonnes de largeur constante, tout en permettant aux colonnes d'être de largeurs différentes.

Fichier:

bash-3.2$ cat f2
Column1    Column2
str1       1
str2       2
str3       3
bash-3.2$ od -a f2
0000000    C   o   l   u   m   n   1  sp  sp  sp  sp   C   o   l   u   m
0000020    n   2  nl   s   t   r   1  sp  sp  sp  sp  sp  sp  sp   1  sp
0000040   sp  sp  sp  sp  sp  nl   s   t   r   2  sp  sp  sp  sp  sp  sp
0000060   sp   2  sp  sp  sp  sp  sp  sp  nl   s   t   r   3  sp  sp  sp
0000100   sp  sp  sp  sp   3  sp  sp  sp  sp  sp  sp  nl
0000114

Transformer:

bash-3.2$ sed "s/\([^ ]*\)\([ ]*\) \([^ ]*\)\([ ]*\)/\3\4 \1\2/" f2
Column2 Column1
1       str1      
2       str2      
3       str3      
bash-3.2$ sed "s/\([^ ]*\)\([ ]*\) \([^ ]*\)\([ ]*\)/\3\4 \1\2/" f2 | od -a
0000000    C   o   l   u   m   n   2  sp   C   o   l   u   m   n   1  sp
0000020   sp  sp  nl   1  sp  sp  sp  sp  sp  sp  sp   s   t   r   1  sp
0000040   sp  sp  sp  sp  sp  nl   2  sp  sp  sp  sp  sp  sp  sp   s   t
0000060    r   2  sp  sp  sp  sp  sp  sp  nl   3  sp  sp  sp  sp  sp  sp
0000100   sp   s   t   r   3  sp  sp  sp  sp  sp  sp  nl 
0000114

Enfin, bien que l'exemple de la question n'ait pas de chaînes de longueur inégale, cette expression sed prend en charge ce cas.

Fichier:

bash-3.2$ cat f3
Column1    Column2
str1       1      
string2    2      
str3       3      

Transformer:

bash-3.2$ sed "s/\([^ ]*\)\([ ]*\) \([^ ]*\)\([ ]*\)/\3\4 \1\2/" f3
Column2 Column1   
1       str1      
2       string2   
3       str3    
bash-3.2$ sed "s/\([^ ]*\)\([ ]*\) \([^ ]*\)\([ ]*\)/\3\4 \1\2/" f3 | od -a
0000000    C   o   l   u   m   n   2  sp   C   o   l   u   m   n   1  sp
0000020   sp  sp  nl   1  sp  sp  sp  sp  sp  sp  sp   s   t   r   1  sp
0000040   sp  sp  sp  sp  sp  nl   2  sp  sp  sp  sp  sp  sp  sp   s   t
0000060    r   i   n   g   2  sp  sp  sp  nl   3  sp  sp  sp  sp  sp  sp
0000100   sp   s   t   r   3  sp  sp  sp  sp  sp  sp  nl 
0000114

Comparaison avec d'autres méthodes de réorganisation des colonnes sous shell

  • Étonnamment pour un outil de manipulation de fichiers, awk n'est pas bien adapté pour couper d'un champ à la fin d'un enregistrement. Dans sed, cela peut être accompli en utilisant des expressions régulières, par exemple \(xxx.*$\)xxxest l'expression correspondant à la colonne.

  • L'utilisation de sous-shell coller et couper devient délicate lors de l'implémentation de scripts shell internes. Le code qui fonctionne à partir de la ligne de commande ne parvient pas à être analysé lorsqu'il est introduit dans un script shell. Au moins c'était mon expérience (qui m'a poussé à cette approche).

Bill Gale
la source
0

Extension de la réponse de @Met, également en utilisant Perl:
Si l'entrée et la sortie sont délimitées par TAB:

perl -F'\t' -lane 'print join "\t", @F[1, 0]' in_file

Si l'entrée et la sortie sont séparées par des espaces:

perl -lane 'print join " ", @F[1, 0]' in_file

Ici,
-eindique à Perl de rechercher le code en ligne, plutôt que dans un fichier de script séparé,
-nlit l'entrée 1 ligne à la fois,
-lsupprime le séparateur d'enregistrement d'entrée ( \nsur * NIX) après avoir lu la ligne (similaire à chomp), et ajoute la sortie séparateur d'enregistrement ( \nsur * NIX) à chacun print,
-adivise la ligne d'entrée sur les espaces en tableau @F,
-F'\t'en combinaison avec -adivise la ligne d'entrée sur les TAB, au lieu des espaces en tableau @F.

@F[1, 0]est le tableau composé des 2e et 1er éléments du tableau @F, dans cet ordre. N'oubliez pas que les tableaux en Perl sont indexés à zéro, tandis que les champs dans cutsont indexés à 1. Les champs de @F[0, 1]sont donc les mêmes que ceux de cut -f1,2.

Notez qu'une telle notation permet une manipulation plus flexible de l'entrée que dans certaines autres réponses publiées ci-dessus (ce qui est bien pour une tâche simple). Par exemple:

# reverses the order of fields:
perl -F'\t' -lane 'print join "\t", reverse @F' in_file

# prints last and first fields only:
perl -F'\t' -lane 'print join "\t", @F[-1, 0]' in_file
Timur Shtatland
la source