Thay đổi thứ tự các dòng trong một tệp


11

Tôi đang cố gắng thay đổi thứ tự các dòng trong một mẫu cụ thể. Làm việc với một tệp có nhiều dòng (ví dụ 99 dòng). Đối với mỗi ba dòng, tôi muốn dòng thứ hai là dòng thứ ba và dòng thứ ba là dòng thứ hai.

THÍ DỤ.

1- Đầu vào:

gi_1234
My cat is blue.
I have a cat.
gi_5678
My dog is orange.
I also have a dog.
...

2- Đầu ra:

gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.
...

Câu trả lời:


12

Sử dụng awkvà toán số nguyên:

awk 'NR%3 == 1 { print } NR%3 == 2 { delay=$0 } NR%3 == 0 { print; print delay; delay=""} END { if(length(delay) != 0 ) { print delay } }' /path/to/input

Toán tử mô đun thực hiện phép chia số nguyên và trả về phần còn lại, vì vậy với mỗi dòng, nó sẽ trả về chuỗi 1, 2, 0, 1, 2, 0 [...]. Biết rằng, chúng ta chỉ lưu đầu vào trên các dòng trong đó mô đun là 2 cho sau này - để dí dỏm, ngay sau khi in đầu vào khi nó bằng không.


Chúng tôi có một lỗ hổng nhỏ ở đây. Xem câu trả lời của tôi, phần cải tiến nhỏ
Sergiy Kolodyazhnyy

Cảm ơn vì đã bắt tốt; Tôi đã kết hợp một sửa chữa vào câu trả lời của tôi dưới dạng NR%3 == 0 { print; print delay; delay=""} END { if(length(delay) != 0 ) { print delay }.
DopeGhoti

23
$ seq 9 | sed -n 'p;n;h;n;G;p'
1
3
2
4
6
5
7
9
8

Đó là, pxé dòng hiện tại, lấy phần mở rộng n, hcũ, lấy phần mở rộng n, Gvà dòng được giữ (nối nó vào không gian mẫu) và ptô màu không gian mẫu 2 dòng với dòng thứ ba và thứ hai được hoán đổi.


3

Một cách tiếp cận awk khác :

awk '{print $0; if ((getline L2)>0 && (getline L3)>0){ print L3 ORS L2 }}' file

Đầu ra:

gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.

  • (getline L2)>0 && (getline L3)>0- trích xuất 2 bản ghi tiếp theo nếu chúng tồn tại

  • mỗi bản ghi thứ 2 và thứ 3 được gán cho L2L3các biến tương ứng


1
Tôi giả sử các biến đó bắt đầu bằng chữ L (viết thường). Chúng là những lựa chọn kém cho khả năng đọc vì chúng trông giống như các chữ số cho mười hai và mười ba. Một sự lựa chọn tốt hơn có thể line2, v.v.
Tạm dừng cho đến khi có thông báo mới.

@DennisWilliamson, đổi thành chữ hoa
RomanPerekhrest

1

Sử dụng perlvà một đoạn script ngắn:

user@pc:~$ cat input.txt 
gi_1234
My cat is blue.
I have a cat.
gi_5678
My dog is orange.
I also have a dog.

user@pc:~$ perl -ne '$l2=<>; $l3=<>; print $_,$l3,$l2;' input.txt 
gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.

Kịch bản xử lý toàn bộ tệp, đối với mỗi dòng (được lưu trữ $_), nó sẽ nhận hai dòng tiếp theo ( $l2$l3) và in chúng theo thứ tự được yêu cầu: line1, line3, line2.


1

Một cách có thể như sau:

sed -e '
   /\n/s/\(.*\)\(\n\)\(.*\)/\3\2\1/;//b
   $!N;$q;N;                            # load up the pattern space with 3 lines provided eof not reached
   P;D;                                 # first just print the first line then interchange the two and print them
' yourfile

Ngoài ra,

perl -ne 'print $_, reverse scalar <>, scalar <>' yourfile

Các kết quả

gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.

1

Tại sao không chỉ thực hiện một vòng lặp while? Ở dạng mở rộng:

( while read a
  do
    read b
    read c
    echo "$a"
    echo "$c"
    echo "$b"
  done
) < input.txt

Trong "định dạng dòng đơn":

( while read a ; do read b ; read c ; echo "$a" ; echo "$c" ; echo "$b" ; done) < input.txt

Đầu ra:

gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.

1

Perl

perl -ne 'print if $.%3==1;$var=$_ if $.%3==2;print $_ . $var if $.%3==0' input.txt

Ý tưởng ở đây là chúng ta sử dụng toán tử modulo %với $.biến số dòng , để tìm ra cái nào là đầu tiên, cái nào là mỗi giây và cái nào là dòng thứ 3. Với mỗi dòng thứ 3 còn lại là 0, trong khi với mỗi dòng thứ 1 và thứ 2, nó sẽ có các số tương ứng.

Kiểm tra:

$ cat input.txt                                                                                                          
gi_1234
My cat is blue.
I have a cat.
gi_5678
My dog is orange.
I also have a dog.

$ perl -ne 'print if $.%3==1;$var=$_ if $.%3==2;print $_ . $var if $.%3==0' input.txt                                    
gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.

Cải thiện nhỏ

Cách tiếp cận với việc lưu trữ dòng thứ hai vào một biến có một lỗ hổng. Điều gì sẽ xảy ra nếu dòng cuối cùng là dòng "thứ hai", tức là phần còn lại của số dòng đó là 2? Mã ban đầu trong câu trả lời của tôi và DopeGhoti sẽ không được in My dog is orangenếu chúng tôi rời khỏi dòng cuối cùng. Cách khắc phục trong cả hai trường hợp là sử dụng END{}khối mã, với việc bỏ đặt biến tạm thời sau khi in. Nói cách khác:

$ awk 'NR%3 == 1 { print } NR%3 == 2 { delay=$0 } NR%3 == 0 { print; print delay;delay=""}END{print delay}' input.txt

$ perl -ne '$s=$_ if $.%3==2;print $_ . $s and $s="" if $.%3==0 or $.%3==1;END{print $s}' input.txt 

Theo cách này, mã sẽ hoạt động với số lượng dòng tùy ý trong một tệp, không chỉ các dòng chia hết cho 3.

Sửa chữa bổ sung cho vấn đề được đề cập trong ý kiến

Trong trường hợp của awk, nếu dòng cuối cùng trong tệp tạo ra đầu ra là 1 cho $. % 3, mã trước có vấn đề xuất ra dòng mới trống vì in vô điều kiện END{print delay}, vì printchức năng được đề cập trong các nhận xét luôn nối thêm dòng mới vào bất kỳ biến nào mà nó đang hoạt động. Trong trường hợp perlphiên bản, sự cố này không xảy ra, vì chức năng -necờ printkhông nối thêm dòng mới.

Tuy nhiên, cách khắc phục trong trường hợp của awk là tạo điều kiện, như Dope Ghoti đã đề cập trong các bình luận là để xác minh độ dài của biến tạm thời. Phiên bản perl của cùng một bản sửa lỗi sẽ là:

$ perl -ne '$s=$_ if $.%3==2;print $_ . $s and $s="" if $.%3==0 or $.%3==1;END{print $s if length $s}' input.txt 

1
Bản sửa lỗi của bạn có một lỗ hổng nhỏ tiềm ẩn ở chỗ nó sẽ nối thêm một dòng đầu ra trống cho các tệp có số dòng 'sai'. Tôi đã sửa lỗi này trong việc kết hợp cải tiến của bạn trong câu trả lời của tôi với (cho awk) NR%3 == 0 { print; print delay; delay=""} END { if(length(delay) != 0 ) { print delay }.
DopeGhoti

1
@DopeGhoti Sự cố không xảy ra với perl, vì bản in của perl với -necờ không tạo ra dòng mới. Nó thực sự in, nhưng đó là một chuỗi null, không có dòng mới. Hàng tháng, tôi đã thêm đề cập đến vấn đề và cách khắc phục tương tự vào câu trả lời của mình. Cảm ơn !
Sergiy Kolodyazhnyy

1

Vim

Không phù hợp với các tệp dài, nhưng vẫn tiện dụng nếu bạn chỉ chỉnh sửa một tệp và muốn, ví dụ, để sắp xếp lại một số khổ thơ yaml.

Đầu tiên ghi một macro:

gg qq j ddp j q

Và sau đó lặp lại số lần mong muốn:

@q @q @q ...

Hoặc chỉ là ví dụ

3@q

Giải trình:

  • gg - đi đến dòng đầu tiên
  • qq - bắt đầu ghi một macro
  • j - đi đến dòng thứ hai
  • ddp - hoán đổi dòng thứ hai và thứ ba
  • j - đi đến dòng thứ tư, tức là đến dòng đầu tiên trong ba dòng tiếp theo
  • q - dừng ghi âm
  • @q - phát lại macro một lần
  • 3 @ q - phát lại macro ba lần

1
Thay vì lặp lại thủ công @q @q @q, có thể thực hiện bằng cách này 3@q- lặp lại ba lần. 100@q- lặp lại macro 100 lần.
MiniMax

0

Sử dụng: ./shuffle_lines.awk input.txt

Kiểm tra shebang #!/usr/bin/awk -f, vì awkvị trí có thể khác nhau trên hệ thống của bạn.

#!/usr/bin/awk -f

{
    if ((NR + 1) % 3 == 0) {
        buffer = $0;
    } else if (NR % 3 == 0) {
        print $0 ORS buffer;
        buffer = "";
    } else {
        print;
    }
}
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.