Nhận dòng cụ thể từ tệp văn bản chỉ bằng cách sử dụng tập lệnh shell


100

Tôi đang cố lấy một dòng cụ thể từ tệp văn bản.

Cho đến nay, trên mạng tôi chỉ thấy những thứ như sed, (tôi chỉ có thể sử dụng sh -not bash hoặc sed hoặc bất cứ thứ gì tương tự). Tôi chỉ cần làm điều này bằng cách sử dụng một tập lệnh shell cơ bản.

cat file | while read line
    do
       #do something
    done

Tôi biết cách lặp qua các dòng, như được hiển thị ở trên, nhưng điều gì sẽ xảy ra nếu tôi chỉ cần lấy nội dung của một dòng cụ thể


bạn có biết số dòng không?
Mehul Rathod

1
Sau đó, bạn phải đếm.
Ignacio Vazquez-Abrams

vâng, số dòng là 5 @MehulRathod
GangstaGraham

3
Tại sao được catnhưng sedkhông được? Điều đó không có ý nghĩa.
William Pursell

5
Bởi vì không ai có thể nói không với cat. Aw ... dễ thương cat!

Câu trả lời:


204

sed:

sed '5!d' file

awk:

awk 'NR==5' file

Còn với lệnh sh thì sao, mình không dùng được sed, awk. Tôi nên làm rõ hơn điều này trong câu hỏi.
GangstaGraham

@GangstaGraham bạn đã nói rằng bạn biết cách lặp qua các dòng, còn việc thêm bộ đếm thì sao? nếu bộ đếm đạt đến số dòng mục tiêu của bạn, hãy lấy dòng và ngắt vòng lặp. nó có ích gì không
Kent

4
@KanagaveluSugumar đã đọc trang thông tin của sed. 5!dnghĩa là xóa tất cả các dòng ngoại trừ 5. shell var là có thể, bạn cần dấu ngoặc kép.
Kent

13
Tôi sẽ đề xuất thêm một biến thể khác: sed -n 5pĐiều này có vẻ hợp lý hơn để ghi nhớ đối với người mới, vì -ncó nghĩa là "không có đầu ra theo mặc định" và plà viết tắt của "in" và không có đề cập đến việc xóa (khi mọi người nói về tệp, xóa dòng có xu hướng nghĩa là khác).
Josip Rodin

1
@JosipRodin bạn nói đúng, cũng -n '5p'hoạt động cho vấn đề này. Sự khác biệt ở đây là, 5!dbạn có thể thêm -iđể ghi thay đổi trở lại tệp. tuy nhiên, -n 5pbạn phải nói sed -n '5p' f > f2&& mv f2 flại, đối với câu hỏi này, tôi đồng ý với ý kiến ​​của bạn.
Kent

21

Giả sử linelà một biến chứa số dòng yêu cầu của bạn, nếu bạn có thể sử dụng headtail , thì nó khá đơn giản:

head -n $line file | tail -1

Nếu không, điều này sẽ hoạt động:

x=0
want=5
cat lines | while read line; do
  x=$(( x+1 ))
  if [ $x -eq "$want" ]; then
    echo $line
    break
  fi
done

Đây -eqso sánh là cho số nguyên, vì vậy nó muốn một số dòng, không phải nội dung dòng ( $line). Điều này phải được khắc phục bằng cách xác định ví dụ: want=5trước vòng lặp, và sau đó sử dụng phép -eqso sánh trên $want. [đã chuyển từ bản chỉnh sửa bị từ chối]
Josip Rodin,

1
@JosipRodin Tôi đã đưa ra một đề xuất chỉnh sửa độc lập dựa trên nhận xét của bạn, vì tôi đồng ý với nó. Hy vọng rằng lần này nó sẽ không bị từ chối.
Victor Zamanian

15

Bạn có thể sử dụng sed -n 5p file.

Bạn cũng có thể nhận được một phạm vi, ví dụ sed -n 5,10p file,.


11

Phương pháp hiệu suất tốt nhất

sed '5q;d' file

seddừng đọc bất kỳ dòng nào sau dòng thứ 5

Cập nhật thử nghiệm từ ông Roger Dueck

Tôi đã cài đặt wcanadian-insane (6.6MB) và so sánh sed -n 1p / usr / share / dict / words và sed '1q; d' / usr / share / dict / words bằng lệnh thời gian; lần đầu tiên mất 0,043 giây, lần thứ hai chỉ 0,002 giây, vì vậy việc sử dụng 'q' chắc chắn là một cải thiện hiệu suất!


1
Điều này cũng thường được viết:sed -n 5q
William Pursell

3
Tôi thích giải pháp này vì seddừng đọc bất kỳ dòng nào sau dòng thứ 5.
Anthony Geoghegan

1
Tôi đã cài đặt wcanadian-insane (6.6MB) và so sánh sed -n 1p /usr/share/dict/wordssed '1q;d' /usr/share/dict/wordssử dụng timelệnh; lần đầu tiên mất 0,043 giây, lần thứ hai chỉ 0,002 giây, vì vậy việc sử dụng 'q' chắc chắn là một cải thiện hiệu suất!
Roger Dueck

5

Ví dụ: nếu bạn muốn lấy các dòng từ 10 đến 20 của tệp, bạn có thể sử dụng từng phương pháp sau:

head -n 20 york.txt | tail -11

hoặc là

sed -n '10,20p' york.txt 

p trong lệnh trên là viết tắt của print.

Đây là những gì bạn sẽ thấy: nhập mô tả hình ảnh ở đây


2

Cách tiêu chuẩn để làm điều này là sử dụng các công cụ bên ngoài. Việc không cho phép sử dụng các công cụ bên ngoài trong khi viết kịch bản shell là vô lý. Tuy nhiên, nếu bạn thực sự không muốn sử dụng các công cụ bên ngoài, bạn có thể in dòng 5 với:

i=0; while read line; do test $((++i)) = 5 && echo "$line"; done < input-file

Lưu ý rằng điều này sẽ in ra dòng logic 5. Nghĩa là, nếu input-filechứa các dòng liên tục, chúng sẽ được tính là một dòng. Bạn có thể thay đổi hành vi này bằng cách thêm -rvào lệnh đọc. (Đó có lẽ là hành vi mong muốn.)


1
$((++i))dường như là một chủ nghĩa cơ bản; nếu OP bị hạn chế trong việc sử dụng các công cụ bên ngoài, tôi sẽ không cho rằng họ sẽ có quyền truy cập vào hơn một đồng bằng/bin/sh
Josip Rodin

@JosipRodin Không, đó là một tính năng POSIX (nhưng hỗ trợ cho số ++gia tăng được đánh dấu cụ thể là tùy chọn).
tripleee

@tripleee nó không hoạt động với dấu gạch ngang hiện đại là / bin / sh, vì vậy tôi sẽ không dựa vào nó.
Josip Rodin

Nhưng một cách giải quyết đơn giản như cũng $((i+=1))hoạt động trong Dash.
ba

$(($i+1))là cách giải quyết đơn giản mà tôi đã nghĩ đến.
Josip Rodin

1

Song song với câu trả lời của William Pursell , đây là một cấu trúc đơn giản sẽ hoạt động ngay cả trong shell v7 Bourne ban đầu (và do đó cũng có những nơi không có sẵn Bash).

i=0
while read line; do
    i=`expr "$i" + 1`
    case $i in 5) echo "$line"; break;; esac
done <file

Cũng lưu ý đến việc tối ưu hóa breakra khỏi vòng lặp khi chúng tôi đã có được dòng mà chúng tôi đang tìm kiếm.


0

Tôi đặc biệt không thích bất kỳ câu trả lời nào.

Đây là cách tôi đã làm điều đó.

# Convert the file into an array of strings
lines=(`cat "foo.txt"`)

# Print out the lines via array index
echo "${lines[0]}"
echo "${lines[1]}"
echo "${lines[5]}"

-1

Dễ dàng với perl! Nếu bạn muốn lấy dòng 1, 3 và 5 từ một tệp, hãy nói / etc / passwd:

perl -e 'while(<>){if(++$l~~[1,3,5]){print}}' < /etc/passwd

seq 5 | perl -ne 'print if $. ~~ [1, 4, 5]'nhưng smartmatch là thử nghiệm và nó sử dụng nản
Sorin

Không một giải pháp nào trong số các giải pháp khác là ngắn gọn hoặc cho phép sự linh hoạt này. (Tại sao dường như mọi thứ tiết kiệm thời gian và làm cho mọi thứ dễ dàng hơn lại bị "những người thông minh" làm cho "nản lòng, tất cả chúng ta đều phải
dán

-1
line=5; prep=`grep -ne ^ file.txt | grep -e ^$line:`; echo "${prep#$line:}"

3
bạn có thể mô tả một chút ít nhất là tại sao điều này lại có tác dụng để người đặt câu hỏi hiểu rõ hơn không?
ted

Vì vậy, grep đầu tiên chọn tất cả các dòng thêm số dòng ở đầu của chúng. Sau đó, grep thứ hai chọn một dòng cụ thể bằng cách khớp với số dòng ở đầu. Và cuối cùng số dòng được cắt bớt từ dòng bắt đầu bằng tiếng vang.
Oder

Đây là cả hai phức tạp và kém hiệu quả so với sed -n 5p, trong đó tất nhiên vẫn có thể được tối ưu hóa để một cái gì đó giống nhưsed -n '5!d;p;q'
tripleee
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.