Tomar la enésima columna en un archivo de texto

86

Tengo un archivo de texto:

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

Quiero tomar la segunda y cuarta palabra de cada línea como esta:

1657 19.6117
1410 18.8302
3078 18.6695
2434 14.0508
3129 13.5495

Estoy usando este código:

 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

Funciona, pero es muy complicado y lleva mucho tiempo procesar archivos de texto largos.

¿Existe una forma más sencilla de hacer esto?

mnrl
fuente
1
La segunda palabra de cada fila se llama simplemente segunda columna.
Bernard

Respuestas:

127

iirc:

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

o, como se menciona en los comentarios:

awk '{ print $2 $4 }' filename.txt
Tom van der Woerdt
fuente
16
UUOC !!! awk '{print $2,$4}' filename.txtes mejor (sin tubería, solo se llama un programa)
azul
5
@blue A menudo uso caten mis scripts bash en lugar de especificar un nombre de archivo, porque la sobrecarga es mínima y porque la sintaxis cat ... | ... > ...muestra muy bien cuál es la entrada y hacia dónde va la salida. Sin embargo, tiene razón, en realidad no es necesario aquí.
Tom van der Woerdt
8
@TomvanderWoerdt: A veces escribo < input awk '{ print $2 $4 }' > outputcon ese propósito.
ruakh
69

Puedes usar el cutcomando:

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

huellas dactilares

1657 19.6117
1410 18.8302
3078 18.6695
2434 14.0508
3129 13.5495

los

  • -d' '- es decir, utilizar spacecomo delimitador
  • -f3,5 - tomar e imprimir la tercera y quinta columna

El cutes mucho más rápido de archivos de gran tamaño como una solución pura cáscara. Si su archivo está delimitado con varios espacios en blanco, puede eliminarlos primero, como:

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

donde el (gnu) sed reemplazará cualquier carácter tabo spacecon un solo space.

Para una variante, aquí también hay una solución de Perl:

perl -lanE 'say "$F[2] $F[4]"' < datafile.txt
jm666
fuente
1
Funciona bien ... si tiene garantizado ese número de espacios en cada línea, exactamente ... :)
rogerdpack
24

En aras de la exhaustividad:

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

En lugar de _una variable arbitraria (como junk) también se puede utilizar. El punto es solo extraer las columnas.

Manifestación:

$ 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
fuente
Agradable, legible y sin perls / awks / otros necesarios, todo en un shell por elementos incorporados.
Petr Matousu
6

Una variante más simple:

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

Si su archivo contiene n líneas, entonces su secuencia de comandos debe leer el archivo n veces; por lo que si duplica la longitud del archivo, cuadriplica la cantidad de trabajo que hace su script, y casi todo ese trabajo simplemente se tira a la basura, ya que todo lo que desea hacer es recorrer las líneas en orden.

En cambio, la mejor manera de recorrer las líneas de un archivo es usar un whilebucle, con el comando de condición readintegrado:

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

En su caso, dado que desea dividir la línea en una matriz, y la función readincorporada en realidad tiene un soporte especial para completar una variable de matriz, que es lo que desea, puede escribir:

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

o mejor aún:

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

Sin embargo, para lo que está haciendo, puede usar la cututilidad:

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

(o awk, como sugiere Tom van der Woerdt, o perl, o incluso sed).

ruakh
fuente
preferirían readmás cutporque es robusto frente a múltiples espacios entre los campos y que no necesita la magia matriz:while read word1 word2 word3 word4 rest; do doSomethingWith $word2 $word4; done
user829755
3

Si está utilizando datos estructurados, esto tiene el beneficio adicional de no invocar un proceso de shell adicional para ejecutar try / cuto algo. ...

(Por supuesto, querrá protegerse contra las malas entradas con condicionales y alternativas sensatas).

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