Lệnh lấy dòng thứ n của STDOUT


210

Có lệnh bash nào cho phép bạn nhận được dòng thứ n của STDOUT không?

Đó là để nói, một cái gì đó sẽ mất này

$ ls -l
-rw-r--r--@ 1 root  wheel my.txt
-rw-r--r--@ 1 root  wheel files.txt
-rw-r--r--@ 1 root  wheel here.txt

và làm một cái gì đó như

$ ls -l | magic-command 2
-rw-r--r--@ 1 root  wheel files.txt

Tôi nhận ra rằng đây sẽ là một thực tiễn tồi khi viết các kịch bản có nghĩa là được sử dụng lại, NHƯNG khi làm việc với trình bao ngày này sẽ rất hữu ích để tôi có thể lọc STDOUT của mình theo cách như vậy.

Tôi cũng nhận ra đây sẽ là lệnh bán tầm thường để viết (bộ đệm STDOUT, trả về một dòng cụ thể), nhưng tôi muốn biết liệu có một số lệnh shell tiêu chuẩn để thực hiện điều này sẽ có sẵn mà không cần tôi thả tập lệnh vào vị trí không.


5
Tôi vừa bỏ phiếu này cho việc sử dụng từmagic-command
Sevenearths

Câu trả lời:


332

Sử dụng sed, chỉ để đa dạng:

ls -l | sed -n 2p

Sử dụng giải pháp thay thế này, có vẻ hiệu quả hơn vì nó dừng đọc đầu vào khi dòng yêu cầu được in, có thể tạo SIGPIPE trong quy trình cấp dữ liệu, từ đó có thể tạo ra thông báo lỗi không mong muốn:

ls -l | sed -n -e '2{p;q}'

Tôi đã thấy rằng tôi thường sử dụng cái đầu tiên (dù sao cũng dễ gõ hơn), mặc dù đó lskhông phải là một lệnh phàn nàn khi nó bị SIGPIPE.

Đối với một loạt các dòng:

ls -l | sed -n 2,4p

Đối với một số phạm vi dòng:

ls -l | sed -n -e 2,4p -e 20,30p
ls -l | sed -n -e '2,4p;20,30p'

P có nghĩa là gì? thích 2p 3p? Theo nghĩa tôi hiểu rằng đó là 2/3 dòng, nhưng lý thuyết / sự hiểu biết đằng sau nó là gì?
Leo Ufimtsev

4
@LeoUfimtsev: các psedtuyên bố rằng sẽ in các dòng xác định bởi một loạt các dòng đứng trước. Do đó, 2,4pcó nghĩa là in các dòng 2, 3, 4.
Jonathan Leffler

3
ncó nghĩa là KHÔNG in tất cả các dòng khác
Timo

85
ls -l | head -2 | tail -1

13
+2, nhưng tôi đề nghị head -n 2 | tail -n 1- đầu và đuôi hiện đại có xu hướng đưa ra cảnh báo khác.
Michael Krelin - tin tặc

2
Sử dụng hai quy trình để lọc dữ liệu là một chút quá mức (nhưng, với sức mạnh của máy móc ngày nay, có lẽ họ sẽ đối phó). Tổng quát hóa để xử lý một phạm vi không hoàn toàn tầm thường (đối với phạm vi N..M, bạn sử dụng head -n M | tail -n M-N+1) và việc khái quát hóa để xử lý nhiều phạm vi là không thể.
Jonathan Leffler

3
đầu ống thành đuôi? kinh khủng. Nhiều cách tốt hơn để làm điều đó.
camh

16
Điều tuyệt vời về * nix dòng lệnh: một triệu đồng đến một cách để làm tất cả mọi thứ và tất cả mọi người đã yêu thích và ghét tất cả các cách khác ... :-)
beggs

1
Cách in một phạm vi dòng theo cách khác: $ ls -l | awk '{if (NR>=4 && NR<=64) print}'(có thể thêm nhiều điều kiện dễ dàng hơn, nhiều phạm vi, v.v ...)
user10607

41

Thay thế cho cách đầu / đuôi đẹp:

ls -al | awk 'NR==2'

hoặc là

ls -al | sed -n '2p'

1
awk của tôi là tốt hơn, nhưng sed là tốt đẹp.
Michael Krelin - hacker

Không cần "nếu" - xem câu trả lời của hacker (ngoại trừ phần tìm kiếm mọi tệp trên hệ thống). ;-)
Tạm dừng cho đến khi có thông báo mới.

viết '2p'tắt của từ gì
băm nhỏ

1
@shrpping câu trả lời ngắn in dòng thứ hai; dài -> Khi được sử dụng, chỉ các dòng khớp với mẫu được cung cấp lệnh sau địa chỉ. Tóm lại, khi được sử dụng với cờ / p, các dòng trùng khớp được in hai lần: tệp sed '/ PATTERN / p'. Và tất nhiên, MẪU là bất kỳ biểu hiện thông thường nào nữa
Medhat

Nếu 2là một biến thì sao? Mỗi ví dụ sử dụng dấu ngoặc đơn, nhưng với một var bạn cần dấu ngoặc kép. Chúng ta có thể "lập trình" awk để làm việc với các dấu ngoặc đơn khi sử dụng vars không? Ví dụ line =1; ls | awk 'NR==$line'. Tôi biết rằng bashsử dụng dấu ngoặc kép với vars.
Timo

21

Từ sed1line :

# print line number 52
sed -n '52p'                 # method 1
sed '52!d'                   # method 2
sed '52q;d'                  # method 3, efficient on large files

Từ awk1line :

# print line number 52
awk 'NR==52'
awk 'NR==52 {print;exit}'          # more efficient on large files

9

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

mã ngắn hơn

find / | awk NR==3

cuộc sống ngắn hơn

find / | awk 'NR==3 {print $0; exit}'

Bạn không đùa về sự hoàn thiện!
Tạm dừng cho đến khi có thông báo mới.

Tôi không phải ;-) Thật ra, tôi không biết tại sao mọi người lại sử dụng ls- câu hỏi ban đầu là về ai đó STDOUT, vì vậy tôi nghĩ tốt hơn là nên làm cho nó lớn hơn.
Michael Krelin - hacker

Ít nhất là với GNU awk, hành động mặc định là { print $0 }, vì vậy `awk 'NR == 3' là một cách ngắn hơn để viết tương tự.
ephemient

Bạn có lẽ nên thêm "; exit" vào hành động đó. Không có điểm nào xử lý phần còn lại của dòng khi bạn sẽ không làm gì với chúng.
camh

@camh: Xem câu trả lời của Jonathan Leffer về điều đó: "có thể tạo SIGPIPE trong quy trình cung cấp, từ đó có thể tạo ra thông báo lỗi không mong muốn".
ephemient


6

Bạn có thể sử dụng awk:

ls -l | awk 'NR==2'

Cập nhật

Đoạn mã trên sẽ không có được những gì chúng ta muốn vì lỗi ngoài luồng : dòng đầu tiên của lệnh ls -l là dòng tổng . Do đó, mã sửa đổi sau đây sẽ hoạt động:

ls -l | awk 'NR==3'

4

Perl có dễ dàng có sẵn cho bạn?

$ perl -n -e 'if ($. == 7) { print; exit(0); }'

Rõ ràng thay thế bất cứ số nào bạn muốn cho 7.


Đây là fav của tôi vì nó có vẻ dễ khái quát nhất với những gì cá nhân tôi sau đó là mỗi thứ n, perl -n -e 'if ($.% 7 == 0) {print; thoát (0); } '
mat kelcey

@matkelcey bạn cũng có thể làm $ awk 'NR % 7' filenameđể in mỗi dòng thứ 7 trong tệp.
dùng10607

3

Một poster khác đề nghị

ls -l | head -2 | tail -1

nhưng nếu bạn nối đầu vào đuôi, có vẻ như mọi thứ thuộc dòng N được xử lý hai lần.

Đuôi ống vào đầu

ls -l | tail -n +2 | head -n1

Sẽ hiệu quả hơn?


0

Vâng, cách hiệu quả nhất (như Jonathan Leffler đã chỉ ra) là sử dụng sed với in & thoát:

set -o pipefail                        # cf. help set
time -p ls -l | sed -n -e '2{p;q;}'    # only print the second line & quit (on Mac OS X)
echo "$?: ${PIPESTATUS[*]}"            # cf. man bash | less -p 'PIPESTATUS'

Việc sử dụng pipefail và PIPESTATUS rất thú vị và điều này bổ sung rất nhiều cho trang này.
Mike S

0

Ừm

sed đã không làm việc trong trường hợp của tôi. Tôi đề nghị:

cho các dòng "lẻ" 1,3,5,7 ... ls | awk '0 == (NR + 1)% 2'

cho các dòng "chẵn" 2,4,6,8 ls | awk '0 == (NR)% 2'


-1

Để hoàn thiện hơn ..

ls -l | (for ((x=0;x<2;x++)) ; do read ; done ; head -n1)

Vứt bỏ các dòng cho đến khi bạn nhận được thứ hai, sau đó in ra dòng đầu tiên sau đó. Vì vậy, nó in dòng thứ 3.

Nếu đó chỉ là dòng thứ hai ..

ls -l | (read; head -n1)

Đặt càng nhiều 'đọc càng cần thiết.


-1

Tôi đã thêm cái này vào .bash_profile

function gdf(){
  DELETE_FILE_PATH=$(git log --diff-filter=D --summary | grep delete | grep $1 | awk '{print $4}')
  SHA=$(git log --all -- $DELETE_FILE_PATH | grep commit | head -n 1 | awk '{print $2}')
  git show $SHA -- $DELETE_FILE_PATH
}

Và nó hoạt động

gdf <file name>

Tôi hoang mang - điều này có liên quan gì đến câu hỏi?
Jonathan Leffler
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.