Dapatkan baris tertentu dari file teks hanya dengan menggunakan skrip shell


100

Saya mencoba mendapatkan baris tertentu dari file teks.

Sejauh ini, secara online saya hanya melihat hal-hal seperti sed, (Saya hanya dapat menggunakan sh -not bash atau sed atau semacamnya). Saya perlu melakukan ini hanya dengan menggunakan skrip shell dasar.

cat file | while read line
    do
       #do something
    done

Saya tahu cara mengulang melalui garis, seperti yang ditunjukkan di atas, tetapi bagaimana jika saya hanya perlu mendapatkan konten dari baris tertentu


apakah kamu tahu nomor barisnya?
Mehul Rathod

1
Kemudian Anda bisa menghitung.
Ignacio Vazquez-Abrams

ya, nomor barisnya adalah 5 @MehulRathod
GangstaGraham

3
Kenapa catoke tapi sedtidak? Itu tidak masuk akal.
William Pursell

5
Karena tidak ada yang bisa mengatakan tidak cat. Aw ... imut cat!

Jawaban:


204

sed:

sed '5!d' file

awk:

awk 'NR==5' file

Bagaimana dengan perintah sh, saya tidak bisa menggunakan sed, awk. Saya harus memperjelas hal ini dalam pertanyaan.
GangstaGraham

@GangstaGraham Anda mengatakan Anda tahu cara mengulang melalui garis, bagaimana jika menambahkan penghitung? jika penghitung mencapai nomor baris target Anda, dapatkan garis dan hentikan loop. apakah itu membantu?
Kent

4
@Kanagavelsugumar membaca halaman info sed. 5!dberarti hapus semua baris kecuali 5. shell var dimungkinkan, Anda perlu tanda kutip ganda.
Kent

13
Saya menyarankan untuk menambahkan varian lain: sed -n 5pIni tampaknya lebih logis untuk diingat untuk pemula, karena -nberarti "tidak ada keluaran secara default" dan psingkatan dari "print", dan tidak ada penyebutan penghapusan yang berpotensi membingungkan (ketika orang membicarakan file, menghapus baris cenderung berarti sesuatu yang berbeda).
Josip Rodin

1
@JosipRodin Anda benar, -n '5p'bekerja untuk masalah ini juga. Perbedaannya di sini adalah, dengan 5!dAnda dapat menambahkan -iuntuk menulis perubahan kembali ke file. Namun, dengan -n 5pAnda harus melakukannya sed -n '5p' f > f2&& mv f2 flagi, untuk pertanyaan ini, saya setuju dengan pendapat Anda.
Kent

21

Dengan asumsi lineadalah variabel yang menyimpan nomor baris yang Anda butuhkan, jika Anda dapat menggunakan headdan tail, maka itu cukup sederhana:

head -n $line file | tail -1

Jika tidak, ini seharusnya berhasil:

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

Ini -eqperbandingan untuk bilangan bulat, sehingga ingin nomor baris, tidak puas baris ( $line). Ini harus diperbaiki dengan mendefinisikan misalnya want=5sebelum pengulangan, dan kemudian menggunakan -eqperbandingan pada $want. [dipindahkan dari suntingan yang ditolak]
Josip Rodin

1
@JosipRodin Saya membuat saran edit independen berdasarkan komentar Anda, karena saya setuju dengannya. Semoga kali ini tidak ditolak.
Victor Zamanian

15

Anda bisa menggunakan sed -n 5p file.

Anda juga bisa mendapatkan rentang, misalnya sed -n 5,10p file.


11

Metode kinerja terbaik

sed '5q;d' file

Karena sedberhenti membaca baris apa pun setelah baris ke-5

Perbarui eksperimen dari Tn. Roger Dueck

Saya menginstal wcanadian-insane (6.6MB) dan membandingkan sed -n 1p / usr / share / dict / words dan sed '1q; d' / usr / share / dict / words menggunakan perintah waktu; yang pertama mengambil 0,043 detik, yang kedua hanya 0,002, jadi menggunakan 'q' jelas merupakan peningkatan kinerja!


1
Ini juga biasa ditulis:sed -n 5q
William Pursell

3
Saya suka solusi ini karena sedberhenti membaca baris apa pun setelah baris ke-5.
Anthony Geoghegan

1
Saya menginstal wcanadian-insane (6.6MB) dan membandingkan sed -n 1p /usr/share/dict/wordsserta sed '1q;d' /usr/share/dict/wordsmenggunakan timeperintah; yang pertama mengambil 0,043 detik, yang kedua hanya 0,002, jadi menggunakan 'q' jelas merupakan peningkatan kinerja!
Roger Dueck

5

Jika misalnya Anda ingin mendapatkan baris 10 hingga 20 dari sebuah file, Anda dapat menggunakan masing-masing dari dua metode ini:

head -n 20 york.txt | tail -11

atau

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

p di atas perintah berarti pencetakan.

Inilah yang akan Anda lihat: masukkan deskripsi gambar di sini


2

Cara standar untuk melakukan hal semacam ini adalah dengan menggunakan alat eksternal. Melarang penggunaan alat eksternal saat menulis skrip shell tidak masuk akal. Namun, jika Anda benar-benar tidak ingin menggunakan alat eksternal, Anda dapat mencetak baris 5 dengan:

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

Perhatikan bahwa ini akan mencetak baris logis 5. Artinya, jika input-fileberisi garis lanjutan, mereka akan dihitung sebagai satu baris. Anda dapat mengubah perilaku ini dengan menambahkan -rke perintah baca. (Yang mungkin merupakan perilaku yang diinginkan.)


1
$((++i))tampaknya menjadi bashism; jika OP dibatasi dalam menggunakan alat eksternal, saya tidak akan berasumsi mereka akan memiliki akses ke lebih dari sekadar/bin/sh
Josip Rodin

@JosipRodin Tidak, ini adalah fitur POSIX (tetapi dukungan untuk ++peningkatan secara khusus ditandai sebagai opsional).
tripleee

@tripleee tidak berfungsi dengan tanda hubung modern sebagai / bin / sh, jadi saya tidak akan mengandalkannya.
Josip Rodin

Tapi solusi sederhana seperti $((i+=1))bekerja di Dash juga.
tripleee

$(($i+1))adalah solusi sederhana yang saya pikirkan.
Josip Rodin

1

Sejalan dengan jawaban William Pursell , berikut adalah konstruksi sederhana yang harus bekerja bahkan di shell Bourne v7 asli (dan dengan demikian juga tempat-tempat di mana Bash tidak tersedia).

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

Perhatikan juga optimasi untuk breakkeluar dari loop ketika kita telah mendapatkan garis yang kita cari.


0

Saya tidak terlalu menyukai jawaban apa pun.

Inilah cara saya melakukannya.

# 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

Mudah dengan perl! Jika Anda ingin mendapatkan baris 1, 3 dan 5 dari sebuah file, katakan / etc / passwd:

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

seq 5 | perl -ne 'print if $. ~~ [1, 4, 5]'tetapi smartmatch bersifat eksperimental dan penggunaannya tidak disarankan
Sorin

Tidak ada satu pun dari solusi lain yang sesingkat ini, atau memungkinkan banyak fleksibilitas. (Mengapa tampaknya segala sesuatu yang menghemat waktu dan membuat segalanya lebih mudah, "dikecilkan" oleh "orang pintar", apakah kita semua harus menatap layar sepanjang hari?)
dagelf

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

3
Bisakah Anda menjelaskan sedikit setidaknya mengapa ini berhasil untuk membuatnya lebih jelas bagi orang yang mengajukan pertanyaan?
ted

Jadi, grep pertama memilih semua baris yang menambahkan nomor baris pada awalnya. Kemudian grep kedua memilih baris tertentu dengan mencocokkan nomor baris di awal. Dan akhirnya nomor baris dipangkas dari baris yang dimulai di echo.
Oder

Ini rumit dan tidak efisien dibandingkan sed -n 5p, yang tentu saja masih dapat dioptimalkan untuk sesuatu sepertised -n '5!d;p;q'
tripleee
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.