Pipe va yo'naltirish — |, >, >>, 2>, stdin/stdout/stderr

~15 daq · Linux · Matn bilan ishlash

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, keyin sort bo'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'qoladi
  • 2>&1 — "stderr ni stdout ketayotgan joyga yubor"

[!INFO] Tartib muhim: > fayl 2>&1 — ikkalasi faylga. 2>&1 > fayl — stderr ekranga, stdout faylga (chunki 2>&1 paytida 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
  1. cat — faylni chiqaradi
  2. grep "POST" — faqat POST so'rovlar qatorlarini o'tkazadi
  3. wc -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'rniga grep ... fayl yozish mumkin (bitta jarayon kam). Lekin zanjirni o'qish va qurish uchun cat bilan 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'qish
  • 2> — xatolar; 2>/dev/null — xatolarni yashirish; > f 2>&1 — hammasi bitta faylga
  • | — stdout → keyingi buyruqning stdin
  • tee — 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