Prendre la nième colonne dans un fichier texte

86

J'ai un fichier texte:

1 Q0 1657 1 19.6117 Exp
1 Q0 1410 2 18.8302 Exp
2 Q0 3078 1 18.6695 Exp
2 Q0 2434 2 14.0508 Exp
2 Q0 3129 3 13.5495 Exp

Je veux prendre le 2ème et 4ème mot de chaque ligne comme ceci:

1657 19.6117
1410 18.8302
3078 18.6695
2434 14.0508
3129 13.5495

J'utilise ce code:

 nol=$(cat "/path/of/my/text" | wc -l)
 x=1
 while  [ $x -le "$nol" ]
 do
     line=($(sed -n "$x"p /path/of/my/text)
     echo ""${line[1]}" "${line[3]}""  >> out.txt
     x=$(( $x + 1 ))
 done

Cela fonctionne, mais c'est très compliqué et prend beaucoup de temps pour traiter de longs fichiers texte.

Existe-t-il un moyen plus simple de procéder?

mnrl
la source
1
2ème mot de chaque ligne appelé simplement 2ème colonne!
Bernard

Réponses:

127

iirc:

cat filename.txt | awk '{ print $2 $4 }'

ou, comme mentionné dans les commentaires:

awk '{ print $2 $4 }' filename.txt
Tom van der Woerdt
la source
16
UUOC !!! awk '{print $2,$4}' filename.txtc'est mieux (pas de pipe, juste un programme appelé)
bleu
5
@blue J'utilise souvent catdans mes scripts bash au lieu de spécifier un nom de fichier, car la surcharge est minime et parce que la syntaxe cat ... | ... > ...montre très bien ce qu'est l'entrée et où va la sortie. Vous avez raison cependant, ce n'est pas vraiment nécessaire ici.
Tom van der Woerdt
8
@TomvanderWoerdt: J'écris parfois < input awk '{ print $2 $4 }' > outputdans ce but.
ruakh
69

Vous pouvez utiliser la cutcommande:

cut -d' ' -f3,5 < datafile.txt

impressions

1657 19.6117
1410 18.8302
3078 18.6695
2434 14.0508
3129 13.5495

la

  • -d' '- signifie, utiliser spacecomme délimiteur
  • -f3,5 - prendre et imprimer la 3e et la 5e colonne

le cut est beaucoup plus rapide pour les gros fichiers en tant que solution shell pure. Si votre fichier est délimité par plusieurs espaces, vous pouvez d'abord les supprimer, comme:

sed 's/[\t ][\t ]*/ /g' < datafile.txt | cut -d' ' -f3,5

où le (gnu) sed remplacera tout tabspace caractères ou par un seul space.

Pour une variante - voici aussi une solution perl:

perl -lanE 'say "$F[2] $F[4]"' < datafile.txt
jm666
la source
1
Fonctionne bien ... si vous êtes assuré de ce nombre d'espaces sur chaque ligne, exactement ... :)
rogerdpack
24

Par souci d'exhaustivité:

while read _ _ one _ two _; do
    echo "$one $two"
done < file.txt

Au lieu de _ une variable arbitraire (telle quejunk ), vous pouvez également utiliser. Le but est juste d'extraire les colonnes.

Démo:

$ while read _ _ one _ two _; do echo "$one $two"; done < /tmp/file.txt
1657 19.6117
1410 18.8302
3078 18.6695
2434 14.0508
3129 13.5495
Johannes Weiss
la source
Nice, lisible, et pas de perls / awks / autres nécessaires, le tout dans un seul shell par builtins.
Petr Matousu le
6

Une variante plus simple -

$ while read line
  do
      set $line          # assigns words in line to positional parameters
      echo "$3 $5"
  done < file
AKA11
la source
4

Si votre fichier contient n lignes, votre script doit lire le fichier n fois; Donc, si vous doublez la longueur du fichier, vous quadruplez la quantité de travail que votre script fait - et presque tout ce travail est simplement jeté, car tout ce que vous voulez faire est de boucler les lignes dans l'ordre.

Au lieu de cela, la meilleure façon de boucler sur les lignes d'un fichier est d'utiliser une whileboucle, la commande condition étant la commande readinterne:

while IFS= read -r line ; do
    # $line is a single line of the file, as a single string
    : ... commands that use $line ...
done < input_file.txt

Dans votre cas, puisque vous voulez diviser la ligne en un tableau, et que le readbuiltin a en fait un support spécial pour remplir une variable de tableau, ce que vous voulez, vous pouvez écrire:

while read -r -a line ; do
    echo ""${line[1]}" "${line[3]}"" >> out.txt
done < /path/of/my/text

ou mieux encore:

while read -r -a line ; do
    echo "${line[1]} ${line[3]}"
done < /path/of/my/text > out.txt

Cependant, pour ce que vous faites, vous pouvez simplement utiliser l' cututilitaire:

cut -d' ' -f2,4 < /path/of/my/text > out.txt

(ou awk, comme le suggère Tom van der Woerdt, ou perl, ou même sed).

Ruakh
la source
préférerait readplutôt cutparce qu'il est robuste contre plusieurs espaces entre les champs et que vous n'avez pas besoin de magie de tableau:while read word1 word2 word3 word4 rest; do doSomethingWith $word2 $word4; done
user829755
3

Si vous utilisez des données structurées, cela présente l'avantage supplémentaire de ne pas appeler un processus shell supplémentaire à exécuter tret / oucut ou quelque chose. ...

(Bien sûr, vous voudrez vous protéger contre les mauvaises entrées avec des conditions et des alternatives saines.)

...
while read line ; 
do 
    lineCols=( $line ) ;
    echo "${lineCols[0]}"
    echo "${lineCols[1]}"
done < $myFQFileToRead ; 
...
ingyhere
la source