Tarkibga o'tish

Fayl va matn qidirish: find, locate va grep

"Fayl qayerda?" va "bu xabar qaysi faylda yozilgan?" — ikki xil savol, garchi yangi boshlovchilar ikkalasiga ham ko'pincha bitta buyruq bilan javob izlasa ham. find va locate fayllarni yo'li, nomi va metama’lumotlari bo'yicha qidiradi. grep esa faylning ichidagi matnni qatorma-qator qidiradi. To'g'ri vositani tanlash vaqtni tejaydi va ruxsat xatosini "hech narsa topilmadi" deb noto'g'ri tushunib qolishning oldini oladi.

Misollar Ubuntu Server LTS’da, Bash muhitida, GNU Findutils va GNU Grep bilan ishlashga mo'ljallangan. Sizga Fayl va katalog buyruqlari maqolasidagi qo'shtirnoqlar va wildcard’lar, Matnli fayllarni ko'rish maqolasidagi standart chiqish (stdout) va standart xato (stderr) tushunchalari tanish bo'lishi kerak. Quyida find bilan nom, tur, hajm va vaqt bo'yicha qidirishni, grep natijasi va chiqish holatini to'g'ri o'qishni, shuningdek nomida bo'sh joy yoki yangi qator belgisi bor fayllarni natijani buzmasdan qayta ishlashni ko'rib chiqamiz.

Laboratoriyani tayyorlash

mkdir -p ~/lab/search/{config,logs,archive}
printf 'listen_port=8080\nenv=production\n' > ~/lab/search/config/app.conf
printf 'level=INFO order_id=ord-1001 message=checkout started\n' > ~/lab/search/logs/app.log
printf 'level=ERROR order_id=ord-1002 message=payment gateway timeout\n' >> ~/lab/search/logs/app.log
printf 'level=error order_id=ord-1003 message=invalid card\n' > ~/lab/search/logs/auth.log
printf 'quarterly report draft\n' > ~/lab/search/archive/report.txt
touch -d '10 days ago' ~/lab/search/archive/report.txt
find ~/lab/search -maxdepth 3 -printf '%y %p\n'

touch -d — GNU Coreutils’ning kengaytmasi. U faqat shu laboratoriya faylining vaqt tamg'asini o'zgartiradi, shunda keyingi -mtime va -newer misollarida solishtirishga arziydigan narsa bo'ladi.

find: ishlab turgan fayl tizimini aylanib chiqish

find buyrug'ining umumiy ko'rinishi:

find STARTING_POINT... EXPRESSION
find ~/lab/search -type f -name '*.log' -print

Bu yerda:

  • ~/lab/search — boshlang'ich nuqta, qidiruv shu yerdan boshlanadi.
  • -type f — oddiy fayllarni tanlaydigan test.
  • -name '*.log' — fayl nomini (basename) andoza bilan solishtiradigan test.
  • -print — har bir mos kelgan yo'lni standart chiqishga yozadigan amal.

Ikki test orasida hech qanday operator bo'lmasa, find ularni mantiqiy VA (-a) bilan bog'langan deb hisoblaydi. '*.log' andozasi qo'shtirnoqqa olingan: shunda uni har bir nomzod fayl uchun find o'zi tekshiradi. Aks holda Bash uni find ishga tushishidan oldinroq joriy katalogdagi fayllar bilan almashtirib yuborgan bo'lardi.

Boshlang'ich nuqtani doim aniq yozing. U yozilmasa, GNU find . ni (joriy katalogni) oladi, lekin skript qaysi katalogdan ishga tushirilganiga qarab natija bilinmay o'zgarib qolishi mumkin.

Nom va yo'l bo'yicha qidirish

find ~/lab/search -type f -name '*.conf'
find ~/lab/search -type f -iname '*.LOG'
find ~/lab/search -path '*/archive/*'

-name fayl nomini katta-kichik harfni farqlagan holda solishtiradi, -iname harf registriga e’tibor bermaydi, -path esa faqat oxirgi qismni emas, butun yo'lni solishtiradi. -iname ning Unicode harflar bilan aniq qanday ishlashi locale va dastur versiyasiga bog'liq. Shu sabab avtomatlashtirishda registrni farqlamaydigan qidiruvga tayangandan ko'ra, fayllarni bir xil qoidada nomlash ishonchliroq.

Tur, chuqurlik, hajm va vaqt

find ~/lab/search -maxdepth 2 -type d -print
find ~/lab/search -type f -size +1k -print
find ~/lab/search -type f -mtime +7 -print
find ~/lab/search -type f -newer ~/lab/search/config/app.conf -print
  • -type f oddiy fayllarni, -type d kataloglarni, -type l symbolic link’larni tanlaydi.
  • -maxdepth 2 qidiruvni boshlang'ich nuqtadan pastga qarab ikki daraja bilan cheklaydi.
  • -size +1k GNU find da 1024 baytdan katta fayllarni tanlaydi.
  • -mtime +7 o'zgartirilgan vaqti oxirgi yettita to'liq 24 soatlik davrdan oldinroq bo'lgan fayllarni tanlaydi.
  • -newer FILE har bir faylning o'zgartirilgan vaqtini namuna faylning o'zgartirilgan vaqti bilan solishtiradi.

-mtime 1 ni kalendar ma’nosidagi "kecha" deb tushunmang. GNU find vaqtni mahalliy yarim tundan emas, buyruq ishga tushgan paytdan boshlab 24 soatlik davrlarga bo'lib hisoblaydi. Daqiqagacha aniqlik kerak bo'lsa, -mmin dan foydalaning. Aynan kalendar kuni chegarasi kerak bo'lsa, o'z tizimingizdagi man find dan -daystart opsiyasini o'qing.

Testlarni mantiqiy operatorlar bilan birlashtirish

find ~/lab/search -type f \( -name '*.log' -o -name '*.conf' \) -print

Qavslar qochirilgan (\), chunki shell ularni maxsus belgi deb tushunadi. Qavslarsiz -a, -o va ! operatorlarining ustuvorligi siz kutmagan natija berishi mumkin. Inkor quyidagicha yoziladi:

find ~/lab/search -type f ! -path '*/archive/*' -print

Topilgan fayllar ustida amal bajarish

Faqat o'qiydigan misol:

find ~/lab/search -type f -name '*.conf' -exec file -- {} +

{} o'rniga topilgan yo'llar qo'yiladi, + esa iloji boricha ko'p yo'lni file buyrug'ining bitta chaqiruviga jamlaydi. Boshqa shakl — -exec ... {} \; — odatda har bir topilgan fayl uchun alohida yangi jarayon ishga tushiradi, shuning uchun katta katalog daraxtida ancha sekin ishlaydi.

Danger

find ... -delete, -exec rm, -exec chmod va -exec chown bir vaqtda ko'p faylni o'zgartiradi va sukut bo'yicha hech qanday tasdiq so'ramaydi. Ulardan birortasini production ma’lumotlariga qarshi ishlatishdan oldin aynan shu ifodani avval -print bilan ishga tushiring, qidiruv boshqa mount nuqtalariga o'tishi yoki symlink’lar ortidan borishi kerakmi-yo'qligini tekshiring va zaxira nusxa yoki VM snapshot oling. Hatto laboratoriyada ham yalang'och -exec rm o'rniga -ok (har bir amaldan oldin so'raydi) yoki alohida, ko'rib chiqilgan qadamni afzal ko'ring.

Ishonchi pastroq foydalanuvchi yoza oladigan kataloglarda -execdir yo'l bilan bog'liq ba’zi poyga (race) xavflarini kamaytiradi. Lekin u xavfsiz PATH ning va chaqirilayotgan buyruqning o'z ehtiyot choralarining o'rnini bosmaydi. Root sifatida ishlayotganda cheklanmagan find / ni troubleshooting’dagi odatiy qadamingizga aylantirmang.

find xatolarini o'qish: Permission denied

Haqiqiy tizimda find sizning foydalanuvchingiz kira olmaydigan kataloglarni ham aylanib chiqadi. Ularning har biri uchun standart xato oqimiga bitta qator yoziladi va bu qatorlar standart chiqishdagi topilgan natijalar bilan aralashib chiqadi:

/etc/app/app.conf
find: '/etc/ssl/private': Permission denied
/etc/hosts

Topilganlar (stdout) va xatolar (stderr) — ikki alohida oqim, ular shunchaki bitta terminalda chiqadi. Natijani biror joyga yo'naltirishingiz bilan aynan shu farq hal qiluvchi bo'lib qoladi:

  • find / -name '*.conf' 2>/dev/null barcha xatolarni yashiradi, jumladan noto'g'ri yozilgan yo'lni yoki bilishingiz kerak bo'lgan, haqiqatan o'qib bo'lmaydigan joyni ham. Natijada chala natija to'liq natijadan farq qilmay qoladi.
  • find / -name '*.conf' 2>find-errors.log ekranda topilganlarni toza ko'rsatadi, xatolarni esa keyin ko'rib chiqish uchun faylga saqlaydi. Qidiruv natijasiga ishonish kerak bo'lganda shu yo'l xavfsizroq.

stderr ni ko'r-ko'rona tashlab yuborish — find asosidagi tekshiruv (audit) jimgina kam natija ko'rsatishining eng keng tarqalgan sababi. Agar sizga haqiqatan faqat o'qish mumkin bo'lgan joylar kerak bo'lsa, buni ochiq ko'rsating: qidiruvni o'zingizga tegishli katalog ichidan boshlang. Qidiruv doirasi chala bo'lganini ko'rsatadigan dalilni o'chirib qo'ymang.

locate: indeks bo'yicha tezkor qidirish

Ubuntu LTS’da locate odatda plocate paketi orqali keladi va minimal o'rnatilgan tizimda bo'lmasligi mumkin:

command -v locate || echo "locate is not installed"

Agar mavjud bo'lsa:

locate app.conf
locate -b '\app.conf'

locate oldindan updatedb tuzib qo'ygan indeks bo'yicha qidiradi. Shu sababli u katta fayl tizimida ham tez ishlaydi. Buning evaziga indeks eskirgan bo'lishi mumkin:

  • indeks oxirgi marta yangilangandan keyin yaratilgan fayl hali ko'rinmaydi;
  • o'chirilgan fayl nomi bir muddat indeksda qolib ketishi mumkin;
  • updatedb.conf ba’zi yo'llar yoki fayl tizimlarini butunlay chiqarib tashlagan bo'lishi mumkin;
  • siz ko'radigan natija ishlab turgan fayl tizimiga emas, indeksning o'z ko'rinish qoidalariga bog'liq.

Fayl tizimining hozirgi, real vaqtdagi holati va metama’lumot bo'yicha filtrlar kerak bo'lsa, find dan foydalaning. Indeksni qayta qurish disk (I/O) yukini oshiradi va tizim siyosatiga bo'ysunadi, shuning uchun production’da bitta faylni topish uchun sudo updatedb ni ishga tushirmang. Ubuntu’da plocate indeksini yangilashni odatda systemd timer allaqachon bajarib turadi.

grep: fayllar ichidan qidirish

Aniq (literal) satrni qidirishda -F eng to'g'ri tanlov:

grep -nF 'level=ERROR' ~/lab/search/logs/app.log
2:level=ERROR order_id=ord-1002 message=payment gateway timeout

-n qator raqamini qo'shadi. Sukut bo'yicha grep faqat mos kelgan bo'lakni emas, butun qatorni chiqaradi. Faqat mos kelgan qismni chiqarish uchun -o ishlatiladi.

Ko'pincha birga ishlatiladigan opsiyalar:

grep -niF 'level=error' ~/lab/search/logs/*.log
grep -cF 'level=ERROR' ~/lab/search/logs/app.log
grep -vF 'level=INFO' ~/lab/search/logs/app.log
grep -lF 'timeout' ~/lab/search/logs/*.log
grep -C 1 -F 'timeout' ~/lab/search/logs/app.log
  • -i harf registrini farqlamaydi.
  • -c qator ichidagi mosliklar sonini emas, mos kelgan qatorlar sonini chiqaradi.
  • -v mos kelmagan qatorlarni tanlaydi.
  • -l kamida bitta moslik topilgan har bir faylning faqat nomini chiqaradi.
  • -C 1 har bir moslikdan oldingi va keyingi bittadan qatorni ham ko'rsatadi.

Aniq matn, asosiy regex va kengaytirilgan regex

grep -F 'a.b' file.txt
grep '^level=' file.txt
grep -E 'level=(ERROR|WARN)' file.txt

-F bilan har bir belgi o'z ma’nosida, ya’ni oddiy belgi sifatida qidiriladi. Oddiy grep asosiy regular expression’lardan (BRE), grep -E esa kengaytirilgan regular expression’lardan (ERE) foydalanadi. Regex’da ^ qator boshini, $ qator oxirini bildiradi, . istalgan bitta belgiga mos keladi, * esa oldingi elementni nol yoki undan ko'p marta takrorlaydi. Bu * shell’dagi wildcard * bilan bog'liq emas: shell’dagi * fayl nomlarini grep ga yetib kelishidan oldinroq kengaytiradi.

Qidiriladigan so'z ishonchsiz manbadan kelsa, grep -F -- "$needle" dan foydalaning:

needle='message=timeout'
grep -nF -- "$needle" ~/lab/search/logs/app.log

-- - bilan boshlanadigan qiymatni grep opsiya deb qabul qilishining oldini oladi, o'zgaruvchini qo'shtirnoqqa olish esa so'zlarga bo'linish va globbing’ni to'xtatadi.

Rekursiv qidirish

grep -RInF --include='*.log' 'timeout' ~/lab/search

-R symbolic link’lar ortidan borishi mumkin, -r esa odatda bormaydi. Shu sabab katalog daraxtida nima borligini aniq bilmasangiz, -r xavfsizroq tanlov. Katta repozitoriy yoki / bo'ylab nazoratsiz rekursiv qidiruv diskka sezilarli yuk beradi. Qidiruv doirasini --include, --exclude va --exclude-dir bilan toraytiring.

Chiqish holatini to'g'ri o'qish

GNU grep uchta chiqish holatini qaytaradi:

  • 0: kamida bitta qator mos keldi.
  • 1: birorta ham qator mos kelmadi.
  • 2: xatolik yuz berdi (noto'g'ri andoza, o'qib bo'lmaydigan fayl va hokazo).
if grep -qF 'level=ERROR' ~/lab/search/logs/app.log; then
    echo "found an ERROR line"
else
    status=$?
    if ((status == 1)); then
        echo "no ERROR line found"
    else
        printf 'grep failed, status=%s\n' "$status" >&2
    fi
fi

-q odatdagi chiqishni o'chiradi va shart ichida ishlatish uchun qulay. Bunga qo'shimcha 2>/dev/null yozsangiz, "moslik topilmadi" va "grep xato bilan tugadi" holatlarining farqi yashirinib qolishi mumkin. Bu farq muhim bo'lgan joyda bunday qilmang.

Nomida bo'sh joy va yangi qator bor fayllar

Linux’da fayl nomida yangi qator belgisi bo'lishi ham qonuniy. Shu sababli find ... -print | while read ... har qanday nom uchun ishonchli emas. Buning o'rniga yozuvlarni NUL bayt bilan ajrating:

find ~/lab/search -type f -name '*.log' -print0 |
    while IFS= read -r -d '' file; do
        printf 'file: %q\n' "$file"
    done

Bash’dagi read -d '' NUL baytgacha o'qiydi. Natijani tashqi vositalarga uzatishda find -print0 va xargs -0 juftligi ko'p ishlatiladi. Lekin vazifaga mos kelsa, find -exec ... {} + soddaroq va xavfsizroq.

Ishni parallel bajarish: xargs -0 -P

find -exec ... {} + bitta buyruqni ishga tushiradi va unga sig'adigan barcha argumentlarni beradi. xargs ham xuddi shunday NUL bilan xavfsiz jamlashni beradi, ustiga -exec o'zi qila olmaydigan bitta imkoniyat qo'shadi: buyruqning bir nechta nusxasini bir vaqtda ishga tushirish.

find ~/lab/search -type f -name '*.log' -print0 |
    xargs -0 -P4 -I{} gzip -k -- {}
  • find tomonidagi -print0 har bir yo'lni yangi qator emas, NUL bayt bilan tugatadi. Shunda nomida bo'sh joy, tab yoki hatto yangi qator belgisi bor fayl ham bo'linib ketmaydi va noto'g'ri o'qilmaydi.
  • xargs tomonidagi -0 unga kirishdagi ma’lumot NUL bilan ajratilganini bildiradi va -print0 ga mos keladi.
  • -P4 gzip ni birin-ketin emas, bir vaqtda to'rttagacha nusxada ishga tushiradi. Ko'p yadroli mashinada bir-biriga bog'liq bo'lmagan ko'plab fayllarni siqish, checksum hisoblash yoki formatini o'zgartirishda bu foydali, chunki har bir chaqiruv o'zining disk (I/O) va CPU ishini kutib turadi.
  • -I{} har bir yo'lni buyruq shablonidagi {} o'rniga qo'yadi, har bir chaqiruvga bittadan yo'l.

-print0/-0 bo'lmasa, xargs sukut bo'yicha kirishni bo'sh joy va yangi qatorlar bo'yicha bo'ladi va nomida bo'sh joy yoki yangi qator bor faylni jimgina buzib yuboradi: bitta yo'l ikki yoki undan ko'p alohida argument bo'lib qoladi. Bu yuqoridagi read siklidagi muammoning aynan o'zi. "Skript mening test fayllarimda ishlaydi, production’da esa buziladi" degan xatolarning keng tarqalgan sababi ham shu, chunki production’dagi kataloglarda ertami-kechmi hech kim kutmagan nomli fayl paydo bo'ladi.

Intervyuda

Nega -print0/-0 muhim, deb so'rashsa, "bu zamonaviy usul" deb javob berish yetarli emas. To'g'ri javob: Linux’da fayl nomi / va NUL dan boshqa istalgan baytni, jumladan bo'sh joy va yangi qator belgisini o'z ichiga olishi mumkin. Shu sababli sukut bo'yicha bo'sh joy yoki yangi qator bo'yicha bo'ladigan har qanday vosita (xargs, sodda for f in $(find ...) yoki -d '' siz read) shu taxminni buzadigan fayl nomiga duch kelguncha faqat tasodifan xavfsiz ishlaydi.

Amaliy vaziyat: timeout’ning izini topish

Muammo: ilova katalogi ichida qaysi log fayllarida va aynan qaysi qatorlarda timeout so'zi uchrashini aniqlash kerak.

  1. Avval qidiruv doirasini tasdiqlang va undagi fayllarni ro'yxatga oling:

    root=$HOME/lab/search
    test -d "$root/logs" || exit 1
    find "$root/logs" -maxdepth 1 -type f -name '*.log' -printf '%f %s bytes\n'
    
  2. Faqat log fayllarida, aniq satr sifatida qidiring:

    grep -HnF 'timeout' "$root"/logs/*.log
    grep_status=$?
    printf 'grep status=%s\n' "$grep_status"
    
  3. Kattaroq katalog daraxtida glob’ga tayanish o'rniga rekursiyani cheklang:

    grep -rHnF --include='*.log' --exclude-dir=archive 'timeout' "$root"
    
  4. Moslik atrofida nima bo'lganini ko'rish kerak bo'lsa, kontekst qo'shing:

    grep -HnC 1 -F 'timeout' "$root"/logs/app.log
    

Natijada fayl yo'li, qator raqami va hodisaning o'zi ko'rinadi. Production log'ini boshqalarga yuborishdan oldin undagi tokenlar, cookie’lar, email manzillar va boshqa maxfiy ma’lumotlarni yashiring.

Keng tarqalgan xatolar

  • find va grep ni bir-birining o'rnini bosadi deb hisoblash. Aslida biri nom va metama’lumot bo'yicha, ikkinchisi esa mazmun bo'yicha qidiradi.
  • -name '*.log' ni qo'shtirnoqsiz qoldirib, shell uni find ishga tushishidan oldin kengaytirib yuborishiga yo'l qo'yish.
  • -mtime +1 ni o'zgaruvchan 24 soatlik davr emas, kalendar ma’nosidagi "kechadan oldin" deb tushunish.
  • locate fayl tizimining hozirgi, real vaqtdagi holatini ko'rsatadi deb o'ylash.
  • grep -c mos kelgan qatorlar sonini emas, matnning har bir uchrashini sanaydi deb o'ylash.
  • Foydalanuvchi kiritgan matnni avval aniq satr sifatida ko'rib chiqmasdan, to'g'ridan-to'g'ri regex’ga berish.
  • grep ning 1 chiqish holatini ish xatosi, 2 ni esa "moslik topilmadi" deb tushunish.
  • Ruxsat xatolarini /dev/null ga tashlab, qidiruv to'liq bajarildi degan xulosaga kelish.
  • Nomida yangi qator bo'lishi mumkin bo'lgan fayllarni qatorlarga asoslangan pipeline bilan qayta ishlash.
  • Zarurat bo'lmasa ham, odat bo'yicha root sifatida keng find / qidiruvini ishga tushirish.

Mashqlar

  1. Laboratoriya katalogida find yordamida faqat oddiy .conf fayllarni toping va yozgan ifodangizning har bir qismini tushuntiring.
  2. Ikkita qo'shimcha laboratoriya fayli yarating: biri 1 KiB dan katta, ikkinchisi oxirgi besh daqiqada o'zgartirilgan bo'lsin. Har bir shartni alohida ajratib oladigan find ifodasini yozing.
  3. Mos ravishda grep -F, oddiy grep va grep -E ga mos keladigan uchta andoza yozing va har bir andoza uchun nega aynan shu rejim to'g'ri tanlov ekanini qayd eting.
  4. Laboratoriya katalogi ichida grep ning 0, 1 va 2 chiqish holatlarini xavfsiz tarzda hosil qiling.
  5. Nomida bo'sh joy va yangi qator belgisi bor ikkita fayl yarating, so'ng ikkalasini find -print0 sikli bilan to'g'ri ro'yxatlang.

Mashqni bajardim deyish uchun: "moslik topilmadi", "qidiruvning o'zi xato bilan tugadi" va "indeks hali yangilanmagan" holatlari orasidagi farqni dalillar bilan tushuntirib bera olishingiz kerak.

Manbalar

  • GNU Findutils qo'llanmasi: https://www.gnu.org/software/findutils/manual/html_node/find_html/index.html
  • GNU Findutils: bir nechta fayl ustida buyruq bajarish: https://www.gnu.org/software/findutils/manual/html_node/find_html/Multiple-Files.html
  • GNU Grep qo'llanmasi: https://www.gnu.org/software/grep/manual/grep.html
  • GNU Grep: Exit Status: https://www.gnu.org/software/grep/manual/html_node/Exit-Status.html
  • Ubuntu qo'llanma sahifasi: plocate(1): https://manpages.ubuntu.com/manpages/noble/man1/plocate.1.html
  • Ubuntu qo'llanma sahifasi: updatedb(8): https://manpages.ubuntu.com/manpages/noble/man8/updatedb.plocate.8.html