Di chuyển N dòng đầu ra đầu tiên đến hết mà không sử dụng tệp tạm thời


11

Hãy tưởng tượng một đầu ra của một lệnh như

44444
55555
11111
22222
33333

Làm thế nào tôi có thể kéo N dòng đầu tiên (hai dòng đầu tiên trong ví dụ ở trên) và nối chúng ở cuối, nhưng không sử dụng tệp tạm thời (vì vậy chỉ sử dụng đường ống)?

11111
22222
33333
44444
55555

Một cái gì đó dọc theo dòng | sed -n '3,5p;1,2p'(rõ ràng là không hoạt động vì sed không quan tâm đến thứ tự của các lệnh).


2
Tại sao chúng ta không thể sử dụng tệp tạm thời?
Braiam

Câu trả lời:


13

Chỉ cần sao chép các dòng đó vào bộ đệm giữ (sau đó xóa chúng) và khi trên dòng cuối cùng nối thêm nội dung của bộ đệm giữ vào không gian mẫu:

some command | sed '1,NUMBER{           # in this range
H                                       # append line to hold space and
1h                                      # overwrite if it's the 1st line
d                                       # then delete the line
}
$G'                                     # on last line append hold buffer content

Với gnu sedbạn có thể viết nó như là

some command | sed '1,NUMBER{H;1h;d;};$G'

Đây là một cách khác với ol ' ed(nó rdẫn đầu ra của some commandbộ đệm văn bản và sau đó đặt mcác dòng 1,NUMBERsau la $t one):

ed -s <<IN
r ! some command
1,NUMBERm$
,p
q
IN

Lưu ý rằng - như đã chỉ ra - cả hai sẽ thất bại nếu đầu ra có ít hơn NUMBER+1 dòng. Một cách tiếp cận vững chắc hơn sẽ là ( gnu sedcú pháp):

some command | sed '1,NUMBER{H;1h;$!d;${g;q;};};$G'

cái này chỉ xóa các dòng trong phạm vi đó miễn là chúng không phải là dòng cuối cùng ( $!d) - nếu không nó sẽ ghi đè lên không gian mẫu bằng nội dung bộ đệm giữ ( g) và sau đó là quits (sau khi in không gian mẫu hiện tại).


2
sed -e '1,NUMBER{H;1h;d;}' -e '$G'cũng hoạt động tốt (lưu ý rằng một số sedtriển khai không thể chứa nhiều hơn vài kilobyte trong không gian giữ, vì vậy SỐ không thể quá lớn ở đó)
Stéphane Chazelas

Tài khoản đi trước một <dòng mới> " vì vậy theo họ không nên như sed -e '1,NUMBER{H;1h;d' -e'}' -e '$G'vậy?
don_crissti

4
Thông thường, một cái mới -ethay thế một dòng mới. d;}chưa phải là POSIX mà là di động. Điều đó sẽ được sửa trong thông số POSIX tiếp theo. Xem austingroupbugs.net/view.php?id=944#c2720
Stéphane Chazelas

2
@don_crissti cảm ơn bạn! Sẽ thật tuyệt nếu bạn cũng có thể bao gồm một lời giải thích ngắn về lý do tại sao nó hoạt động. (Tất nhiên tôi sẽ đi tìm nó, nhưng nó sẽ làm cho câu trả lời có giá trị hơn.)
Peter Uhnak

Trong đầu tôi, điều 1,NUMBER{H;1h;d;}không thể nói về việc không có dấu chấm phẩy ngay sau khi mở nẹp. Tuy nhiên, đó có thể là một lỗi trong SunOS 4.1 sedmà cách khắc phục vẫn còn dây vào ngón tay tôi 20 năm sau.
zwol

11

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

cmd | awk -v n=3 '
  NR <= n {head = head $0 "\n"; next}
  {print}
  END {printf "%s", head}'

Một lợi ích so với cách tiếp cận của @ don_crissti'ssed là nó vẫn hoạt động (đầu ra các dòng) nếu đầu ra có ncác dòng hoặc ít hơn.


Bạn có thể muốn thay thế mã hóa cứng \nbằng ORS, để nó hoạt động với các trình phân tách bản ghi khác (giả sử bạn muốn sử dụng các đoạn, v.v.).
fedorqui

6

Tôi có xclipvà với nó điều này có thể được thực hiện theo cách này:

./a_command | xclip -in && xclip -o | tail -n +3 && xclip -o | head -n 2

Dưới đây là mô tả của nó:

xclip - command line interface to X selections (clipboard)

NAME
       xclip - command line interface to X selections (clipboard)

SYNOPSIS
       xclip [OPTION] [FILE]...

DESCRIPTION
       Reads from standard in, or from one or more files, and makes the data available as an X selection for pasting into X applications. Prints current X selection to standard out.

       -i, -in
              read text into X selection from standard input or files (default)

       -o, -out
              prints the selection to standard out (generally for piping to a file or program)

3
+1 cho việc sử dụng xclip sáng tạo (mis-). Câu trả lời cần một máy chủ X có thể truy cập / đang chạy.
jofel

3

Một cách Perl:

perl -ne '$.<3?($f.=$_):print;}{print $f'

Hoặc, điều tương tự được viết ít mật mã hơn:

perl -ne 'if($.<3){ $f.=$_ } else{ print } END{print $f}'

Ví dụ:

$ cat file
44444
55555
11111
22222
33333

$ cat file | perl -ne '$.<3?($f.=$_):print;}{print $f'
11111
22222
33333
44444
55555

Giải trình

  • -ne: đọc tệp đầu vào / dòng theo dòng và áp dụng tập lệnh được cung cấp -echo từng dòng.
  • $.<3: $.là số dòng hiện tại, vì vậy hãy thay đổi 3số dòng bạn muốn thay đổi.
  • $.<3?($f.=$_):print;: đây là toán tử có điều kiện, định dạng chung là condition ? case1 : case2, nó sẽ chạy case1nếu conditionđúng và case2nếu sai. Ở đây, nếu số dòng hiện tại nhỏ hơn 3, nó sẽ nối dòng hiện tại ( $_) vào biến $fvà nếu số dòng lớn hơn 3, nó sẽ in.
  • }{ print $f: tốc }{ký perl cho END{}. Nó sẽ chạy sau khi tất cả các dòng đầu vào đã được xử lý. Tại thời điểm này, chúng tôi sẽ thu thập tất cả các dòng chúng tôi muốn thay đổi và sẽ in tất cả các dòng chúng tôi muốn để lại một mình, vì vậy hãy in các dòng được lưu dưới dạng $f.

1
liên quan đến phiên bản chơi gôn của bạn, một vài ký tự có thể được xóaperl -ne '$.<3?$f.=$_:print}{print $f
123

1

Sử dụng POSIX ex. Vâng, nó nhằm mục đích chỉnh sửa tập tin, nhưng nó sẽ hoạt động trong một đường ống dẫn.

printf %s\\n 111 222 333 444 555 | ex -sc '1,2m$|%p|q!' /dev/stdin

Điều này có thể có bất kỳ lệnh tùy ý được thêm vào đầu hoặc cuối của đường ống và sẽ hoạt động như nhau. Tốt hơn nữa, với sự hiện diện của /dev/stdin, nó tuân thủ POSIX.

(Tôi không biết có /dev/stdinđược chỉ định trong POSIX hay không, nhưng tôi thấy nó hiện diện cả trong Linux và Mac OS X.)

Điều này có một lợi thế dễ đọc hơn so với việc sử dụng sedkhông gian giữ của bạn, bạn chỉ cần nói ex"di chuyển những dòng này đến cuối" và nó làm như vậy. (Phần còn lại của các lệnh có nghĩa là "in bộ đệm" và "thoát", cũng khá dễ đọc.)

NB: exLệnh được đưa ra ở trên sẽ thất bại nếu được cung cấp ít hơn 2 dòng làm đầu vào.

Đọc thêm:


0

Một đoạn ngắn python:

#!/usr/bin/env python3
import sys
file_ = sys.argv[1]
lines = int(sys.argv[2])
with open(file_) as f:
    f = f.readlines()
    out = f[lines:] + f[:lines]
    print(''.join(out), end='')

Truyền tên tệp làm đối số thứ nhất và số dòng để di chuyển làm đối số thứ hai.

Thí dụ:

$ cat input.txt
44444
55555
11111
22222
33333

$ ./sw_lines.py input.txt 2
11111
22222
33333
44444
55555

$ ./sw_lines.py input.txt 4
33333
44444
55555
11111
22222

0

Nếu bạn có thể lưu trữ toàn bộ đầu ra trong bộ nhớ:

data=$(some command)
n=42                  # or whatever
{ tail -n +$((n+1)) <<<"$data"; head -n $n <<<"$data"; } > outputfile

0

Đây là một tùy chọn khác yêu cầu GNU sed:

(x=$(gsed -u 3q);cat;printf %s\\n "$x")

-ulàm cho GNU không có bộ sedđệm để sedlệnh không tiêu thụ quá 3 dòng STDIN. Việc thay thế lệnh sẽ loại bỏ các dòng trống, để lệnh không bao gồm các dòng trống ở cuối đầu ra nếu các dòng thứ ba, thứ ba và thứ hai hoặc thứ ba, thứ hai và thứ nhất trống.

Bạn cũng có thể làm một cái gì đó như thế này:

tee >(sponge /dev/stdout|sed -u 3q)|sed 1,3d

Nếu không có sponge /dev/stdout|lệnh sẽ thất bại với đầu vào dài. sponge /dev/stdoutcũng có thể được thay thế bằng tac|tac, mặc dù bản in đó chẳng hạn a\ncbnếu đầu vào là a\nb\ncnơi cung cấp \ndòng, hoặc với (x=$(cat);printf %s\\n "$x"), mặc dù điều đó sẽ loại bỏ các dòng trống từ cuối đầu vào. Lệnh trên xóa dòng đầu tiên khi số dòng của đầu vào là một hoặc hai.

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.