Ambil kolom ke-n dalam file teks

85

Saya memiliki file teks:

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

Saya ingin mengambil kata ke-2 dan ke-4 dari setiap baris seperti ini:

1657 19.6117
1410 18.8302
3078 18.6695
2434 14.0508
3129 13.5495

Saya menggunakan kode ini:

 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

Ini berfungsi, tetapi sangat rumit dan membutuhkan waktu lama untuk memproses file teks yang panjang.

Apakah ada cara yang lebih sederhana untuk melakukan ini?

mnrl
sumber
1
Kata ke-2 dari setiap baris disebut kolom ke-2!
Bernard

Jawaban:

127

iirc:

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

atau, seperti yang disebutkan di komentar:

awk '{ print $2 $4 }' filename.txt
Tom van der Woerdt
sumber
16
UUOC !!! awk '{print $2,$4}' filename.txtlebih baik (tidak ada pipa, hanya satu program yang dipanggil)
biru
5
@blue Saya sering menggunakan catskrip bash saya daripada menentukan nama file, karena overhead minimal dan karena sintaks cat ... | ... > ...menunjukkan dengan sangat baik apa input dan ke mana output pergi. Anda benar, sebenarnya tidak diperlukan di sini.
Tom van der Woerdt
8
@TomvanderWoerdt: Saya terkadang menulis < input awk '{ print $2 $4 }' > outputuntuk tujuan itu.
ruakh
68

Anda dapat menggunakan cutperintah:

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

cetakan

1657 19.6117
1410 18.8302
3078 18.6695
2434 14.0508
3129 13.5495

itu

  • -d' '- maksudnya, gunakan spacesebagai pembatas
  • -f3,5 - ambil dan cetak kolom ke-3 dan ke-5

The cutadalah jauh lebih cepat untuk file besar sebagai solusi shell murni. Jika file Anda dibatasi dengan beberapa spasi, Anda dapat menghapusnya terlebih dahulu, seperti:

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

di mana (gnu) sed akan mengganti karakter tabatau spacedengan satu space.

Untuk varian - berikut adalah solusi perl juga:

perl -lanE 'say "$F[2] $F[4]"' < datafile.txt
jm666
sumber
1
Bekerja dengan baik ... jika Anda dijamin dengan jumlah spasi di setiap baris, tepatnya ... :)
rogerdpack
24

Demi kelengkapan:

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

Alih-alih _variabel arbitrer (sepertijunk ) dapat digunakan juga. Intinya adalah mengekstrak kolom.

Demo:

$ 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
sumber
Bagus, mudah dibaca, dan tidak perlu perls / awks / lainnya, semuanya dalam satu shell oleh bawaan.
Petr Matousu
6

Satu varian yang lebih sederhana -

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

Jika file Anda berisi n baris, maka skrip Anda harus membaca file n sebanyak kali; jadi jika Anda menggandakan panjang file, Anda melipatgandakan jumlah pekerjaan yang dilakukan skrip Anda - dan hampir semua pekerjaan itu dibuang begitu saja, karena yang ingin Anda lakukan hanyalah mengulang baris secara berurutan.

Sebaliknya, cara terbaik untuk mengulang baris file adalah dengan menggunakan whileloop, dengan perintah kondisi sebagai readbawaannya:

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

Dalam kasus Anda, karena Anda ingin membagi baris menjadi array, dan readbuiltin sebenarnya memiliki dukungan khusus untuk mengisi variabel array, yang Anda inginkan, Anda dapat menulis:

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

atau lebih baik lagi:

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

Namun, untuk apa yang Anda lakukan, Anda dapat menggunakan cututilitas:

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

(atau awk, seperti yang disarankan Tom van der Woerdt, atau perl, atau bahkan sed).

ruakh
sumber
lebih suka readlebih cutkarena kuat melawan banyak spasi antara bidang dan Anda tidak memerlukan sihir array:while read word1 word2 word3 word4 rest; do doSomethingWith $word2 $word4; done
user829755
3

Jika Anda menggunakan data terstruktur, manfaat tambahannya adalah tidak meminta proses shell ekstra untuk dijalankan trdan / cutatau semacamnya. ...

(Tentu saja, Anda ingin berhati-hati terhadap masukan yang buruk dengan kondisional dan alternatif yang masuk akal.)

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