Tarkibga o'tish

Matnni qayta yozish: sed va awk

Fayl va matn qidirish maqolasida ko'rilgan grep qatorni topadi, lekin uni o'zgartirmaydi. Log formatini qayta tuzish, skript orqali konfiguratsiyadagi qiymatni yangilash yoki hisobotdagi sonlar ustunini qo'shib chiqish kerak bo'lsa, bitta grep yetmaydi. Bu sed va awk ning vazifasi.

Misollar Ubuntu Server LTS’dagi GNU sed va GNU awk (gawk) uchun yozilgan. Sizga Oqimlar, pipe va yo'naltirish maqolasidagi chiqish holati va pipefail tushunchalari tanish bo'lishi kerak. Quyida sed bilan qatorni xavfsiz qayta yozishni, o'zgarishni faylga tegishidan oldin tekshirishni, awk bilan ustunli ma’lumotlarni umumlashtirishni va vazifaga qaysi vosita mosligini aniqlashni ko'rib chiqamiz.

Laboratoriyani tayyorlash

mkdir -p ~/lab/textproc
cd ~/lab/textproc
printf 'listen_port=8080\nenv=staging\ndebug=true\n' > app.conf
printf '%s\n' \
    '2026-07-29 10:00:00 INFO orders-api 12ms' \
    '2026-07-29 10:00:02 ERROR orders-api 340ms' \
    '2026-07-29 10:00:03 ERROR billing-db 890ms' \
    '2026-07-29 10:00:04 WARN orders-api 45ms' > requests.log

sed: oqimni qatorma-qator qayta yozish

sed (stream editor — oqim muharriri) qatorlarni birin-ketin o'qiydi, har biriga buyruq qo'llaydi va natijani standart chiqishga yozadi. Agar buni alohida aytmasangiz, u asl faylga tegmaydi: faylni joyida o'zgartirish bayrog'i bo'lmasa, manba fayl o'zgarishsiz qoladi.

Oddiy almashtirish: s///

sed 's/staging/production/' app.conf
listen_port=8080
env=production
debug=true

Bu natija faqat ekranga chiqdi, app.conf ning o'zi esa o'zgarmadi:

cat app.conf
listen_port=8080
env=staging
debug=true

Sintaksis: s/pattern/replacement/[flags], ya’ni s/andoza/almashtiriladigan_matn/[bayroqlar]. / — ajratuvchi, lekin uning o'rniga istalgan boshqa belgini ishlatish mumkin. Andozaning o'zida / bo'lsa, # qulay:

sed 's#/var/log#/srv/log#' <<< 'path=/var/log/app.log'

Hamma mosliklarni almashtirish: g bayrog'i

Sukut bo'yicha sed har bir qatordagi faqat birinchi moslikni almashtiradi:

sed 's/ERROR/WARN/' requests.log
2026-07-29 10:00:00 INFO orders-api 12ms
2026-07-29 10:00:02 WARN orders-api 340ms
2026-07-29 10:00:03 WARN billing-db 890ms
2026-07-29 10:00:04 WARN orders-api 45ms

Bu faylning har bir qatorida bittadan moslik bor, shuning uchun farq ko'rinmaydi. Qatorda bir nechta moslik bo'lsa, g qo'shmaguningizcha faqat birinchisi o'zgaradi:

sed 's/o/0/' <<< 'foo bar foo'
sed 's/o/0/g' <<< 'foo bar foo'
f0o bar foo
f00 bar f00

Faylni joyida tahrirlash: -i

sed -i.bak 's/env=staging/env=production/' app.conf
cat app.conf
listen_port=8080
env=production
debug=true

GNU sed da -i.bak ikki ishni bajaradi: asl faylni .bak nusxa sifatida saqlaydi, so'ng asl faylni joyida tahrirlaydi. Zaxira qo'shimchasi bo'lmasa (yalang'och -i), asl fayl qaytarib bo'lmaydigan tarzda o'zgaradi.

Danger

Zaxira qo'shimchasisiz sed -i ni ortga qaytarib bo'lmaydi. Production konfiguratsiya fayliga tegishdan oldin yoki zaxira qo'shimchasini bering (-i.bak), yoki avval xuddi shu almashtirishni -i siz ishga tushirib, u faqat kutilgan qatorlarga ta’sir qilishini tasdiqlang. Kutilganidan kengroq andoza siz umuman tegmoqchi bo'lmagan qatorlarni ham jimgina qayta yozib yuborishi mumkin, shuning uchun oldindan grep bilan tekshiring.

O'zgartirishdan oldin tekshirish odati

grep -n 'env=staging' app.conf

Faqat aynan kutilgan qator(lar) chiqqanini tasdiqlagandan keyin:

sed -i.bak 's/env=staging/env=production/' app.conf
diff app.conf.bak app.conf

sed -i ga ko'r-ko'rona ishonishdan ko'ra, diff natijasini ko'rib chiqish xavfsizroq oraliq qadam.

Kerakli qatorlarni tanlash

sed -n '2p' requests.log
sed -n '2,3p' requests.log
sed '1d' requests.log
  • -n sukut bo'yicha chiqishni o'chiradi, shunda faqat p bilan aniq chiqarilgan qatorlar ko'rinadi.
  • 2,3p 2-qatordan 3-qatorgacha bo'lgan oraliqni tanlaydi.
  • 1d birinchi qatorni chiqish oqimidan o'chiradi. Faylning o'ziga tegilmaydi, faqat oqim o'zgaradi.

Qatorni raqami o'rniga regular expression bilan ham tanlash mumkin:

sed -n '/ERROR/p' requests.log

Guruhlar va orqaga havolalar

Qavs ichidagi guruhni keyin \1, \2 va hokazo shaklida qayta ishlatish mumkin:

sed -E 's/^([0-9-]+) ([0-9:]+) ([A-Z]+)/\3 \1T\2/' requests.log
INFO 2026-07-29T10:00:00 orders-api 12ms
ERROR 2026-07-29T10:00:02 orders-api 340ms
ERROR 2026-07-29T10:00:03 billing-db 890ms
WARN 2026-07-29T10:00:04 orders-api 45ms

-E kengaytirilgan regular expression’larni yoqadi: ularda qavslarni qochirish shart emas (\(...\) o'rniga (...)). Bu misol sana, vaqt va daraja maydonlarining o'rnini almashtirib, ISO 8601 ga yaqin ko'rinish hosil qiladi.

Almashtirishdagi & tuzog'i

s/// ning almashtiriladigan qismida qochirilmagan & oddiy ampersand belgisini emas, mos kelgan butun matnni bildiradi:

sed 's/orders-api/[&]/' <<< 'service=orders-api'
service=[orders-api]

& butun moslikka almashib, uni kvadrat qavslar ichiga oldi. Oddiy ampersand qo'yish uchun uni \& deb qochiring. Xuddi shunday ehtiyotkorlik \1, ajratuvchi va teskari chiziqlarga ham tegishli: qochirilmagan yoki ishonchsiz ma’lumotdan yig'ilgan almashtirish matni siz kutmagan tarzda kengayishi mumkin. O'zgarish arzimasdek ko'rinsa ham, uni qo'llashdan oldin almashtirishni -i siz ko'rib chiqish kerakligining yana bir sababi shu.

Bir nechta qator bilan ishlash: hold space

Hozirgacha barcha misollar bir vaqtda bitta qator bilan ishladi, sed odatda shunday ishlaydi: qatorni pattern space ga o'qiydi, buyruqlarni qo'llaydi, uni chiqaradi va keyingi qatorga o'tadi. Ba’zi tahrirlar uchun bir vaqtda bir nechta qatorni ko'rish kerak bo'ladi: bo'linib ketgan qatorni birlashtirish, ketma-ket bo'sh qatorlarni bittaga qisqartirish yoki faylni teskari tartibda chiqarish. Buning uchun sed hold space deb ataladigan ikkinchi yordamchi buferni saqlaydi.

  • N yangi sikl boshlash o'rniga keyingi kirish qatorini pattern space’ga yangi qator belgisi orqali qo'shadi.
  • D pattern space’dagi birinchi yangi qator belgisigacha bo'lgan hamma narsani o'chiradi va yangi qator o'qimasdan siklni qayta boshlaydi. U ko'p qatorli pattern space bilan qatorma-qator ishlash uchun ishlatiladi.
  • h / H pattern space’ni hold space’ga nusxalaydi yoki qo'shadi.
  • g / G hold space’ni pattern space’ga qaytarib nusxalaydi yoki qo'shadi.

Ko'p uchraydigan qo'llanish — ketma-ket bo'sh qatorlarni bittaga qisqartirish:

printf 'one\n\n\n\ntwo\n\nthree\n' > blanks.txt
sed '/^$/{ N; /^\n$/D }' blanks.txt
one

two

three

Qadamma-qadam o'qisak: bo'sh qator topilganda N keyingi qatorni ham olib keladi va u ham bo'shligini tekshiradi. Agar ikki qatorli pattern space aynan ikkita bo'sh qatordan iborat bo'lsa (^\n$), D birinchisini tashlab yuboradi va qolgani bilan siklni qayta boshlaydi. Natijada istalgan uzunlikdagi bo'sh qatorlar ketma-ketligi bitta bo'sh qatorga qisqaradi va buning uchun awk’da butun faylni qamrab oladigan yechim yozish shart bo'lmaydi.

Hold space, ayniqsa, keyingi qatorni qayta ishlayotganda oldingi qatordagi ma’lumot asosida qaror qabul qilish kerak bo'lganda muhim. Masalan, oldingi qatorni joriy qator bilan birga chiqarish:

sed -n '1!G;h;$p' requests.log
2026-07-29 10:00:04 WARN orders-api 45ms
2026-07-29 10:00:03 ERROR billing-db 890ms
2026-07-29 10:00:02 ERROR orders-api 340ms
2026-07-29 10:00:00 INFO orders-api 12ms

Chapdan o'ngga o'qisak: 1!G "birinchisidan boshqa har bir qatorda hold space’ni pattern space’ga qo'sh" degani, h keyin (o'sib borayotgan) pattern space’ni hold space’ga qaytarib nusxalaydi, $p esa yig'ilgan yakuniy natijani faqat oxirgi qatorda chiqaradi. Natijada (klassik sed usuli) fayl teskari tartibda chiqadi. Bu andozani aniq tanib olish foydali, chunki u troubleshooting qo'llanmalarida va sed ning ichki tuzilishi haqidagi intervyu savollarida uchraydi. Sababi faylni qo'lda teskari aylantirish tez-tez kerak bo'lishida emas: buni tac to'g'ridan-to'g'ri va tushunarliroq bajaradi.

Intervyuda

"sed bir vaqtda bir nechta qator bilan qanday ishlaydi?" deb so'rashsa, javob yuqoridagi pattern space va hold space modeli. sed asosan qatorlar bilan ishlaydigan vosita. Qatorlararo kontekst kerak bo'ladigan kam uchraydigan holatlar uchun u N/D va hold space mexanizmini taklif qiladi. awk esa oddiy o'zgaruvchilar va massivlarda butun kirish bo'yicha holatni tabiiy ravishda to'plab boradi.

awk: ustunli ma’lumotlar bilan ishlash

sed har bir qatorni bir butun sifatida ko'radi. awk esa har bir qatorni bo'sh joy (yoki siz ko'rsatgan boshqa ajratuvchi) bo'yicha maydonlarga bo'ladi. Shu sabab ustunlar ustida hisob-kitob qilish uchun u ancha qulay.

Kerakli maydonlarni chiqarish

awk '{ print $1, $3 }' requests.log
2026-07-29 INFO
2026-07-29 ERROR
2026-07-29 ERROR
2026-07-29 WARN

$1, $2 va hokazo — bo'sh joy bilan ajratilgan maydonlar, $0 esa butun qator. Sukut bo'yicha maydon ajratuvchisi bir yoki bir nechta bo'sh joy yoki tab belgisi.

Shart bo'yicha filtrlash

awk '$3 == "ERROR" { print $1, $2, $4 }' requests.log
2026-07-29 10:00:02 orders-api
2026-07-29 10:00:03 billing-db

grep dan farqli ravishda awk mos kelgan qatorni topish bilan cheklanmaydi: undan faqat sizga kerakli ustunlarni ajratib oladi.

O'z maydon ajratuvchingiz: -F

CSV yoki key=value ko'rinishidagi ma’lumotlar uchun:

printf 'name=orders-api,status=up,latency=12\nname=billing-db,status=down,latency=340\n' > services.csv
awk -F',' '{ print $1 }' services.csv
name=orders-api
name=billing-db

Yig'indi va sanash: BEGIN/END

awk '$3 == "ERROR" { count++ } END { print "ERROR count:", count+0 }' requests.log
ERROR count: 2

Agar birorta ham ERROR qatori uchramasa, count+0 natija bo'sh satr emas, 0 bo'lib chiqishini ta’minlaydi. awk da hech qachon qiymat berilmagan o'zgaruvchi sukut bo'yicha bo'sh/nol qiymatga ega bo'ladi.

BEGIN bloki kirish o'qilishidan oldin bir marta, END bloki esa butun kirish o'qib bo'lingandan keyin bir marta bajariladi:

awk 'BEGIN { print "Analysis started" }
     $3 == "ERROR" { total += $4 + 0 }
     END { print "Total ERROR latency (ms):", total+0 }' requests.log

Bu yerda $4 (masalan, 340ms) avtomatik ravishda son bo'lmaydi. awk satrning boshidagi son qismini o'qiydi va oxiridagi ms ni e’tiborga olmaydi, chunki qiymat faqat arifmetik amalda ishlatilgandagina songa aylantiriladi.

Ustun bo'yicha guruhlash

awk '{ count[$3]++ } END { for (level in count) print level, count[level] }' requests.log
INFO 1
ERROR 2
WARN 1

count[$3] — log darajasi kalit bo'lgan assotsiativ massiv. awk dagi har bir massiv assotsiativ bo'ladi. for (level in count) massiv kalitlari bo'ylab aylanadi; bunda kalitlarning tartibi kafolatlanmaydi.

Natijani printf bilan formatlash

print qulay, lekin ustun kengligi yoki sonlar formatini deyarli boshqarib bo'lmaydi. awk dagi printf C tili va shell’dagi xuddi shu nomli funksiya kabi ishlaydi va odam o'qiydigan hisobot uchun yaxshiroq tanlov:

awk '{ printf "%-12s %-8s %5s\n", $4, $3, $5 }' requests.log
orders-api   INFO      12ms
orders-api   ERROR    340ms
billing-db   ERROR    890ms
orders-api   WARN      45ms

%-12s satrni 12 belgili maydonda chapga, %5s esa 5 belgili maydonda o'ngga tekislaydi. Bu C dagi printf yoki shell’ning ichki buyrug'idagi format belgilarining aynan o'zi. print dan farqli ravishda printf oxiriga yangi qator belgisini o'zi qo'shmaydi, shuning uchun format satri oxiridagi \n shart.

Bir nechta fayl bilan ishlash: FNR va NR

awk bir nechta kirish faylini qabul qiladi. Bu turli manbalardagi ma’lumotlarni birlashtirish yoki solishtirishda muhim:

printf 'name=orders-api,status=up,latency=12\nname=billing-db,status=down,latency=340\n' > services.csv
awk -F',' '{ print FILENAME, FNR, NR }' requests.log services.csv
requests.log 1 1
requests.log 2 2
requests.log 3 3
requests.log 4 4
services.csv 1 5
services.csv 2 6

NR — shu paytgacha o'qilgan barcha fayllar bo'yicha umumiy yozuvlar soni. FNR esa har bir yangi fayl boshida 1 ga qaytadi. FILENAME hozir o'qilayotgan fayl nomini saqlaydi. FNR == 1 sharti "yangi fayl hozirgina boshlandi" degan holatni aniqlashning standart usuli. U har bir fayl uchun sarlavha chiqarish yoki har bir fayldagi sarlavha qatorini alohida tashlab o'tish uchun foydali:

awk 'FNR == 1 { print "== " FILENAME " ==" } { print FNR, $0 }' requests.log services.csv

Bitta faylda yozilib sinalgan skript keyinchalik bir nechta faylga qo'llanganda NR va FNR ni adashtirish ko'p xatolarga sabab bo'ladi. Qator raqamiga bog'liq har qanday mantiq (sarlavhani tashlab o'tish, ma’lum qatorni topish) jimgina buziladi, chunki NR qaytadan boshlanish o'rniga o'sishda davom etadi.

sed yoki awk: qachon qaysi birini tanlash kerak

Vazifa Yaxshiroq vosita Sababi
Bitta qator ichida matnni almashtirish sed Qatorni bir butun deb ko'radi; oddiy almashtirish uchun sodda
Konfiguratsiya faylidagi bitta qiymatni yangilash sed -i (ehtiyotkorlik bilan) Bitta qator, bitta almashtirish — boshqa hech narsa kerak emas
Muayyan ustunlarni ajratib olish yoki ular ustida hisoblash awk Maydonlarga o'zi bo'ladi, arifmetika va massivlarni qo'llab-quvvatlaydi
Bir nechta shart bo'yicha yig'indi yoki sanoq awk BEGIN/END va assotsiativ massivlar aynan shu ish uchun yaratilgan
Qatorni o'zgartirmasdan faqat topish grep Ikkala vosita ham buni qila oladi, lekin grep soddaroq va tezroq

Ikkala vosita ham to'liq ma’noda alohida dasturlash tillari. Bu yerda ko'rsatilgani tizim administratorining kundalik ishida kerak bo'ladigan qismi xolos.

Amaliy vaziyat: sozlamani almashtirish va logni umumlashtirish

Muammo: app.conf da env=staging ni env=production ga o'zgartirish kerak, lekin faqat aynan bitta qator mos kelishini tasdiqlagandan keyin. So'ng o'zgarish yozuvi (change record) uchun requests.log bo'yicha har bir darajadagi hodisalar sonini chiqarish kerak.

  1. Faylga tegishdan oldin almashtiriladigan qatorni tasdiqlang:

    grep -nc 'env=staging' app.conf
    

    Natija 1 bo'lsa, andoza faylni xavfsiz tahrirlash uchun yetarlicha aniq. 0 yoki 2 va undan ko'p bo'lsa, to'xtang va andozani aniqlashtiring. Aks holda sed -i yo hech narsani o'zgartirmaydi, yo kutilganidan ko'p qatorni qayta yozadi va zaxira nusxani o'tkazib yuborgan bo'lsangiz, ikkinchi imkoniyat bo'lmaydi.

  2. Zaxira nusxa bilan tahrirlang, so'ng faqat kutilgan qator o'zgarganini tasdiqlang:

    sed -i.bak 's/^env=staging$/env=production/' app.conf
    diff app.conf.bak app.conf
    

    Andozani ^...$ bilan qotirish env=staging boshqa uzunroq qiymatning bir qismi sifatida uchragan joylarda almashtirish ishlab ketishining oldini oladi.

  3. O'zgarish tiketi uchun logni darajalar bo'yicha umumlashtiring:

    awk '{ count[$3]++ } END { for (level in count) printf "%-6s %d\n", level, count[level] }' requests.log
    
    INFO   1
    ERROR  2
    WARN   1
    

Asosiy g'oya — vazifalarning taqsimlanishi: sed bitta qatorga ko'rib chiqilgan va ortga qaytariladigan bitta o'zgarish kiritdi, awk esa ustun bo'yicha umumiy natijani hisobladi. Konfiguratsiyani tahrirlash uchun awk ni yoki logni sanash uchun sed ni olish ikkala holatda ham noto'g'ri vosita tanlash bo'lardi.

Keng tarqalgan xatolar

  • Production faylga zaxira nusxasiz yoki oldindan sinab ko'rmasdan (dry run) to'g'ridan-to'g'ri sed -i qo'llash.
  • s/// da g bayrog'ini unutib, qatordagi faqat birinchi moslik o'zgarganini sezmay qolish.
  • $3 == "ERROR" kabi awk solishtiruvida satrni qo'shtirnoqsiz yozish — satrlarni solishtirishda qo'shtirnoq muhim.
  • awk maydonlari raqamlanishi faylning haqiqiy ajratuvchisiga mos keladi deb tekshirmasdan qabul qilish (masalan, vergul bilan ajratilgan faylni bo'sh joy bo'yicha bo'linadi deb o'ylash).
  • count yoki total kabi awk yig'uvchisini +0 siz chiqarib, u hech qachon oshirilmaganda bo'sh natija chiqqanidan hayron qolish.
  • sed va grep ni bir xil vosita deb hisoblab, oddiy grep yetarli bo'lgan joyda sed -n '/pattern/p' ni ishlatish.

Mashqlar

  1. sed -i.bak yordamida app.conf dagi debug=true ni debug=false ga o'zgartiring, so'ng o'zgarishni diff bilan tasdiqlang.
  2. sed -n yordamida requests.log dan faqat WARN va ERROR qatorlarini chiqaring, so'ng xuddi shu natijani awk bilan hosil qiling. Ikki yondashuvni solishtiring.
  3. awk yordamida har bir xizmat (orders-api, billing-db) requests.log da necha marta uchrashini sanang.
  4. sed -E yordamida requests.log dagi sana va vaqtni T bilan birlashtirilgan bitta maydonga aylantiring. Yuqoridagi misolga o'xshash, lekin o'zingiz yozgan andoza bilan.

Mashqni bajardim deyish uchun: har bir natija uchun qaysi vositani (sed yoki awk) tanlaganingizni va nima uchun aynan shu vazifaga u yaxshiroq mos kelganini tushuntirib bera olishingiz kerak.

Manbalar

  • GNU sed qo'llanmasi: https://www.gnu.org/software/sed/manual/sed.html
  • GNU sed: faylni joyida tahrirlash: https://www.gnu.org/software/sed/manual/sed.html#sed-Basics
  • GNU Awk User's Guide: https://www.gnu.org/software/gawk/manual/gawk.html
  • POSIX: sed: https://pubs.opengroup.org/onlinepubs/9799919799/utilities/sed.html
  • POSIX: awk: https://pubs.opengroup.org/onlinepubs/9799919799/utilities/awk.html