Toma la n-ésima columna en un archivo de texto

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

¿Hay una manera más simple de hacer esto?

iirc:

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

o, como se menciona en los comentarios:

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

Puedes usar el comando de cut :

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

huellas dactilares

 1657 19.6117 1410 18.8302 3078 18.6695 2434 14.0508 3129 13.5495 

el

  • -d' ' - mean, usa el space como un delimitador
  • -f3,5 - tomar e imprimir 3ra y 5ta columna

El cut es mucho más rápido para archivos grandes como una solución de caparazón puro. Si su archivo está delimitado con múltiples espacios en blanco, puede eliminarlos primero, como:

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

donde (gnu) sed reemplazará cualquier tab o space caracteres con un solo space .

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

 perl -lanE 'say "$F[2] $F[4]"' < datafile.txt 

En aras de la exhaustividad:

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

En lugar de _ se puede usar una variable arbitraria (como junk ). 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 

Una variante más simple –

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

Si su archivo contiene n líneas, su secuencia de comandos tiene que leer el archivo n veces; así que si doblas la longitud del archivo, cuadruplicas la cantidad de trabajo que hace tu script, y casi todo ese trabajo simplemente se descarta, ya que todo lo que quieres hacer es recorrer las líneas en orden.

En su lugar, la mejor manera de recorrer las líneas de un archivo es usar un ciclo while, con el comando de condición siendo el comstackdo de read :

 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 versión incorporada de read realidad tiene soporte especial para rellenar 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ás haciendo puedes usar la herramienta de cut :

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

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

Si está utilizando datos estructurados, esto tiene el beneficio adicional de no invocar un proceso de shell adicional para ejecutar tr y / o cut o algo así. …

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

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