Saya ingin menghitung berapa kali urutan byte tertentu terjadi di dalam file yang saya miliki. Sebagai contoh, saya ingin mencari tahu berapa kali angka itu \0xdeadbeef
terjadi di dalam file yang dapat dieksekusi. Saat ini saya sedang melakukan itu menggunakan grep:
#/usr/bin/fish
grep -c \Xef\Xbe\Xad\Xde my_executable_file
(Bytes ditulis dalam urutan terbalik karena CPU saya adalah little-endian)
Namun, saya memiliki dua masalah dengan pendekatan saya:
- Mereka
\Xnn
escape sequence hanya bekerja di shell ikan. - grep sebenarnya menghitung jumlah garis yang berisi angka ajaibku. Jika polanya muncul dua kali dalam garis yang sama, itu hanya akan dihitung satu kali.
Apakah ada cara untuk memperbaiki masalah ini? Bagaimana saya bisa membuat liner yang satu ini berjalan di Bash shell dan secara akurat menghitung berapa kali pola terjadi di dalam file?
bash
grep
escape-characters
hugomg
sumber
sumber
grep -o
11221122
, seperti apa input yang harus dikembalikan112211221122
? 1 atau 2?Jawaban:
Ini adalah solusi satu baris yang diminta (untuk shell baru-baru ini yang memiliki "substitusi proses"):
Jika tidak ada "substitusi proses"
<(…)
, gunakan saja grep sebagai filter:Di bawah ini adalah deskripsi terperinci dari setiap bagian dari solusi.
Nilai byte dari angka hex:
Masalah pertama Anda mudah diselesaikan:
Ubah bagian atas
X
ke bawahx
dan gunakan printf (untuk sebagian besar shell):Atau gunakan:
Untuk shell yang memilih untuk tidak mengimplementasikan representasi '\ x'.
Tentu saja, menerjemahkan hex ke octal akan bekerja pada (hampir) shell apa pun:
Di mana "$ sh" adalah shell (wajar). Tetapi cukup sulit untuk tetap mengutipnya dengan benar.
File biner.
Solusi yang paling kuat adalah mengubah file dan urutan byte (keduanya) menjadi beberapa pengkodean yang tidak memiliki masalah dengan nilai karakter aneh seperti (baris baru)
0x0A
atau (byte nol)0x00
. Keduanya cukup sulit untuk dikelola dengan benar dengan alat yang dirancang dan diadaptasi untuk memproses "file teks".Transformasi seperti base64 mungkin tampak valid, tetapi menyajikan masalah bahwa setiap byte input mungkin memiliki hingga tiga representasi output tergantung apakah itu byte pertama, kedua atau ketiga dari posisi mod 24 (bit).
Hex mentransformasi.
Thats why transformasi paling kuat harus menjadi yang dimulai pada setiap batas byte, seperti representasi HEX sederhana.
Kita bisa mendapatkan file dengan representasi hex file dengan salah satu dari alat ini:
Urutan byte untuk pencarian sudah dalam hex dalam hal ini.
:
Tetapi bisa juga diubah. Contoh round trip hex-bin-hex berikut:
String pencarian dapat diatur dari representasi biner. Salah satu dari tiga opsi yang disajikan di atas od, hexdump, atau xxd adalah setara. Pastikan untuk memasukkan spasi untuk memastikan kecocokan berada pada batas byte (tidak boleh menggeser shift):
Jika file biner terlihat seperti ini:
Kemudian, pencarian grep sederhana akan memberikan daftar urutan yang cocok:
Satu baris?
Itu semua dapat dilakukan dalam satu baris:
Misalnya, mencari
11221122
dalam file yang sama akan membutuhkan dua langkah ini:Untuk "melihat" kecocokan:
… 0a 3131323231313232313132323131323231313232313132323131323231313232 313132320a
Buffering
Ada kekhawatiran bahwa grep akan buffer seluruh file, dan, jika file besar, membuat beban berat untuk komputer. Untuk itu, kami dapat menggunakan solusi sed yang tidak disatukan:
Sed pertama adalah unbuffered (
-u
) dan hanya digunakan untuk menyuntikkan dua baris baru pada aliran per string yang cocok. Keduased
hanya akan mencetak garis yang cocok (pendek). Wc -l akan menghitung garis yang cocok.Ini hanya akan menyangga beberapa garis pendek. String yang cocok di sed kedua. Ini harus cukup rendah dalam sumber daya yang digunakan.
Atau, agak lebih kompleks untuk dipahami, tetapi ide yang sama dalam satu sed:
sumber
grep
akan berakhir memuat seluruh dalam memori (di sini dua kali ukuran file asli + 1 karena pengkodean hex), jadi pada akhirnya, itu menjadi lebih overhead daripadapython
pendekatan atau yangperl
dengan-0777
. Anda juga memerlukangrep
implementasi yang mendukung garis panjang sewenang-wenang (yang-o
biasanya mendukung ). Jawaban yang bagus sebaliknya.od -An -tx1 | tr -d '\n'
atauhexdump -v -e '/1 " %02x"'
dengan string pencarian juga mengandung spasi hindari ini, tapi saya tidak melihat perbaikan untuk ituxxd
.sed -u
(jika tersedia) untuk unbuffer. Itu berarti akan membaca satu byte pada satu waktu pada input, dan output outputnya langsung tanpa buffering. Di anycase, masih perlu memuat seluruh garis di ruang pola, jadi tidak akan membantu di sini.Dengan GNU
grep
's-P
(perl-regexp) flagLC_ALL=C
adalah untuk menghindari masalah di tempat multi-byte di managrep
kalau tidak akan mencoba untuk menafsirkan urutan byte sebagai karakter.-a
memperlakukan file biner yang setara dengan file teks (alih-alih perilaku normal, di managrep
hanya mencetak apakah setidaknya ada satu yang cocok atau tidak)sumber
grep
agar cocok?-a
opsi, jika tidak grep akan menjawab denganBinary file file.bin matches
untuk file apa pun yang grep mendeteksi sebagai biner.Yang memperlakukan file input sebagai biner (tidak ada terjemahan untuk pengumpanan atau penyandian baris, lihat perlrun ) kemudian lilitkan pada file input yang tidak mencetak incrementing counter untuk semua kecocokan dari hex yang diberikan (atau bentuk apa pun, lihat perlre ) .
sumber
-0ooo
).$/
, dengan tradeoff yang sedikit berbeda (penggunaan memori sebanding dengan jarak maksimum antara urutan tersebut):perl -nE 'BEGIN { $/ = "\xef\xbe\xad\xde" } chomp; $c++ unless eof && length; END { say $c }'
Dengan GNU
awk
, Anda dapat melakukan:Jika ada byte yang merupakan operator ERE, mereka harus lolos (dengan
\\
). Seperti0x2e
yang.
harus dimasukkan sebagai\\.
atau\\\x2e
. Selain itu, itu harus bekerja dengan nilai byte sembarang termasuk 0 dan 0xa.Perhatikan bahwa ini tidak sesederhana hanya
NR-1
karena ada beberapa kasus khusus:RT==""
.Perhatikan juga bahwa dalam kasus terburuk (jika file tidak mengandung istilah pencarian), file tersebut akan berakhir dimuat seluruhnya dalam memori).
sumber
Terjemahan paling mudah yang saya lihat adalah:
Di mana saya telah menggunakan
$'\xef'
sebagai bash ANSI-quoting (awalnya aksh93
fitur, sekarang didukung olehzsh
,bash
,mksh
, FreeBSDsh
) versi ikan\Xef
, dan digunakangrep -o ... | wc -l
untuk menghitung contoh.grep -o
output setiap pertandingan pada baris yang terpisah. The-a
flag membuat berperilaku grep pada file biner dengan cara yang sama dilakukannya pada file teks.-F
adalah untuk string tetap sehingga Anda tidak perlu melarikan diri dari operator regex.Seperti pada
fish
kasus Anda, Anda tidak dapat menggunakan pendekatan itu jika urutan yang dicari termasuk byte 0 atau 0xa (baris baru di ASCII).sumber
printf '%b' $(printf '\\%o ' $((0xef)) $((0xbe)) $((0xad)) $((0xde))) > hugohex'
akan menjadi metode "cangkang murni" yang paling portabel. Tentu saja:printf "efbeadde" | xxd -p -r > hugohex
sepertinya metode yang paling praktis.Anda bisa menggunakan Python
bytes.count
metode untuk mendapatkan jumlah total substring yang tidak tumpang tindih dalam bytestring.One-liner ini akan memuat seluruh file ke dalam memori, jadi bukan yang paling efisien, tetapi bekerja dan lebih terbaca daripada Perl; D
sumber
239I$ 190I$ 173I$ 222I$ HXA ERfile$Y 0UC <:S^EQA$; %C$> QC=
(gd & r)mmap()
file dalam Python ; yang akan mengurangi komit memori.sumber
Saya pikir Anda dapat menggunakan Perl, cobalah:
Ganti perintah
s
memberikan jumlah penggantian yang dibuat, -0777 berarti tidak memperlakukan baris baru sebagai karakter khusus,e
- mengeksekusi perintah,say
untuk mencetak apa yang terjadi kemudian mencetak karakter baris baru,n
saya belum sepenuhnya dipahami, tetapi tidak berhasil w / out - dari dokumen:sumber