GNU/Linux >> Belajar Linux >  >> Linux

Bagaimana cara memilih beberapa baris dari file atau dari pipa dalam skrip?

Anda dapat menggunakan awk ini:

awk -v s='2,4' 'BEGIN{split(s, a, ","); for (i in a) b[a[i]]} NR in b' file
two
four

Melalui skrip terpisah lines.sh :

#!/bin/bash
awk -v s="$1" 'BEGIN{split(s, a, ","); for (i in a) b[a[i]]} NR in b' "$2"

Kemudian berikan izin eksekusi:

chmod +x lines.sh

Dan menyebutnya sebagai:

./lines.sh '2,4' 'test.txt'

Coba sed :

sed -n '2p; 4p' inputFile

-n memberi tahu sed untuk menekan keluaran, tetapi untuk baris 2 dan 4 , p (cetak) perintah digunakan untuk mencetak baris-baris ini.

Anda juga dapat menggunakan rentang, mis.:

sed -n '2,4p' inputFile

Dua versi Bash murni. Karena Anda sedang mencari solusi umum dan dapat digunakan kembali, sebaiknya Anda berusaha sedikit untuk itu. (Juga, lihat bagian terakhir).

Versi 1

Skrip ini memasukkan seluruh stdin ke dalam array (menggunakan mapfile , jadi agak efisien) lalu mencetak baris yang ditentukan pada argumennya. Rentang valid, mis.,

1-4 # for lines 1, 2, 3 and 4
3-  # for everything from line 3 till the end of the file

Anda dapat memisahkannya dengan spasi atau koma. Baris-baris dicetak persis sesuai dengan urutan argumen yang diberikan:

lines 1 1,2,4,1-3,4- 1

akan mencetak baris 1 dua kali, lalu baris 2, lalu baris 4, lalu baris 1, 2 dan 3, lalu semuanya dari baris 4 hingga akhir, dan terakhir, baris 1 lagi.

Ini dia:

#!/bin/bash

lines=()

# Slurp stdin in array
mapfile -O1 -t lines

# Arguments:
IFS=', ' read -ra args <<< "$*"

for arg in "${args[@]}"; do
   if [[ $arg = +([[:digit:]]) ]]; then
      arg=$arg-$arg
   fi
   if [[ $arg =~ ([[:digit:]]+)-([[:digit:]]*) ]]; then
      ((from=10#${BASH_REMATCH[1]}))
      ((to=10#${BASH_REMATCH[2]:-$((${#lines[@]}))}))
      ((from==0)) && from=1
      ((to>=${#lines[@]})) && to=${#lines[@]}
      ((from<=to)) || printf >&2 'Argument %d-%d: lines not in increasing order' "$from" "$to"
      for((i=from;i<=to;++i)); do
         printf '%s\n' "${lines[i]}"
      done
   else
      printf >&2 "Error in argument \`%s'.\n" "$arg"
   fi
done
  • Pro:Keren banget.
  • Con:Perlu membaca seluruh aliran ke dalam memori. Tidak cocok untuk streaming tanpa batas.

Versi 2

Versi ini mengatasi masalah aliran tak terbatas sebelumnya. Namun Anda akan kehilangan kemampuan untuk mengulang dan menyusun ulang baris.

Hal yang sama, rentang diperbolehkan:

lines 1 1,4-6 9-

akan mencetak baris 1, 4, 5, 6, 9 dan semuanya sampai akhir. Jika rangkaian baris dibatasi, keluar segera setelah baris terakhir dibaca.

#!/bin/bash

lines=()
tillend=0
maxline=0

# Process arguments
IFS=', ' read -ra args <<< "[email protected]"

for arg in "${args[@]}"; do
   if [[ $arg = +([[:digit:]]) ]]; then
       arg=$arg-$arg
   fi
   if [[ $arg =~ ([[:digit:]]+)-([[:digit:]]*) ]]; then
      ((from=10#${BASH_REMATCH[1]}))
      ((from==0)) && from=1
      ((tillend && from>=tillend)) && continue
      if [[ -z ${BASH_REMATCH[2]} ]]; then
         tillend=$from
         continue
      fi
      ((to=10#${BASH_REMATCH[2]}))
      if ((from>to)); then
         printf >&2 "Invalid lines order: %s\n" "$arg"
         exit 1
      fi
      ((maxline<to)) && maxline=$to
      for ((i=from;i<=to;++i)); do
         lines[i]=1
      done
   else
      printf >&2 "Invalid argument \`%s'\n" "$arg"
      exit 1
   fi
done

# If nothing to read, exit
((tillend==0 && ${#lines[@]}==0)) && exit

# Now read stdin
linenb=0
while IFS= read -r line; do
   ((++linenb))
   ((tillend==0 && maxline && linenb>maxline)) && exit
   if [[ ${lines[linenb]} ]] || ((tillend && linenb>=tillend)); then
      printf '%s\n' "$line"
   fi
done
  • Pro:Sangat keren dan tidak membaca aliran penuh di memori.
  • Kontra:Tidak dapat mengulang atau menyusun ulang baris sebagai Versi 1. Kecepatan bukanlah poin terkuatnya.

Pemikiran lebih lanjut

Jika Anda benar-benar menginginkan skrip umum yang luar biasa yang melakukan apa yang dilakukan Versi 1 dan Versi 2, dan lebih banyak lagi, Anda pasti harus mempertimbangkan untuk menggunakan bahasa lain, misalnya, Perl:Anda akan mendapatkan banyak (dalam kecepatan tertentu)! Anda akan dapat memiliki opsi bagus yang akan melakukan banyak hal yang jauh lebih keren. Ini mungkin bermanfaat dalam jangka panjang, karena Anda menginginkan skrip yang umum dan dapat digunakan kembali. Anda bahkan mungkin memiliki skrip yang membaca email!

Penafian. Saya belum memeriksa skrip ini secara menyeluruh... jadi waspadalah terhadap bug!


Linux
  1. Bagaimana Menghapus Baris Kosong Dari File (termasuk Tab Dan Spasi)?

  2. Bagaimana Cara Menghapus Beberapa Baris Acak Dari File Teks Menggunakan Sed?

  3. Bagaimana cara memilih semua teks dari file dengan nano?

  1. Pilih Baris Dari File Teks Yang Memiliki Id Terdaftar Di File Lain?

  2. Bagaimana Membuat File Sementara Di Shell Script?

  3. Bagaimana Cara Cat File Dari Awk?

  1. Bagaimana Cara Menambahkan Beberapa Baris Ke File?

  2. Awk Dari Baris Yang Berbeda?

  3. Bagaimana cara menghapus garis yang muncul pada file B dari file A lainnya?