grepping string yang tetap di awal baris


20

grep "^$1"semacam karya, tapi bagaimana saya bisa melarikan diri "$1"sehingga grep tidak menafsirkan karakter apa pun di dalamnya secara khusus?

Atau ada cara yang lebih baik?

Sunting: Saya tidak ingin mencari '^$1'tetapi untuk string tetap yang dimasukkan secara dinamis yang seharusnya hanya cocok jika itu di awal baris. Itu yang saya maksud dengan $1.


Apakah Anda mencoba menggunakan tanda kutip tunggal alih-alih tanda kutip ganda, misalnya grep '^$1'? Atau bukankah maksud Anda bahwa Anda ingin mencegah $1perluasan oleh shell?
mnille

@ mnille Saya tidak ingin mencari '^ $ 1' tetapi untuk string tetap yang disisipkan secara dinamis yang seharusnya hanya cocok jika berada di awal baris. Itulah yang saya maksud dengan $ 1.
PSkocik

3
Anda dapat melakukannya dengan grepterlalu tetapi Anda harus melarikan diri karakter khusus dalam string Anda pertama misalnyaprintf %s ^;printf %s "$1" | sed 's/[][\.*^$]/\\&/g'; } | grep -f- infile
don_crissti

@don_crissti itu lebih baik daripada beberapa jawaban lainnya. Mau membuatnya?
roaima

@roaima - Saya tahu tetapi sudah ada banyak jawaban di sini dan ini (lolos dari karakter khusus di dalam vars) adalah sesuatu yang saya (dan beberapa pengguna lain di sini) telah memalu rumah selama beberapa waktu ... Anda selalu dapat menambahkan ke jawaban Anda jika Anda mau dan saya akan menghapus komentar di sini (jangan lupa untuk menambahkan kurung kurawal yang hilang).
don_crissti

Jawaban:


7

Saya tidak bisa memikirkan cara untuk melakukan ini menggunakan grep; ^itu sendiri adalah bagian dari ekspresi reguler sehingga menggunakannya membutuhkan ekspresi reguler untuk ditafsirkan. Itu sepele menggunakan pencocokan substring awk, perlatau apa pun:

awk -v search="$1" 'substr($0, 1, length(search)) == search { print }'

Untuk menangani string pencarian yang mengandung \, Anda dapat menggunakan trik yang sama seperti pada jawaban 123 :

search="$1" awk 'substr($0, 1, length(ENVIRON["search"])) == ENVIRON["search"] { print }'

Ini tidak akan berfungsi untuk string seperti\/
123

@ 123 memang, saya telah menambahkan varian untuk mengatasinya.
Stephen Kitt

Akan tetap gagal untuk string rumit seperti \\\/\/\/\\\\/yang terlihat \\///\\/dalam program. Sejauh yang saya tahu tidak ada cara untuk melarikan diri backslash dengan benar di awk, kecuali Anda tahu berapa banyak yang akan digunakan sebelumnya.
123

1
@ 123 terima kasih, saya telah menyesuaikan trik Anda dalam menjelajahi lingkungan untuk menghindari pemrosesan yang lolos.
Stephen Kitt

Saya masih suka solusi ini yang terbaik. Efisien (awk + tidak ada waktu yang terbuang melihat-lihat), startup cepat (awk + tidak ada proses tambahan yang diperlukan untuk pengaturan negara) menggunakan alat standar, dan cukup ringkas. Semua jawaban lain kekurangan setidaknya beberapa di antaranya. (Efisiensi adalah titik kuat di sini karena grep dikenal untuk kecepatan yang tak tertandingi.)
PSkocik

14

Jika Anda hanya perlu memeriksa apakah kecocokan ditemukan atau tidak, potong semua jalur input dengan panjang awalan yang diinginkan ( $1) dan kemudian gunakan grep pola-tetap:

if cut -c 1-"${#1}" | grep -qF "$1"; then
    echo "found"
else
    echo "not found"
fi

Juga mudah untuk mendapatkan hitungan garis yang cocok:

cut -c 1-"${#1}" | grep -cF "$1"

Atau nomor baris semua baris yang cocok (nomor baris dimulai dari 1):

cut -c 1-"${#1}" | grep -nF "$1" | cut -d : -f 1

Anda dapat memberi makan nomor baris ke headdan tailuntuk mendapatkan teks lengkap dari baris yang cocok, tetapi pada saat itu lebih mudah untuk hanya menjangkau bahasa skrip modern seperti Python atau Ruby.

(Contoh di atas mengasumsikan Posix grep dan cut. Mereka menganggap file yang akan dicari berasal dari input standar, tetapi dapat dengan mudah diadaptasi untuk mengambil nama file sebagai gantinya.)

Sunting: Anda juga harus memastikan bahwa pola ( $1) bukan string dengan panjang nol. Kalau tidak, cutgagal mengatakan values may not include zero. Juga, jika menggunakan Bash, gunakan set -o pipefailuntuk menangkap keluar kesalahan oleh cut.


10

Cara menggunakan perl yang akan menghargai backslash

v="$1" perl -ne 'print if index($_, $ENV{"v"} )==0' file

Ini mengatur variabel lingkungan v untuk perintah, kemudian mencetak jika indeks variabel adalah 0 yaitu awal baris.

Anda juga dapat melakukan hal yang sama dalam awk

v="$1" awk 'index($0, ENVIRON["v"])==1' file

7

Berikut ini opsi all-bash, bukan yang saya sarankan bash untuk pemrosesan teks, tetapi berfungsi.

#!/usr/bin/env bash
# searches for $1 at the beginning of the line of its input

len=${#1}
while IFS= read -r line
do
  [[ "${line:0:len}" = "$1" ]] && printf "%s\n" "$line"
done

Script menghitung panjang lenparameter yang dimasukkan $ 1, kemudian menggunakan ekspansi parameter pada setiap baris untuk melihat apakah lenkarakter pertama cocok dengan $ 1. Jika demikian, itu akan mencetak garis.


4

Jika Anda $1adalah ASCII murni dan Anda grepmemiliki -Popsi (untuk mengaktifkan PCRE), Anda dapat melakukan ini:

#!/bin/bash

line_start="$1"
line_start_raw=$(printf '%s' "$line_start" | od -v -t x1 -An)
line_start_hex=$(printf '\\x%s' $line_start_raw)
grep -P "^$line_start_hex"

Idenya di sini adalah yang grep -Pmemungkinkan ekspresi reguler dengan \xXXmenentukan karakter literal, di mana XXnilai hex ASCII dari karakter itu. Karakter dicocokkan secara harfiah, bahkan jika itu karakter regex khusus.

od digunakan untuk mengubah garis awal yang diharapkan ke daftar nilai hex, yang kemudian dirangkai, masing-masing diawali dengan \x oleh printf. ^kemudian menambahkan string ini untuk membangun regex yang diperlukan.


Jika Anda $1unicode, maka ini menjadi sedikit lebih sulit, karena tidak ada korespondensi 1: 1 karakter ke hex byte sebagai output oleh od.


3

Sebagai filter:

perl -ne 'BEGIN {$pat = shift} print if /^\Q$pat/' search-pattern

Jalankan pada satu atau lebih file:

perl -ne 'BEGIN {$pat = shift} print if /^\Q$pat/' search-pattern file..

Bagian “Kutipan metakarakter” dari dokumentasi perlre menjelaskan:

Mengutip metakarakter

Metakarakter Backslashed di Perl adalah alfanumerik, seperti \b, \w, \n. Tidak seperti beberapa bahasa ekspresi reguler lainnya, tidak ada simbol garis miring terbalik yang bukan alfanumerik. Jadi apa pun yang terlihat seperti \\, \(, \), \[, \], \{, atau \}selalu diartikan sebagai karakter literal, bukan metakarakter a. Ini pernah digunakan dalam idiom umum untuk menonaktifkan atau mengutip makna khusus dari metakarakter ekspresi reguler dalam string yang ingin Anda gunakan untuk suatu pola. Cukup kutip semua karakter non-"kata":

    $pattern =~ s/(\W)/\\$1/g;

(Jika use localediatur, maka ini tergantung pada lokal saat ini.) Hari ini lebih umum untuk menggunakan quotemetafungsi atau \Q urutan pelarian metaquoting untuk menonaktifkan arti khusus semua karakter metachar seperti ini:

    /$unquoted\Q$quoted\E$unquoted/

Hati-hati bahwa jika Anda menempatkan backslash literal (yang tidak di dalam variabel interpolasi) antara \Qdan \E, interpolasi backslash double-quotish dapat menyebabkan hasil yang membingungkan. Jika Anda perlu menggunakan backslash literal di dalam \Q...\E, bacalah "Detail Gory dari parsing yang dikutip konstruk" di perlop .

quotemetadan \Qsepenuhnya dijelaskan dalam kutipan .


3

Jika grep Anda memiliki opsi -P, yang berarti PCRE , Anda dapat melakukan ini:

grep -P "^\Q$1\E"

Lihat pertanyaan ini , dan lihat PCRE doc untuk detailnya jika Anda mau.


2

Jika ada karakter yang tidak Anda gunakan, Anda bisa menggunakannya untuk menandai awal baris. Misalnya, $'\a'(ASCII 007). Itu jelek tapi itu akan berhasil:

{ echo 'this is a line to match'; echo 'but this is not'; } >file.txt

stuffing=$'\a'    # Guaranteed never to appear in your source text
required='this'   # What we want to match that beginning of a line

match=$(sed "s/^/$stuffing/" file.txt | grep -F "$stuffing$required" | sed "s/^$stuffing//")

if [[ -n "$match" ]]
then
    echo "Yay. We have a match: $match"
fi

Jika Anda tidak membutuhkan baris yang cocok maka Anda dapat menghapus trailing seddan menggunakannya grep -qF. Tapi itu jauh lebih mudah dengan awk(atau perl) ...


0

Saat Anda ingin melihat file tanpa loop, Anda dapat menggunakan:
Potong file dengan panjang string pencarian

  cut -c1-${#1} < file

Cari string tetap dan nomor baris kembali

  grep -Fn "$1" <(cut -c1-${#1} < file)

Gunakan nomor baris untuk sesuatu seperti sed -n '3p;11p' file

  sed -n "$(grep -Fn "$1" <(cut -c1-${#1} < file) | sed 's/:.*/p;/' | tr -d '\n')" file

Saat Anda ingin menghapus garis-garis ini, gunakan

  sed "$(grep -Fn "$1" <(cut -c1-${#1} < file) | sed 's/:.*/d;/' | tr -d '\n')" file
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.