Lấy cột thứ n trong một tệp văn bản


86

Tôi có một tệp văn bản:

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

Tôi muốn lấy từ thứ 2 và thứ 4 của mỗi dòng như thế này:

1657 19.6117
1410 18.8302
3078 18.6695
2434 14.0508
3129 13.5495

Tôi đang sử dụng mã này:

 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

Nó hoạt động, nhưng rất phức tạp và mất nhiều thời gian để xử lý các tệp văn bản dài.

Có cách nào đơn giản hơn để làm điều này không?


1
Từ thứ 2 của mỗi hàng được gọi là cột thứ 2 đơn giản!
Bernard

Câu trả lời:


127

iirc:

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

hoặc, như đã đề cập trong các nhận xét:

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

16
UUOC !!! awk '{print $2,$4}' filename.txttốt hơn (không có đường ống, chỉ một chương trình được gọi)
màu xanh lam

5
@blue Tôi thường sử dụng cattrong các tập lệnh bash của mình thay vì chỉ định tên tệp, vì chi phí là tối thiểu và bởi vì cú pháp cat ... | ... > ...hiển thị thực sự độc đáo đầu vào là gì và đầu ra đi đâu. Tuy nhiên, bạn nói đúng, nó không thực sự cần thiết ở đây.
Tom van der Woerdt

8
@TomvanderWoerdt: Đôi khi tôi viết < input awk '{ print $2 $4 }' > outputcho mục đích đó.
ruakh

69

Bạn có thể sử dụng cutlệnh:

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

bản in

1657 19.6117
1410 18.8302
3078 18.6695
2434 14.0508
3129 13.5495

các

  • -d' '- nghĩa là, sử dụng spacelàm dấu phân cách
  • -f3,5 - lấy và in cột thứ 3 và 5

Các cutnhanh hơn nhiều cho các tập tin lớn như một giải pháp vỏ tinh khiết. Nếu tệp của bạn được phân tách bằng nhiều khoảng trắng, trước tiên bạn có thể xóa chúng, như:

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

trong đó sed (gnu) sẽ thay thế bất kỳ tabhoặcspace ký tự nào bằng mộtspace .

Đối với một biến thể - đây cũng là một giải pháp perl:

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

1
Hoạt động tốt ... nếu bạn đang đảm bảo rằng số lượng chỗ trên mỗi dòng, chính xác ... :)
rogerdpack

24

Vì lợi ích của sự hoàn chỉnh:

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

Thay vì _một biến tùy ý (chẳng hạn nhưjunk ) cũng có thể được sử dụng. Vấn đề là chỉ trích xuất các cột.

Bản giới thiệu:

$ 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

Đẹp, dễ đọc và không cần perls / awks / others, tất cả trong một trình bao bởi nội trang.
Petr Matousu

6

Một biến thể đơn giản hơn -

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

4

Nếu tệp của bạn chứa n dòng, thì tập lệnh của bạn phải đọc tệp n lần; vì vậy nếu bạn tăng gấp đôi độ dài của tệp, bạn sẽ tăng gấp bốn lần khối lượng công việc mà script của bạn thực hiện - và hầu như tất cả công việc đó chỉ đơn giản là vứt đi, vì tất cả những gì bạn muốn làm là lặp lại các dòng theo thứ tự.

Thay vào đó, cách tốt nhất để lặp qua các dòng của tệp là sử dụng một whilevòng lặp, với lệnh điều kiện là readnội trang:

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

Trong trường hợp của bạn, vì bạn muốn chia dòng thành một mảng và readnội trang thực sự có hỗ trợ đặc biệt để điền một biến mảng, đó là những gì bạn muốn, bạn có thể viết:

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

hoặc tốt hơn:

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

Tuy nhiên, đối với những gì bạn đang làm, bạn chỉ có thể sử dụng cuttiện ích:

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

(hoặc awk, như Tom van der Woerdt gợi ý, hoặc perl, hoặc thậm chí sed).


muốn readhơn cutvì nó mạnh mẽ chống lại nhiều khoảng trống giữa các lĩnh vực và bạn không cần mảng ma thuật:while read word1 word2 word3 word4 rest; do doSomethingWith $word2 $word4; done
user829755

3

Nếu bạn đang sử dụng dữ liệu có cấu trúc, điều này có lợi ích bổ sung là không gọi một quy trình shell bổ sung để chạy trvà / cuthoặc một cái gì đó. ...

(Tất nhiên, bạn sẽ muốn đề phòng các yếu tố đầu vào xấu bằng các điều kiện và lựa chọn thay thế lành mạnh.)

...
while read line ; 
do 
    lineCols=( $line ) ;
    echo "${lineCols[0]}"
    echo "${lineCols[1]}"
done < $myFQFileToRead ; 
...
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.