Oqimlar, pipe va yo'naltirish
Interaktiv terminalda to'g'ri javob chiqaradigan filtr skript ichida buzilgan pipeline’ni yashirib qo'yishi mumkin. Zanjirning oldingi bosqichi xato bilan tugasa ham, oxirgi buyruq muvaffaqiyatli tugab, hammasi joyidadek ko'rinishi mumkin. Maqsad faylini qachon tozalab yozish kerak, standart xato qayerga ketishi kerak, har bir bosqichning holatini qanday tekshirish kerak va natijani ekranda ko'rsatib turib, bir vaqtda faylga qanday saqlash mumkin — bular alohida-alohida savollar. Ularga noto'g'ri javob berish "kecha skript yaxshi ishlagandek edi" degan hodisalarga olib keladi.
Bu maqola Matnli fayllarni ko'rish maqolasidagi standart oqimlar asoslariga va Fayl va matn qidirish maqolasidagi qidirish usullariga tayanadi. Misollar Ubuntu Server LTS’dagi Bash uchun yozilgan; ko'chma POSIX shell boshqacha ishlaydigan joylar alohida ko'rsatiladi. Quyida pipeline bosqichlari nega bir vaqtda ishlashini, oddiy pipeline’ning chiqish holati pipefail ostidagisidan qanday farq qilishini, har bir bosqich holatini PIPESTATUS dan qanday o'qishni, tee natijani ekranga chiqarib turib faylga qanday yozishini va hisobot faylini to'g'ridan-to'g'ri ustidan yozish o'rniga vaqtinchalik fayl orqali qanday almashtirishni ko'rib chiqamiz.
Uchta standart oqim va bitta bayt quvuri
Har bir jarayon uchta ochiq fayl deskriptori bilan boshlanadi: 0 (standart kirish, stdin), 1 (standart chiqish, stdout) va 2 (standart xato, stderr). Sukut bo'yicha kirish klaviaturadan keladi, ikkala chiqish oqimi esa terminalga ketadi. Linux terminalni, pipe’ni va oddiy faylni bir xil fayl deskriptori interfeysi orqali taqdim etadi. Aynan shu tufayli bir jarayonning chiqishini boshqa jarayonning kirishiga yo'naltirish mumkin bo'ladi.
Laboratoriya ma’lumotlari:
mkdir -p ~/lab/pipelines
cd ~/lab/pipelines
printf '%s\n' \
'2026-07-29T10:00:00Z level=INFO service=orders-api' \
'2026-07-29T10:00:02Z level=ERROR service=orders-api' \
'2026-07-29T10:00:03Z level=ERROR service=billing-db' \
'2026-07-29T10:00:04Z level=WARN service=orders-api' > app.log
Pipeline:
Shell barcha bosqichlarni bir-biriga ulaydi va ular odatda bir vaqtda ishlaydi. Pipe baytlar oqimini kernel buferi orqali uzatadi. U yozuvlar, JSON tuzilmasi yoki qatorning ma’nosi haqida hech narsa bilmaydi. Zanjirdagi har bir dastur chegaralar va kodlashni (encoding) o'zi talqin qilishi kerak.
cut -d' ' -f3 bitta bo'sh joyni ajratuvchi deb hisoblaydi. Agar logda bo'sh joylar soni har xil bo'lsa yoki maydonlar qo'shtirnoq ichida bo'lsa, bu tahlil usuli buziladi. Tuzilmali JSON loglar uchun jq dan foydalaning. Maydonlari qat’iy belgilangan barqaror format uchun esa odatda awk qulayroq; bu haqda Matnni qayta yozish: sed va awk maqolasida gaplashamiz.
Standart xato qayerga ketadi: 2>, 2>&1 va tartib
Standart chiqish va standart xato alohida deskriptorlar bo'lgani uchun ular alohida yo'naltiriladi. > (1> ning qisqa shakli) stdout’ni, 2> esa stderr’ni yo'naltiradi:
Topilgan qatorlar found.txt ga, missing.log haqidagi "No such file or directory" xabari esa errors.txt ga tushadi. Ikkala oqimni bitta joyga yuborishda tartib tuzoqqa aylanadi:
2>&1 "2-deskriptor ayni paytda 1-deskriptor qayerga yo'naltirilgan bo'lsa, o'sha yerga yo'naltirilsin" degan ma’noni bildiradi. Yo'naltirishlar chapdan o'ngga bajariladi, shuning uchun > combined.txt birinchi turishi kerak. Shunda 2>&1 bajarilayotgan paytda stdout allaqachon faylga yo'naltirilgan bo'ladi va stderr ham xuddi shu manzilga ulanadi. Ikkalasining o'rnini almashtirsangiz, natija o'zgaradi:
Bu yerda 2>&1 terminalni nusxalaydi (o'sha paytda stdout terminalga yo'naltirilgan edi), keyingi > combined.txt esa faqat stdout’ni qayta yo'naltiradi. Natijada stderr ekranga chiqishda davom etadi, stdout esa faylga ketadi. Intervyuda tez-tez beriladigan bu savolda birini yodlab aytish emas, ikki shakl nega farq qilishini tushuntirib bera olish muhim.
Bash’da "ikkala oqim ham shu faylga" degan ma’noda &> file qisqa shakli ham bor (> file 2>&1 bilan bir xil). /dev/null esa kernel taqdim etadigan "axlat qutisi": 2>/dev/null xatolarni tashlab yuboradi. Bu bir vaqtning o'zida qulay ham, xavfli ham, chunki ovozi o'chirilgan xato haqiqiy nosozlikni yashiradi.
Note
Pipeline’da | keyingi bosqichga faqat stdout’ni ulaydi. Agar stderr’ni yo'naltirmasangiz, u to'g'ridan-to'g'ri terminalga chiqaveradi. Ikkala oqimni ham pipe’ga uzatish uchun cmd 2>&1 | next yoki Bash’dagi qisqa shakl cmd |& next dan foydalaning.
Pipeline chiqish holatidagi yashirin tuzoq
Oddiy Bash’da pipeline’ning chiqish holati odatda uning oxirgi buyrug'ining chiqish holatiga teng bo'ladi:
grep hech narsa topmagani uchun 1 qaytaradi, lekin sort 0 qaytaradi, chunki bo'sh kirishni saralash muvaffaqiyatli amal hisoblanadi. Natijada butun pipeline 0 ni ko'rsatadi. Bu har doim ham xato emas: qidiruvda "moslik topilmadi" mutlaqo kutilgan natija bo'lishi mumkin.
Bash’dagi pipefail opsiyasi buni o'zgartiradi: pipeline holati noldan farqli holat qaytargan eng o'ngdagi bosqichning holatiga teng bo'ladi, barcha bosqichlar muvaffaqiyatli bo'lsa esa 0 bo'ladi:
Subshell bu opsiya interaktiv sessiyangizga o'tib ketishining oldini oladi. pipefail Bash, ksh va ba’zi boshqa shell’larda bor, lekin majburiy POSIX sh standartining qismi emas. Unga tayanishdan oldin skriptingizning shebang qatori aslida qaysi shell’ni ko'rsatishini aniqlang.
Har bir bosqich alohida: PIPESTATUS
Pipeline tugashi bilan Bash har bir bosqichning chiqish holatini PIPESTATUS massiviga yozib qo'yadi:
grep -F 'does-not-exist' app.log | sort | wc -l
statuses=( "${PIPESTATUS[@]}" )
printf 'grep=%s sort=%s wc=%s\n' "${statuses[0]}" "${statuses[1]}" "${statuses[2]}"
Taxminiy natija:
PIPESTATUS ni darhol nusxalab oling, chunki keyingi oddiy buyruq uni qayta yozib yuboradi. Yuqoridagi birinchi 0 holat emas, wc -l sanagan qatorlar soni.
Note
grep uchun 1 "moslik topilmadi", 2 esa "xatolik yuz berdi" degani. pipefail noldan farqli qiymatni ko'rsatadi, lekin bu qiymat sizning ish jarayoningiz uchun nimani anglatishini aytmaydi. Har bir buyruq chiqish holatining hujjatlarda yozilgan ma’nosini o'qing.
set -e pipeline’ni tekshirishning o'rnini bosmaydi
set -e (errexit) qoidalari kontekstga qattiq bog'liqligi bilan tanilgan: u if, while, &&, ||, pipeline’lar va buyruq almashtirishi ichida turlicha ishlaydi. Uni "har qanday xatoda albatta to'xtaydi" deb hisoblamang.
Muhim qadamni alohida tekshirish aniqroq. Shuni yodda tutingki, ! command dan keyingi $? buyruqning o'z holatini emas, mantiqan teskari aylantirilgan holatni ko'rsatadi. Shuning uchun haqiqiy qiymatni buyruqdan keyin darhol saqlab oling:
grep -F 'level=ERROR' app.log > errors.log
grep_status=$?
case $grep_status in
0) echo "matching lines saved" ;;
1) : > errors.log; echo "no matching lines" ;;
*) printf 'grep failed: status=%s\n' "$grep_status" >&2; exit "$grep_status" ;;
esac
Bu yerda : > errors.log bo'sh natijani ataylab yozib qo'yadi. Haqiqiy production hisobotida esa, maqolaning keyingi qismida ko'rsatilganidek, avval vaqtinchalik faylga yozish xavfsizroq.
tee: natijani ko'rish va saqlash
tee o'zining standart kirishini bir vaqtning o'zida standart chiqishga va bir yoki bir nechta faylga nusxalaydi. Terminalni kuzatib turgan operator natijani ko'raveradi, pipeline’ning keyingi bosqichi esa undan foydalana oladi:
Fayl mazmunini o'chirmasdan, oxiriga qo'shib yozish:
Sukut bo'yicha tee ham mavjud maqsad faylining mazmunini o'chirib yozadi. sudo command > /root/file ruxsat xatosi bilan tugaydi, chunki bu yo'naltirishni sudo emas, oddiy shell ochadi. Odatda buni quyidagicha aylanib o'tishadi:
Warning
Bu shakl root’ga tegishli faylni to'g'ridan-to'g'ri ustidan yozib yuborishi mumkin. Uni production’ga tayyor buyruq sifatida ko'chirib qo'ymang. Avval ls -l ni bajaring, zaxira nusxa oling, vaqtinchalik faylga yozing, ilovaning o'z konfiguratsiya tekshiruvchisini ishga tushiring va qanday qilib avvalgi holatga qaytishni (rollback) biling. Maxfiy ma’lumot tee ning o'z stdout’i, terminal tarixi (scrollback) yoki shell tarixi orqali ham oshkor bo'lib qolishi mumkin.
Guruhlash va yo'naltirish doirasi
Bir nechta buyruq natijasini bitta faylga yozish:
{
printf 'host=%s\n' "$(hostname)"
printf 'kernel=%s\n' "$(uname -r)"
date -u +'generated=%FT%TZ'
} > system-report.txt
{ ...; } buyruqlarni joriy shell ichida guruhlaydi; yopuvchi } dan oldin ; yoki yangi qator bo'lishi kerak. Yo'naltirish har bir qator uchun emas, bir marta qo'llanadi.
Subshell:
(
cd /etc || exit
printf 'cwd=%s\n' "$PWD"
find . -maxdepth 1 -type f -printf '%f\n' | sort
) > etc-report.txt
printf 'outer cwd=%s\n' "$PWD"
(...) alohida subshell’da ishlaydi, shuning uchun uning ichidagi cd ota shell’ning joriy katalogini hech qachon o'zgartirmaydi.
Pipeline va o'zgaruvchilarning ta’sir doirasi
Bash’da pipeline bosqichlari odatda subshell’larda ishlaydi:
Bu yerda count ko'pincha 0 bo'lib qoladi, chunki sikl bola muhitdagi o'z nusxasini yangilagan. Siklni joriy shell’da qoldirish uchun unga pipe orqali emas, fayldan kirish yo'naltiring:
Fayl emas, buyruq natijasi kerak bo'lsa, Bash’dagi process substitution subshell muammosini chetlab o'tadi:
count=0
while IFS= read -r line; do
((count++))
done < <(grep -F 'level=ERROR' app.log)
printf 'ERROR lines=%s\n' "$count"
<(...) faylga o'xshash yo'l beradi va POSIX sh ga ko'chma emas. Bu shaklda ichki grep ning chiqish holatini tekshirish noqulay. Ishonchli tekshirilishi kerak bo'lgan har qanday narsa uchun alohida tekshiruv yoki vaqtinchalik fayldan foydalaning.
Here-document va here-string
Ko'p qatorli standart kirish:
Ajratuvchi so'z qo'shtirnoqqa olingani uchun matn ichidagi $variable, $(command) va teskari chiziq kengaytmalari bajarilmaydi. Bu konfiguratsiya shablonini kutilmagan buyruq almashtirishidan himoya qiladi.
Bash’dagi here-string:
<<< faqat Bash’da bor va matn oxiriga yangi qator belgisini qo'shadi. Ko'p hajmli ma’lumot uchun hammasini o'zgaruvchida saqlash o'rniga fayl yoki pipe’dan foydalaning.
SIGPIPE va erta tugaydigan iste’molchi
head beshta qatorni o'qigach yopiladi. Shundan keyin chap tomondagi jarayon o'quvchisi qolmagan pipe’ga yozsa, u SIGPIPE signalini olishi yoki "Broken pipe" xatosini chiqarishi mumkin. pipefail yoqilgan bo'lsa, foydalanuvchi aynan kerakli beshta qatorni olgan bo'lsa ham, bu pipeline noldan farqli holat qaytarishi mumkin.
Boshqacha aytganda, pipefail bu holatni ko'rinadigan qiladi, lekin har bir noldan farqli natija hodisa (incident) ekanini o'zi hal qilib bermaydi. Iste’molchining erta tugashi kutilganmi va bunday holatda ma’lumot ishlab chiqaruvchi jarayonning chiqish holati qanday bo'lishini sinab ko'ring. Faqat birinchi natija kerak bo'lsa, GNU find ... -print -quit ortiqcha ishni umuman qilmaydi:
Hisobotni deyarli atomar tarzda tuzish
command > report.txt faylni ochgan zahoti oldingi hisobotni o'chirib yuboradi. Agar buyruq yarim yo'lda xato bilan tugasa, hisobotni o'qiydigan dastur yarim yozilgan faylni olib, uni to'liq deb hisoblashi mumkin. Avval xuddi shu fayl tizimidagi vaqtinchalik faylga yozish, uni tekshirish va shundan keyingina asl joyiga qayta nomlash xavfsizroq:
report=$HOME/lab/pipelines/error-summary.txt
tmp=$(mktemp "$HOME/lab/pipelines/.error-summary.XXXXXX") || exit 1
if grep -F 'level=ERROR' app.log |
cut -d' ' -f3 |
sort |
uniq -c > "$tmp"; then
test -s "$tmp" || echo "report is empty" >&2
mv -f -- "$tmp" "$report"
else
status=$?
rm -f -- "$tmp"
printf 'report was not generated, status=%s\n' "$status" >&2
exit "$status"
fi
cat "$report"
Bu misolda pipefail yoqilmagan. Agar grep yoki oraliq bosqichdagi xatoni ham ushlash kerak bo'lsa, set -o pipefail ni nazorat ostidagi Bash subshell ichida yoqing. Bitta fayl tizimi ichida mv odatda atomar qayta nomlash bo'ladi. Lekin ma’lumotning diskda saqlanib qolishi (durability), ruxsatlar va boshqa jarayonlarning qulflash bo'yicha kutishlari — alohida masala.
Rollback — hisobotning oldindan saqlab qo'yilgan nusxasini tiklash. Muhim bo'lgan har qanday fayl uchun tartib:
- mavjud faylning metama’lumotlari va mazmunini ko'rib chiqing;
cp -abilan vaqt tamg'ali zaxira nusxa oling;- vaqtinchalik faylga yozing;
- ilovaning o'z tekshiruvchisini ishga tushiring;
- faylni xuddi shu fayl tizimi ichida almashtiring;
- xizmatni qayta yuklang (reload) va health check’ni bajaring;
- biror qadam muvaffaqiyatsiz bo'lsa, zaxira nusxani tiklang.
Amaliy vaziyat: tekshirib bo'ladigan log xulosasi
Muammo: ERROR hodisalarini xizmatlar bo'yicha sanash, natijani ekranda ko'rsatish va faylga saqlash kerak.
log=$HOME/lab/pipelines/app.log
report=$HOME/lab/pipelines/service-errors.txt
test -r "$log" || {
echo "cannot read log: $log" >&2
exit 1
}
(
set -o pipefail
grep -F 'level=ERROR' "$log" |
cut -d' ' -f3 |
sort |
uniq -c |
tee "$report"
)
status=$?
printf 'pipeline status=%s\n' "$status"
Tekshirish:
test -s "$report" && echo "report is not empty"
awk 'NF != 2 { bad=1 } END { exit bad }' "$report"
printf 'format status=%s\n' "$?"
Agar haqiqiy log formati oddiy, bo'sh joy bilan ajratilgan matn bo'lmasa, tahlil usulini shunga moslab almashtiring. Hisobotni boshqalarga yuborishdan oldin unda maxfiy qiymat qolmaganini tekshiring.
Keng tarqalgan xatolar
- Pipeline’ning chiqish holati barcha bosqichlar muvaffaqiyatli bo'lganini ko'rsatadi deb o'ylash.
pipefailni yoqib,grepning1holati yoki kutilganSIGPIPEaslida nimani anglatishini tahlil qilmaslik.PIPESTATUSni uni qayta yozib yuboradigan boshqa buyruqdan keyin o'qish.set -eni holatni alohida tekshirishning to'liq o'rnini bosadi deb hisoblash.teesukut bo'yicha mavjud fayl mazmunini o'chirib yozishini unutish.- Pipe orqali ishlaydigan
whilesikli ichida o'zgargan o'zgaruvchi ota shell’da ham saqlanib qoladi deb kutish. - Here-document ajratuvchisini qo'shtirnoqsiz qoldirib, keraksiz kengaytmaga yo'l qo'yish.
- Muhim hisobotni to'g'ridan-to'g'ri yakuniy manziliga yozish.
- Tahlil usuli ajratuvchi deb hisoblagan belgi log formatiga haqiqatan mos kelishini hech qachon tekshirmaslik.
Mashqlar
- Uch bosqichli pipeline tuzing, o'rtadagi bosqichni ataylab xato bilan tugating va sukut bo'yicha holatni
pipefailostidagi holat bilan solishtiring. - Pipeline tugashi bilan darhol
PIPESTATUSyordamida uchala bosqich holatini massivga saqlang. teeyordamida natijani ekranda ko'rsatib, bir vaqtda faylga saqlang, so'ng ikkinchi ishga tushirishda-aqanday farq qilishini tekshiring.- Pipe orqali ishlaydigan
while readsikli va kirish yo'naltirilgan sikl oxirida hisoblagich o'zgaruvchisining qiymatini solishtiring. - Laboratoriya hisobotini
mktemp→ tekshirish →mvtartibida yangilang, so'ng hisobot tuzish qadamini ataylab buzing va eski fayl saqlanib qolganini tasdiqlang.
Mashqni bajardim deyish uchun: oddiy natijani, har bir bosqichning chiqish holatini, maqsad faylning butunligini va rollback yo'lini birgalikda ko'rsatib bera olishingiz kerak.
Manbalar
- GNU Bash Reference Manual: Pipelines: https://www.gnu.org/software/bash/manual/html_node/Pipelines.html
- GNU Bash Reference Manual: The Set Builtin: https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html
- GNU Bash Reference Manual: Bash Variables (
PIPESTATUS): https://www.gnu.org/software/bash/manual/html_node/Bash-Variables.html - GNU Bash Reference Manual: Process Substitution: https://www.gnu.org/software/bash/manual/html_node/Process-Substitution.html
- GNU Bash Reference Manual: Here Documents: https://www.gnu.org/software/bash/manual/html_node/Redirections.html#Here-Documents
- GNU Coreutils qo'llanmasi:
teeinvocation: https://www.gnu.org/software/coreutils/manual/html_node/tee-invocation.html - POSIX: Pipes: https://pubs.opengroup.org/onlinepubs/9799919799/functions/pipe.html