Cách chỉ giữ mỗi dòng thứ n của tệp


71

Tôi đã có một tệp CSV khá lớn (75MB). Tôi chỉ đang cố gắng tạo ra một biểu đồ của nó, vì vậy tôi thực sự không cần tất cả dữ liệu.

Viết lại: Tôi muốn xóa n dòng, sau đó giữ một dòng, sau đó xóa n dòng, v.v.

Vì vậy, nếu tập tin trông như thế này:

Line 1
Line 2
Line 3
Line 4
Line 5
Line 6

và n = 2, thì đầu ra sẽ là:

Line 3
Line 6

Có vẻ như sedcó thể làm điều này, nhưng tôi không thể tìm ra cách. Một lệnh bash sẽ là lý tưởng, nhưng tôi mở cho bất kỳ giải pháp nào.


2
Bạn có thực sự muốn các dòng 1, 3, 6, v.v., chứ không phải 1, 4, 7, v.v.?
Ilmari Karonen

2
Vì nó là tệp CSV, tôi giả sử dòng đầu tiên chứa dữ liệu meta (tức là tên trường.). Nếu vậy, câu hỏi nên là "mỗi dòng thứ n sau dòng đầu tiên".
iglvzx

7
1, 3, 6 vẫn không có ý nghĩa!
wim

1
Tôi đoán nó phải là 1, 3, 5 trừ khi n = 2 là giá trị ma thuật cho các số tam giác (1, 3, 6, 10, 15, 21, v.v.)
rjmunro

4
Bạn có thể cập nhật câu hỏi của mình để làm cho những gì bạn yêu cầu ("mỗi dòng thứ n", "n = 2") và đầu ra mong muốn của bạn (Dòng 3, Dòng 6) phù hợp không? Độc giả tương lai sẽ bị nhầm lẫn.
Keith Thompson

Câu trả lời:


121
~ $ awk 'NR == 1 || NR % 3 == 0' yourfile
Line 1
Line 3
Line 6

NR(số lượng bản ghi) biến là số bản ghi số dòng vì hành vi mặc định là dòng mới cho RS(bản ghi tách biệt). mẫu và hành động là tùy chọn ở định dạng mặc định của awk 'pattern {actions}'. khi chúng tôi chỉ cung cấp một phần mẫu sau đó awkviết tất cả các trường $0cho các trueđiều kiện của mẫu .


8
Nhờ mặc định, bạn thậm chí không cần nhiều như vậy:awk 'NR == 1 || NR % 3 == 0'
Kevin

@selman: Nếu bạn thích giải pháp của Kevin, bạn có thể muốn xem xét cập nhật câu trả lời của mình.
Keith Thompson

4
Quan tâm để giải thích tại sao nó làm như vậy? Theo cách đó, nếu ai đó muốn điều chỉnh nó một chút, thì hy vọng lời giải thích của bạn sẽ giúp họ làm điều đó
Ivo Flipse

Tôi thấy rằng cách tiếp cận này để lại cho tôi các dòng 1 và 2. Điều này được xác nhận awk 'NR == 1 || NR % 2 == 0' myfile.txt | wc -ldẫn đến một số lẻ trong khi tệp gốc có số dòng chẵn. @kev trả lời hoạt động tốt nhất trong trường hợp thử nghiệm của tôi.
Daniel Da Cunha

58

sed cũng có thể làm điều này:

$ sed -n '1p;0~3p' input.txt
Line 1
Line 3
Line 6

man sedgiải thích ~như sau:

đầu tiên ~ bước Ghép từng dòng của bước bắt đầu với dòng đầu tiên. Ví dụ, `` sed -n 1 ~ 2p '' sẽ in tất cả các dòng được đánh số lẻ trong luồng đầu vào và địa chỉ 2 ~ 5 sẽ khớp với mỗi dòng thứ năm, bắt đầu bằng dòng thứ hai. đầu tiên có thể bằng không; trong trường hợp này, sed hoạt động như thể nó bằng bước. (Đây là một phần mở rộng.)


6
Bạn có thể giải thích lệnh này?
qed

1
@qed Giải thích: 1pin dòng đầu tiên, 0~3pin mọi dòng thứ ba bắt đầu từ dòng 3 ( 1pdo đó bắt buộc phải in dòng 1). Nhưng lưu ý rằng 0~3nó không phải là tiêu chuẩn mà là một phần mở rộng GNU sed.
Arkku

"Đây là một phần mở rộng." Phiên bản nào / bạn đang sử dụng?
Victor

Câu trả lời này đã giúp tôi rất nhiều cho windows PowerShell. Tôi đã mở rộng nó như thế: sed -n '1p;0~10p' '.\in.txt' > out.txtđể in tập tin rút gọn thành tập tin đầu ra.
kimliv

22

Perl cũng có thể làm điều này:

while (<>) {
    print  if $. % 3 == 1;
}

Chương trình này sẽ in dòng đầu tiên của đầu vào và mỗi dòng thứ ba sau đó.

Để giải thích một chút, <>là toán tử đầu vào dòng, lặp lại trên các dòng đầu vào khi được sử dụng trong một whilevòng lặp như thế này. Biến đặc biệt $.chứa số dòng được đọc cho đến nay và %là toán tử mô đun.

Mã này có thể được viết thậm chí gọn hơn dưới dạng một lớp, sử dụng -nvà các -ecông tắc:

perl -ne 'print if $. % 3 == 1'  < input.txt  > output.txt

Khóa -echuyển lấy một đoạn mã Perl để thực thi như một tham số dòng lệnh, trong khi -nkhóa chuyển hoàn toàn bọc mã trong một whilevòng lặp giống như mã được hiển thị ở trên.


Chỉnh sửa: Để thực sự có được các dòng 1, 3, 6, 9, ... như trong ví dụ, thay vì các dòng 1, 4, 7, 10, ... như lần đầu tiên tôi giả sử bạn muốn, hãy thay thế $. % 3 == 1bằng $. == 1 or $. % 3 == 0.


7

Nếu bạn muốn làm điều đó với một tập lệnh Bash, bạn có thể thử:

#!/bin/sh

echo Please enter the file name
read fname
echo Please enter the Nth lines that you want to keep
read n

exec<$fname
value=0
while read line
do
    if [ $(( $value % $n )) -eq 0 ] ; then
        echo -e "$line" >> new_file.txt
    fi
        let value=value+1 
done
echo "Check the 'new_file.txt' that has been created in this directory";

Lưu nó dưới dạng "read_lines.sh" và nhớ cung cấp quyền + x cho tệp bash.

chmod +x ./read_lines.sh

1
Nếu bạn thực hiện điều này chỉ phát ra trên tiêu chuẩn, hãy đọc không có dòng nào để bỏ qua các đối số và đọc tệp từ tiêu chuẩn trong, nó sẽ đơn giản và hữu ích hơn. Bạn vẫn có thể tạo new_file.txt bằng cách thực hiện ./read_lines.sh > new_file.txt.
rjmunro

4

Một giải pháp trong bash thuần túy, không sinh ra một quy trình là:

{ for f in {1..2}; do read line; done;
  while read line; do
    echo $line;
    for f in {1..2}; do read line; done;
  done; } < file

Dòng đầu tiên bỏ qua 2 dòng ở đầu tệp và whilein dòng tiếp theo và bỏ qua 2 dòng một lần nữa.

Nếu tệp của bạn nhỏ, đây là một cách rất hiệu quả để thực hiện công việc vì nó không bắt đầu một quy trình. Khi tệp của bạn lớn, sednên được sử dụng vì nó hiệu quả hơn trong việc xử lý io hơn bash.


1

Phiên bản Python (cả Python 2 và Python 3):

python2 -c "print(''.join(open('file.txt').readlines()[::3]))"

thay thế [::3]bằng các tham số kích thước bắt đầu, kết thúc và bước để kiểm soát nhiều hơn. Ví dụ: [10:36:5]đưa ra các dòng 10,15, ..., 35.

Lưu ý, vì readlines()giữ kết thúc dòng, đầu ra của cuộc gọi này có thể kết thúc bằng một dòng cuối cùng trống, trừ khi dòng cuối cùng ban đầu được đưa ra theo kích thước bước đã chọn.

Phiên bản luồng cũng có thể (chỉ xuất ở đây sau khi kết thúc luồng):

python -c "import sys;print(''.join(list(sys.stdin)[::3]))" < file.txt
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.