Pipe va yo'naltirish — |, >, >>, 2>, stdin/stdout/stderr
Muammodan boshlaylik
Savol: "Nginx log faylida eng ko'p so'rov yuborgan 5 ta IP manzil qaysilar?"
Hech bir buyruq buni yolg'iz qila olmaydi. Lekin bir nechta oddiy buyruqni zanjirga ulasangiz:
cut -d' ' -f1 access.log | sort | uniq -c | sort -rn | head -5
1523 185.220.101.4
847 91.213.8.92
312 213.230.112.5
201 84.54.70.18
156 95.46.98.12
Bitta qator — va javob tayyor. Bu Linux ning eng kuchli g'oyasi: pipe (|). Uni tushunish uchun avval har bir dastur qanday "gaplashishini" ko'raylik.
Uchta oqim
Har bir dastur ishga tushganda unga uchta "kanal" beriladi:
┌──────────────┐
stdin (0) ──→│ │──→ stdout (1) natija
kirish │ dastur │
│ │──→ stderr (2) xatolar
└──────────────┘
| Oqim | Raqam | Standart holatda |
|---|---|---|
| stdin | 0 | Klaviatura |
| stdout | 1 | Ekran |
| stderr | 2 | Ekran |
stdout va stderr ikkalasi ekranga chiqadi — shuning uchun farqi ko'rinmaydi. Lekin ular alohida kanallar — va ularni alohida yo'naltirish mumkin.
> — faylga yozish
ls -la > royxat.txt
echo "Salom" > salom.txt
date > vaqt.txt
Natija ekranga emas, faylga yoziladi.
[!WARNING]
>faylni ustidan yozadi — eski mazmun so'roqsiz yo'qoladi.sort fayl.txt > fayl.txt— faylni bo'shatadi (shell avval faylni tozalaydi, keyinsortbo'sh faylni o'qiydi).
>> — oxiriga qo'shish
echo "Birinchi qator" > log.txt
echo "Ikkinchi qator" >> log.txt
echo "$(date) — deploy tugadi" >> deploy.log
>> — mavjud mazmun saqlanadi, yangisi oxiriga qo'shiladi. Loglar uchun ideal.
< — fayldan o'qish
wc -l < access.log
mysql dokon < backup.sql
Fayl dasturning stdin iga beriladi — xuddi klaviaturadan yozilgandek.
stderr ni yo'naltirish
find / -name "nginx.conf" > natija.txt
Ekranda baribir yuzlab "Permission denied" — chunki ular stderr da, > esa faqat stdout ni yo'naltiradi.
find / -name "nginx.conf" 2> xatolar.txt # xatolar faylga
find / -name "nginx.conf" 2>/dev/null # xatolarni tashlab yuborish
find / -name "nginx.conf" > natija.txt 2>&1 # ikkalasi bitta faylga
find / -name "nginx.conf" &> hammasi.txt # xuddi shu (bash qisqartmasi)
2>— stderr ni yo'naltirish/dev/null— "qora tuynuk": unga yozilgan hamma narsa yo'qoladi2>&1— "stderr ni stdout ketayotgan joyga yubor"
[!INFO] Tartib muhim:
> fayl 2>&1— ikkalasi faylga.2>&1 > fayl— stderr ekranga, stdout faylga (chunki2>&1paytida stdout hali ekranga qarab turgan edi).
Cron va skriptlarda tipik:
/opt/backup.sh >> /var/log/backup.log 2>&1
| — pipe
Pipe bir dasturning stdout ini keyingisining stdin iga ulaydi:
ls -la | less
ls natijasi ekranga emas, to'g'ridan-to'g'ri less ga boradi.
ls ──stdout──→ | ──stdin──→ less ──→ ekran
Zanjir istalgancha uzun bo'lishi mumkin:
cat access.log | grep "POST" | wc -l
cat— faylni chiqaradigrep "POST"— faqat POST so'rovlar qatorlarini o'tkazadiwc -l— qatorlarni sanaydi
Natija — bitta son: nechta POST so'rov bo'lgan.
Tipik zanjirlar
history | grep docker # docker buyruqlari tarixdan
ps aux | grep node # node jarayonlari
ls -l | grep "^d" # faqat papkalar
du -sh * | sort -h # papkalar hajm bo'yicha
cat /etc/passwd | cut -d: -f1 | sort # foydalanuvchilar ro'yxati
[!TIP]
cat fayl | grep ...o'rnigagrep ... faylyozish mumkin (bitta jarayon kam). Lekin zanjirni o'qish va qurish uchuncatbilan boshlash ham normal — tushunarlilik muhimroq.
Pipe faqat stdout ni o'tkazadi
find / -name "*.conf" | wc -l # xatolar ekranda qoladi
find / -name "*.conf" 2>/dev/null | wc -l # toza
command 2>&1 | grep ERROR # stderr ni ham pipe ga
tee — ham ekranga, ham faylga
npm run build | tee build.log
┌──→ ekran
npm ──→ tee ─┤
└──→ build.log
Natijani ko'rib turasiz va u saqlanadi. -a — oxiriga qo'shish:
./deploy.sh 2>&1 | tee -a deploy.log
sudo va yo'naltirish muammosi
sudo echo "127.0.0.1 dokon.local" >> /etc/hosts
bash: /etc/hosts: Permission denied
Nega? sudo faqat echo ga beriladi. >> ni esa sizning shell ingiz (oddiy foydalanuvchi) bajaradi. Yechim — tee:
echo "127.0.0.1 dokon.local" | sudo tee -a /etc/hosts
Bu juda ko'p uchraydigan holat — eslab qoling.
Here document — ko'p qatorli matn
cat > config.env << EOF
PORT=3000
DB_HOST=localhost
NODE_ENV=production
EOF
<< EOF ... EOF orasidagi hamma narsa stdin ga beriladi. Skriptlarda fayllar yaratish uchun juda qulay (37-dars).
$( ) — buyruq natijasini qo'yish
echo "Bugun: $(date +%F)"
mkdir zaxira-$(date +%F)
echo "Fayllar soni: $(ls | wc -l)"
$(buyruq) — buyruq bajariladi va natijasi shu joyga qo'yiladi.
Nima uchun shunday?
Nima uchun UNIX dasturlari matn oqimi orqali "gaplashadi"?
Bu 1970-yillardagi UNIX falsafasi (Doug McIlroy): "Bitta ishni yaxshi qiladigan dasturlar yozing. Birga ishlaydigan dasturlar yozing. Matn oqimi bilan ishlaydigan dasturlar yozing — chunki bu universal interfeys." Natijada grep muallifi uning sort yoki nginx bilan ishlatilishini oldindan bilishi shart emas edi — matn bo'lsa, ishlaydi. 50 yildan keyin ham bu g'oya ishlaydi: siz bugun yozgan skript 1979 yilda yozilgan sort bilan muammosiz birlashadi. Kichik vositalar + pipe = cheksiz kombinatsiyalar.
Xulosa
Eslab qoling:
- 3 oqim: stdin (0), stdout (1), stderr (2)
>— faylga (ustidan yozadi!);>>— oxiriga;<— fayldan o'qish2>— xatolar;2>/dev/null— xatolarni yashirish;> f 2>&1— hammasi bitta faylga|— stdout → keyingi buyruqning stdintee— ham ekran, ham fayl;| sudo tee -a— himoyalangan faylga yozish<< EOF— ko'p qatorli kirish;$(...)— natijani qo'yish
Keyingi dars
14-dars: grep — matnda qidirish, regex asoslari