IlmHamroh
JavaScript Full-stack/24-qism. Node.js50/55-dars18 daqiqa
Mundarija (37)

Node.js CLI anatomiyasi: shebang, bin, parseArgs, stdout/stderr va exit kodlar

Qisqacha: Yaxshi CLI — uchta shartnomani bajaradigan dastur: argument oladi (util.parseArgs), natijani stdout'ga, qolgan hamma narsani stderr'ga yozadi va to'g'ri exit kod qaytaradi (0 — muvaffaqiyat, 1 — ish vaqtidagi xato, 2 — noto'g'ri foydalanish). Uni buyruq sifatida chaqirish uchun faylning birinchi qatori #!/usr/bin/env node (shebang) va package.json da bin maydoni kerak. Shu qoidalar bajarilsa, dastur quvurlarda (| head), skriptlarda va CI'da odam aralashuvisiz ishlaydi.

Bu darsda

  • CLI'ning yo'lini terminaldan main() gacha va qaytib exit kodgacha chizib bera olasiz.
  • Shebang, bin maydoni va npm yaratadigan Windows shim'lari nima qilishini tushuntirasiz.
  • util.parseArgs ning qoidalarini, jumladan manfiy son va -- tuzog'ini bilasiz.
  • stdout va stderr'ni to'g'ri ajratasiz va EPIPE qulashini oldini olasiz.
  • --help, --version, exit kodlar va sozlash ustuvorligi bo'yicha umumiy kelishuvlarni qo'llaysiz.

Oldin bilishingiz kerak: process obyekti: argv, env, exit kodlari, util moduli, readline va terminal, O'z npm paketingizni nashr qilish.

1. Nega bu kerak?

Siz har kuni CLI ishlatasiz: git, npm, node, tsc. Ular bir-biriga o'xshaydi: --help yordam beradi, xato bo'lsa qizil xabar va to'xtash, natijani | bilan boshqa dasturga uzatish mumkin. Bu o'xshashlik tasodif emas — o'nlab yillik kelishuvlar (conventions) natijasi.

valyuta ni qurish davomida bu kelishuvlarni bittalab qo'shdingiz: process darsida exit kodlar, util darsida parseArgs, readline darsida TTY, npm paket darsida bin. Bu dars — ularni bitta rasmga yig'adi. Yangi kod kam, lekin har qismning "nega"si aniq bo'ladi.

O'xshatish: avtomobil. Rul, pedallar, chiroqlar — har mashinada bir joyda. Yangi mashinaga o'tirgan haydovchi qo'llanmasiz yuradi. CLI ham shunday: kelishuvlarga amal qilsangiz, foydalanuvchi valyuta --help ni o'qimasdan ham ko'p narsani taxmin qiladi.

2. CLI'ning yo'li

2.1 Buyruqdan exit kodgacha

valyuta kurs USD -f csv | head -2 yozilganda nima bo'ladi:

flowchart TD
  A["Shell: valyuta kurs USD -f csv"] --> B["PATH'dan valyuta topiladi"]
  B --> C["shebang yoki .cmd shim: node dist/bin.js"]
  C --> D["process.argv"]
  D --> E["parseArgs: bayroqlar va so'zlar"]
  E --> F["main(): ish"]
  F --> G["stdout: natija → head"]
  F --> H["stderr: ogohlantirish → ekran"]
  F --> I["exit kod → shell"]

Nimaga qarang: chiqish uchta kanal — stdout, stderr va exit kod. Ularning har biri boshqa "o'quvchi" uchun: stdout — keyingi dastur yoki fayl, stderr — odam, exit kod — skript va CI.

2.2 valyuta da qayerda nima

Bo'lak Fayl Qaysi darsda
Shebang, SIGINT, process.exitCode src/bin.ts process, ESM
parseArgs, yordam matni, buyruqlar src/cli.ts util
I/O siz mantiq (bajar) src/buyruq.ts readline
bin, files, engines package.json npm paket

Muhim ajratish: bin.ts — yon ta'sirli kirish nuqtasi (jarayonga tegadi), cli.ts dagi main(argv, env) esa faqat son qaytaradi. Shuning uchun testlar main() ni to'g'ridan chaqira oladi va kutubxona sifatida import qilinganda hech narsa ishga tushib ketmaydi.

3. Shebang va bin

3.1 Shebang

Yakuniy dist/bin.js ning birinchi qatori:

text
#!/usr/bin/env node

Shebang (#! — "hash-bang") — Linux va macOS yadrosi uchun ko'rsatma: "bu faylni qaysi dastur bilan ochish kerak". /usr/bin/env node — "PATH'dan node ni top". To'g'ridan /usr/bin/node yozilmaydi: Node har kompyuterda boshqa joyda (nvm, Homebrew, fnm). JavaScript uchun bu qator izoh hisoblanadi — Node uni tashlab ketadi.

Windows yadrosi shebang'ni o'qimaydi. U yerda ishni npm bajaradi (keyingi bo'lim).

3.2 bin maydoni va shim'lar

package.json da:

json
"bin": {
  "valyuta": "dist/bin.js"
}

"Bu paket o'rnatilganda valyuta buyrug'i paydo bo'lsin va dist/bin.js ni ishga tushirsin". O'rnatishda npm node_modules/.bin/ ga uchta fayl yozadi (kanon, Windows): valyuta (bash uchun), valyuta.cmd (cmd uchun), valyuta.ps1 (PowerShell uchun). .cmd ning asosiy qatori:

text
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%"  "%dp0%\..\valyuta\dist\bin.js" %*

Bu yerda %_prog% — node, %* — hamma argumentlar. Ya'ni Windows'da shim "shebang'ning ishini" qo'lda qiladi. Linux'da npm faylni bajariladigan qiladi (chmod +x) va .bin/valyuta ga havola (symlink) yaratadi — hujjat bo'yicha, biz Linux'da sinamadik.

Diqqat: Shebang'ni unutish Windows'da ham sezilmaydigan xato emas. Kanon Workspaces tajribasida shebang'siz cli.ts bilan shunday bo'ldi: bash shim faylni shell skript deb bajardi va import: command not found xatosi chiqdi; npx valyuta esa 2 daqiqadan ko'p osilib qoldi. Saboq: bin faylida shebang shart.

Har o'zgarishdan keyin npm pack va o'rnatish (npm paket darsidagidek) — sekin. npm link loyiha papkasida global havola yaratadi: valyuta buyrug'i to'g'ridan sizning papkangizga ishora qiladi, npm run build dan keyin o'zgarish darhol ko'rinadi. Olib tashlash — npm unlink -g.

Kanonda npm link ishlatilmadi: u global papkaga yozadi, zavod qoidasi esa global o'rnatishni taqiqlaydi. Uning o'rniga npm pack + boshqa papkaga npm i — bu foydalanuvchi aynan nimani olishini ham sinaydi. O'z kompyuteringizda npm link dan bemalol foydalaning. Yana bir yo'l — global hech narsa yozmaydigan node dist/bin.js kurs USD yoki npm run start -- kurs USD (--watch va node --run).

Tekshirib ko'ring: bin fayli dist/bin.ts emas, dist/bin.js. Nega .ts ga qaratilmaydi, axir Node 24 .ts ni ishga tushiradi?

Javob

Node node_modules ichidagi fayllarda type stripping qilmaydi — kanon buni sinagan: ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING (npm paket darsida). Va tezlik: dist/bin.js 103 ms, src/bin.ts 164 ms (TypeScript'ni to'g'ridan ishga tushirish). Paketga build qilingan JS boradi.

4. Argumentlar: parseArgs

4.1 process.argv dan bayroqlargacha

process.argv — oddiy satrlar massivi: [node yo'li, skript yo'li, ...siz yozganlar]. Uni qo'lda tahlil qilish — -f csv, --format=csv, -fcsv hammasini bilish degani. node:util dagi parseArgs buni qiladi. Kanondagi sozlamaga yaqin misol:

js
import { parseArgs } from "node:util";

const options = {
  format: { type: "string", short: "f", default: "jadval" },
  sana: { type: "string", short: "s" },
  offline: { type: "boolean", default: false },
};

function ajrat(args) {
  try {
    const { values, positionals } = parseArgs({
      args, options, allowPositionals: true, strict: true,
    });
    console.log(JSON.stringify({ ...values }), positionals);
  } catch (x) {
    console.log(x.code);
  }
}

ajrat(["kurs", "USD", "EUR", "-f", "csv"]);
ajrat(["kurs", "--sana=2026-10-01", "--offline"]);
ajrat(["-fcsv", "kurs"]);
ajrat(["kurs", "--bayroq"]);
ajrat(["kurs", "--format"]);
ajrat(["kurs", "--offline=ha"]);

Konsolda:

text
{"format":"csv","offline":false} [ 'kurs', 'USD', 'EUR' ]
{"sana":"2026-10-01","offline":true,"format":"jadval"} [ 'kurs' ]
{"format":"csv","offline":false} [ 'kurs' ]
ERR_PARSE_ARGS_UNKNOWN_OPTION
ERR_PARSE_ARGS_INVALID_OPTION_VALUE
ERR_PARSE_ARGS_INVALID_OPTION_VALUE

Qoidalar:

  • -f csv, --format csv, --format=csv, -fcsv — hammasi bir xil.
  • default faqat bayroq berilmaganda. sana da default yo'q — berilmasa, natijada kalit umuman yo'q.
  • strict: true — noma'lum bayroq (--bayroq), qiymatsiz satr bayrog'i (--format) va qiymatli boolean (--offline=ha) xato beradi. Bu yaxshi: --ofline (xato yozilgan) jim o'tib ketmaydi.
  • allowPositionals: true — bayroq bo'lmagan so'zlar (kurs, USD) positionals ga yig'iladi.

parseArgs faqat turni tekshiradi: --sana=ertaga ni ham qabul qiladi. Ma'noni (sana to'g'rimi) dastur o'zi tekshiradi — valyuta da bu sananiTekshir (exit 2).

4.2 Manfiy son va --

Haqiqiy topilma: valyuta -5 USD ishlamaydi. Biz yakuniy holatda sinadik:

text
$ valyuta -5 USD
Xato: Unknown option '-5'. To specify a positional argument starting with a '-', place it at the end of the command after '--', as in '-- "-5"
[exit 2]
$ valyuta -- -5 USD
-5.00 USD = -59 515.75 UZS
[exit 0]

Tarjimasi: "noma'lum bayroq -5". Xabar maslahat ham beradi. Minus bilan boshlanadigan oddiy argumentni -- dan keyin yozing. parseArgs uchun -5 — qisqa bayroq (-f kabi). -- — umumiy kelishuv: "bundan keyin bayroq yo'q, hammasi oddiy so'z". npm run start -- kurs USD dagi -- ham shu ma'noda: "keyingilari npm'niki emas".

Manfiy valyuta miqdori kam kerak bo'ladi, lekin xabar to'g'ri yo'lni o'zi ko'rsatadi — shuning uchun kanon buni qoldirdi.

Tekshirib ko'ring: parseArgs strict: false bilan ishlatilsa, --ofline (bitta f) nima bo'ladi?

Javob

Xato bermaydi: noma'lum bayroq values.ofline = true sifatida qo'shiladi, values.offline esa false qoladi. Dastur jim internetga chiqadi — foydalanuvchi esa oflayn deb o'ylaydi. CLI uchun strict: true va noma'lum bayroqda exit 2 — xavfsizroq.

5. stdout va stderr

5.1 Qoida

stdout (standart chiqish) — dasturning natijasi: jadval, CSV, JSON. stderr (standart xato oqimi) — qolgan hamma narsa: ogohlantirish, xato, jarayon ko'rsatkichi, debug. Sabab — quvur faqat stdout'ni uzatadi. Yakuniy valyuta da (oflayn rejim, biz ishga tushirdik):

text
$ valyuta kurs USD -f csv
Ogohlantirish: offline rejim — saqlangan namuna kurslar (fixtures/) ishlatildi
sana,kod,nomi,nominal,kurs,farq
2026-10-23,USD,AQSH dollari,1,11903.15,-62.36
$ valyuta kurs all -f csv 2>/dev/null | head -3
sana,kod,nomi,nominal,kurs,farq
2026-10-23,USD,AQSH dollari,1,11903.15,-62.36
2026-10-23,EUR,EVRO,1,13329.15,-108.12

Birinchi buyruqda ekranda ikkala oqim aralash ko'rinadi. Ikkinchisida 2>/dev/null stderr'ni tashladi (PowerShell'da 2>$null), head faqat toza CSV oldi. Agar "Ogohlantirish" stdout'ga yozilganida, kurslar.csv fayli birinchi qatori buzilgan holda yaratilardi va Excel uni noto'g'ri ochardi.

5.2 EPIPE: o'quvchi ketib qolsa

Biz kutmagan tuzoq. 2.3 MB CSV ni bitta process.stdout.write bilan yozadigan dasturni | head -1 ga uladik:

js
const qator = "2026-10-23,USD,AQSH dollari,1,11903.15,-62.36\n";
process.stdout.write(qator.repeat(50_000)); // ~2.3 MB
text
2026-10-23,USD,AQSH dollari,1,11903.15,-62.36
node:events:505
    throw er; // Unhandled 'error' event
    ^

Error: EPIPE: broken pipe, write

Uch marta sinadik — har safar exit 1. head bitta qator olib yopildi, Node esa yopiq quvurga yozishda davom etdi. EPIPE (broken pipe — uzilgan quvur) — "o'qiydigan tomon yo'q". Foydalanuvchi uchun bu xato emas: u head bilan atayin kamroq so'radi. Lekin dastur qo'rqinchli stek bilan yiqildi.

Qizig'i: xuddi shu hajmni console.log bilan qatorma-qator yozgan dastur yiqilmadi (exit 0) — console o'z oqim xatolarini e'tiborsiz qoldiradi. process.stdout.write esa yo'q.

Tuzatish — stdout xatosini ushlash:

js
process.stdout.on("error", (xato) => {
  // o'quvchi (head, less) quvurni yopdi — jim chiqamiz
  if (xato.code === "EPIPE") process.exit(0);
  throw xato;
});

Shu bilan uch marta — exit 0, stek yo'q. Bu ishlovchi valyuta kanoniga kirmagan — u faqat mashq. Sababi: uning chiqishi kichik (74 qatorlik kurs all -f csv | head -2 muammosiz o'tdi), katta chiqish faqat tarix buyrug'ida. Uni katta oraliq bilan | head ga ulash — kanonda sinalmagan. Mashqda shu ishlovchini qo'shib ko'rasiz.

Maslahat: Dasturingiz natijasini | head -1 ga ulab sinang. Bu ikki narsani bir zumda tekshiradi: stdout'da ortiqcha matn yo'qligini va EPIPE ga chidamliligini.

6. Exit kodlar

6.1 Kelishuv

Kod Ma'nosi valyuta da misol
0 Muvaffaqiyat valyuta kurs USD
1 Ish vaqtidagi xato tarmoq va kesh yo'q, fayl o'qilmadi
2 Noto'g'ri foydalanish --bayroq, XYZ valyuta, 2026-13-45 sana
130 Ctrl+C (128 + SIGINT raqami 2) uzun tarix paytida to'xtatish

Biz sinagan chiqishlar:

text
$ valyuta -s 2026-13-45 kurs USD
Xato: Sana YYYY-MM-DD ko'rinishida va haqiqiy bo'lishi kerak: 2026-13-45
[exit 2]
$ valyuta kurs USD 2>/dev/null
2026-10-23 holatiga (fixture)
USD    1    11 903.15   -62.36  AQSH dollari
[exit 0]

Nega 1 va 2 ni ajratish kerak? Skript uchun javob boshqacha: 1 — "keyinroq qayta urin" (tarmoq tiklanar), 2 — "qayta urinma, buyruq noto'g'ri". CI'da bu farq "flaky test" va "buzilgan konfiguratsiya" farqi.

6.2 process.exitCode, process.exit() emas

bin.ts oxirida:

ts
const argv = process.argv.slice(2);
process.exitCode = await main(argv, process.env, bekor.signal);

process.exit(kod) — darhol to'xtatish: navbatdagi ish, yozilmagan chiqish, finally bloklari — hammasi tashlanadi. Node hujjati ogohlantiradi: stdout'ga asinxron yozilayotgan ma'lumot yo'qolishi mumkin. Biz Windows'da 2.3 MB ni | wc -c ga yozib, keyin process.exit(0) qildik — uch marta ham 2 300 000 bayt yetib keldi. Bu mashinada yo'qotish kuzatmadik. Sababi hujjatning o'zida: Windows'da quvurga yozish sinxron, Linux va macOS'da esa asinxron (terminalda — aksincha). Demak Linux quvurida xuddi shu kod ma'lumot yo'qotishi mumkin — biz Linux'da sinamadik. process.exitCode = kod esa "tugaganingda shu kod bilan chiq" degani — Node hamma ishni tugatib chiqadi. valyuta da process.exit() faqat ikkinchi Ctrl+C da.

7. Foydalanuvchi bilan kelishuvlar

7.1 --help va --version

  • -h, --help — yordam stdout'ga, exit 0. U so'ralgan natija.
  • -v, --version — faqat versiya raqami, bitta qator (1.0.0). Skriptlar uni o'qiydi, ortiqcha so'z yozmang.
  • Xatoda — qisqa xabar va yordam stderr'ga, exit 2.
  • Argumentsiz chaqiruv — valyuta yordamni chiqaradi va exit 2 beradi: "nima qilishni aytmadingiz".

valyuta --bayroq ning haqiqiy chiqishi — birinchi qatorda Xato: Unknown option '--bayroq'…, keyin bo'sh qator va to'liq yordam matni, exit 2. Xabarning o'zi inglizcha — u parseArgs dan keladi. O'zbekcha qilish uchun x.code bo'yicha o'z matningizni yozish kerak; kanon buni qilmadi — mashq sifatida qoldi.

7.2 Sozlash ustuvorligi

valyuta ni uch joydan sozlash mumkin. Kelishuv — yaqinroq yutadi:

flowchart LR
  A["Bayroq: --offline"] --> B["Muhit: VALYUTA_OFFLINE=1"]
  B --> C["Fayl: config.json"]
  C --> D["Sukut qiymati"]

Chapdagi ustun. Bayroq — shu bitta chaqiruv uchun, muhit o'zgaruvchisi — shu terminal sessiyasi yoki CI uchun, fayl — doimiy odat uchun. valyuta da config.json faqat sukut valyutalar ro'yxatini beradi, VALYUTA_OFFLINE=1 va --offline bir xil ishlaydi.

7.3 Ranglar va TTY

Rang — faqat odam ko'radigan joyda. valyuta jadvaldagi farqni util.styleText bilan bo'yaydi; u o'zi tekshiradi: stdout TTY emas yoki NO_COLOR o'rnatilgan bo'lsa — rangsiz (readline va terminal darsidagi isTTY). CSV va JSON hech qachon rangli emas. --rangsiz bayrog'i — qo'lda o'chirish. Batafsil — Interaktiv CLI darsida.

8. CLI qadami: anatomiya tekshiruvi (holat 52 ga tayyorgarlik)

Bu darsda kod yozilmaydi: kanon anatomiyaning hamma qismini holatlar 08–46 da allaqachon qurgan. Shu yerda ro'yxat bo'yicha o'z valyuta ingizni tekshiring:

Tekshiruv Kutilgan
head -1 dist/bin.js #!/usr/bin/env node
valyuta --version bitta qator, package.json dagi versiya
valyuta --bayroq; echo $? stderr'da xato + yordam, 2
valyuta kurs USD 2>/dev/null faqat jadval
valyuta kurs all -f csv | head -1 sana,kod,nomi,nominal,kurs,farq
valyuta -- -5 USD manfiy miqdor ishlaydi

Holat 52 ning commiti (jarayon ko'rsatkichi) Interaktiv CLI darsida. Hozircha commit qilmang — o'zgarish yo'q.

Raqamlar: holat 52 da sovuq start --help 94 ms, kurs USD 103 ms (tekshiruv-52.txt, bo'sh Node 73 ms). Anatomiya qismlarining hammasi birga ~20–30 ms "qo'shadi" — bu yaxshi CLI'ning narxi.

9. Ko'p uchraydigan xatolar

9.1 Natijani console.error ga, xatoni console.log ga

Teskari ajratish — eng ko'p uchraydigan xato. Belgisi: > fayl.csv da xabar qatorlari bor, yoki xato skriptga "natija" bo'lib ketadi. Qoida: "buni keyingi dastur o'qiydimi?" — ha bo'lsa stdout.

9.2 Har qanday xatoda exit 1

throw qilib yuborilgan xato Node'da exit 1 beradi — foydalanuvchi xatosi ham, tarmoq xatosi ham bir xil. Skript farqlay olmaydi. Foydalanuvchi xatosi uchun alohida klass (FoydalanishXatosi) va exit 2.

9.3 Stek bilan yiqilish

text
Error: EPIPE: broken pipe, write

yoki TypeError: Cannot read properties of undefined — foydalanuvchi uchun qo'rqinchli. CLI kutilgan xatolarni ushlab, bir qatorlik xabar berishi kerak; to'liq stek — faqat NODE_DEBUG yoki --verbose bilan.

9.4 Shebang'siz bin

bash shim (Git Bash yoki Linux) faylni shell skript deb bajaradi — import: command not found; kanon tajribasida npx valyuta Git Bash'da osilib ham qoldi (Workspaces darsi). dist/bin.js ning birinchi qatorini tekshiring — tsc shebang'ni manbadan saqlaydi.

9.5 Interaktiv savol quvurda

Dastur stdin dan javob kutadi, lekin skriptda hech kim javob bermaydi — CI abadiy osilib qoladi. isTTY bo'lmasa — savol bermang, bayroq yoki xato bilan to'xtang (readline).

10. Mashqlar

Mashqlar kurs/mashqlar/24/50-cli-anatomiya/ da.

1-mashq (oson): qaysi oqim, qaysi kod?

Har holat uchun yozing: matn qaysi oqimga (stdout/stderr) va exit kod nechchi.

  1. valyuta 100 USD natijasi: 100.00 USD = 1 190 315.00 UZS
  2. Ogohlantirish: offline rejim …
  3. valyuta kurs XYZ — Xato: Noma'lum valyuta: XYZ
  4. Tarmoq ham, kesh ham yo'q — Xato: fetch failed
  5. valyuta --help
Yechim
  1. stdout, 0 — so'ralgan natija.
  2. stderr — natija emas, xabar; exit kod 0 (ish bajarildi).
  3. stderr, 2 — foydalanuvchi noma'lum kod berdi.
  4. stderr, 1 — buyruq to'g'ri, lekin ish vaqtida muammo; keyinroq qayta urinish mumkin.
  5. stdout, 0 — yordam so'ralgan, demak bu natija.

2-mashq (o'rta): o'zbekcha argument xatolari

argumentlarniOl(args) funksiyasini yozing. U parseArgs ni strict: true bilan chaqirsin (bayroqlar: format — satr, -f; offline — boolean). Muvaffaqiyatda { kod: 0, values, positionals }, xatoda { kod: 2, xabar } qaytarsin. Xabar o'zbekcha: ERR_PARSE_ARGS_UNKNOWN_OPTION uchun Noma'lum bayroq, ERR_PARSE_ARGS_INVALID_OPTION_VALUE uchun Bayroq qiymati noto'g'ri, qolganlari uchun asl xabar. Bayroq nomini xato matnidan oling: u bittalik qo'shtirnoq ichida birinchi keladi. Yordam: /'([^']+)'/.exec(matn) (RegExp asoslari). To'rt holatni sinang: kurs -f csv, --bayroq, --format, --offline=ha.

Yechim
js
import { parseArgs } from "node:util";

const XABARLAR = {
  ERR_PARSE_ARGS_UNKNOWN_OPTION: "Noma'lum bayroq",
  ERR_PARSE_ARGS_INVALID_OPTION_VALUE: "Bayroq qiymati noto'g'ri",
};

function argumentlarniOl(args) {
  try {
    const { values, positionals } = parseArgs({
      args,
      options: {
        format: { type: "string", short: "f" },
        offline: { type: "boolean" },
      },
      allowPositionals: true,
      strict: true,
    });
    return { kod: 0, values, positionals };
  } catch (x) {
    const sarlavha = XABARLAR[x.code];
    if (!sarlavha) return { kod: 2, xabar: x.message };
    const bayroq = /'([^']+)'/.exec(x.message)?.[1] ?? "?";
    return { kod: 2, xabar: `${sarlavha}: ${bayroq}` };
  }
}

for (const args of [
  ["kurs", "-f", "csv"],
  ["--bayroq"],
  ["--format"],
  ["--offline=ha"],
]) {
  const n = argumentlarniOl(args);
  console.log(n.kod, n.xabar ?? JSON.stringify({ ...n.values }));
}

Konsolda:

text
0 {"format":"csv"}
2 Noma'lum bayroq: --bayroq
2 Bayroq qiymati noto'g'ri: -f, --format <value>
2 Bayroq qiymati noto'g'ri: --offline

Uchinchi qatorga qarang: parseArgs xabarida bayroq '-f, --format <value>' ko'rinishida — biz uni o'zgartirmadik, foydalanuvchiga qaysi bayroq ekani baribir aniq. Xato kodi (x.code) bo'yicha ishlash — xabar matnidan ko'ra ishonchliroq: kod o'zgarmaydi, matn Node versiyasi bilan o'zgarishi mumkin.

3-mashq (qiyin): sinab bo'ladigan mini-CLI

main(argv) funksiyasini yozing — u hech narsani o'zi chiqarmaydi, { stdout, stderr, kod } qaytaradi. Buyruq: narx <miqdor> [--valyuta KOD] — so'mni valyutaga aylantiradi (USD — 11903.15, EUR — 13329.15; sukut USD), natija ikki xona aniqlikda. Xatolar: argument yo'q yoki son emas — kod: 2; noma'lum valyuta — kod: 2; --help — yordam stdout'ga, kod: 0. Keyin "haqiqiy chiqish" qiluvchi kichik ishgaTushir(argv) yozing: stdout ni console.log, stderr ni console.error bilan chiqarsin va kodni qaytarsin. To'rt chaqiruvni sinang. Yordam: Number(x) va Number.isFinite (Number turi).

Yechim
js
import { parseArgs } from "node:util";

const KURS = { USD: 11903.15, EUR: 13329.15 };
const YORDAM = "Foydalanish: narx <so'm> [--valyuta USD|EUR]";

function main(argv) {
  let a;
  try {
    a = parseArgs({
      args: argv,
      options: {
        valyuta: { type: "string", default: "USD" },
        help: { type: "boolean", default: false },
      },
      allowPositionals: true,
    });
  } catch (x) {
    return { stdout: "", stderr: x.message, kod: 2 };
  }
  if (a.values.help) return { stdout: YORDAM, stderr: "", kod: 0 };
  const som = Number(a.positionals[0]);
  if (a.positionals.length === 0 || !Number.isFinite(som)) {
    const stderr = `Xato: son kerak\n${YORDAM}`;
    return { stdout: "", stderr, kod: 2 };
  }
  const kod = a.values.valyuta.toUpperCase();
  if (!(kod in KURS)) {
    const stderr = `Xato: noma'lum valyuta ${kod}`;
    return { stdout: "", stderr, kod: 2 };
  }
  const natija = (som / KURS[kod]).toFixed(2);
  const stdout = `${som} UZS = ${natija} ${kod}`;
  return { stdout, stderr: "", kod: 0 };
}

function ishgaTushir(argv) {
  const n = main(argv);
  if (n.stdout) console.log(n.stdout);
  if (n.stderr) console.error(n.stderr);
  return n.kod;
}

console.log("kod:", ishgaTushir(["1000000"]));
console.log("kod:", ishgaTushir(["1000000", "--valyuta", "eur"]));
console.log("kod:", ishgaTushir(["ming"]));
console.log("kod:", ishgaTushir(["5", "--valyuta", "XYZ"]));

Konsolda:

text
1000000 UZS = 84.01 USD
kod: 0
1000000 UZS = 75.02 EUR
kod: 0
Xato: son kerak
Foydalanish: narx <so'm> [--valyuta USD|EUR]
kod: 2
Xato: noma'lum valyuta XYZ
kod: 2

Ajratish (main — toza, ishgaTushir — yon ta'sirli) valyuta dagi cli.ts / bin.ts bo'linishining kichik nusxasi. main ni node:test bilan bemalol sinash mumkin: jarayon ishga tushirish shart emas. Haqiqiy faylda oxirgi qator process.exitCode = ishgaTushir(process.argv.slice(2)) bo'ladi.

4-mashq: CLI qadami — EPIPE himoyasi (tajriba)

O'z valyuta ingizda src/bin.ts ga «EPIPE» bo'limidagi ishlovchini qo'shing va valyuta kurs all -f csv | head -1 hamda katta oraliqli valyuta tarix USD --dan 2025-01-01 -f csv | head -1 ni avval ishlovchisiz, keyin ishlovchi bilan sinang. Exit kodni echo ${PIPESTATUS[0]} (Git Bash) bilan ko'ring. Bu tajriba — kanonda bunday ishlovchi yo'q, shuning uchun commit qilmang.

Yechim

Kutilgan manzara: kichik chiqishda (kurs all) farq yo'q — 74 qator head yopilishidan oldin yozib bo'linadi (biz shunday ko'rdik, exit 0). Katta tarix da ishlovchisiz EPIPE bo'lishi mumkin: pipeline xatoni main() ga qaytaradi va siz Xato: …EPIPE… va exit 1 ko'rasiz. Ishlovchi bilan — jim chiqish, exit 0. Bu holat kanonda sinalmagan — tajriba natijangizni yozib qo'ying. tarix tarmoq talab qiladi (oflayn rejimda faqat keshdagi kunlar bor).

11. Real ishda

  • clig.dev — "Command Line Interface Guidelines", CLI kelishuvlari bo'yicha ochiq qo'llanma. Undagi qoidalar (stdout/stderr, exit kod, --help, NO_COLOR) shu darsdagi bilan bir xil.
  • CI va skriptlar: GitHub Actions har qadamning exit kodiga qaraydi — 0 bo'lmasa to'xtaydi. Noto'g'ri exit kod — "yashil, lekin buzilgan" pipeline.
  • Kasb asboblari: gh, vercel, prisma, eslint — hammasi shu anatomiyada. Ularning --help iga endi boshqa ko'z bilan qaraysiz: qaysi kelishuvni qanday bajargan.
  • Intervyu: "stdout va stderr farqi?", "exit kod 2 nimani bildiradi?", "nega process.exit() emas, exitCode?" — CLI va DevOps lavozimlarida tez-tez so'raladi.

Xulosa

  • CLI'ning uchta chiqish kanali: stdout — natija, stderr — xabarlar, exit kod — skript uchun signal.
  • Shebang #!/usr/bin/env node Linux/macOS uchun, Windows'da npm .cmd/.ps1 shim yozadi; shebang'siz bin buziladi.
  • parseArgs + strict: true: noma'lum bayroq xato; -5 kabi manfiy son uchun --.
  • Katta chiqishni | head ga ulaganda EPIPE — process.stdout.on("error") bilan jim chiqish.
  • Exit kodlar: 0, 1 (ish vaqti), 2 (foydalanish), 130 (Ctrl+C); process.exitCode — process.exit() emas.
  • Kelishuvlar: --help stdout'ga, --version bitta qator, sozlash ustuvorligi bayroq → env → fayl → sukut.

Keyingi dars: Commander bilan buyruqlar tizimi — sub-buyruqlar, avtomatik yordam va tekshiruvni kutubxonaga topshirish: valyuta ning Commander nusxasi va nega u kanonga kirmadi.

Manbalar

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Node.js CLI anatomiyasi: shebang, bin, parseArgs, stdout/stderr va exit kodlar — IlmHamroh