IlmHamroh
JavaScript Full-stack/24-qism. Node.js53/55-dars16 daqiqa
Mundarija (34)

Node.js skriptlar: bash o'rniga JavaScript bilan avtomatlashtirish (zx, execa, fs.glob)

Qisqacha: Takrorlanadigan ishni (fayllarni qayta nomlash, zaxira, nashrdan oldingi tekshiruv) bash o'rniga Node skripti bilan yozsangiz, u Windows, macOS va Linux'da bir xil ishlaydi. Node 24 da ko'p narsa o'rnatilgan: fs.glob, fs.cp, mkdir({ recursive }), child_process. Tashqi dastur chaqirish uchun execa (Windows'dagi npm.cmd muammosini o'zi hal qiladi) yoki zx ($`buyruq` sintaksisi — lekin ichida bash kerak). Skriptni vaqt bo'yicha ishga tushirish — Linux'da cron, Windows'da Task Scheduler.

Bu darsda

  • Bash skriptni qachon Node skripti bilan almashtirish kerakligini tushuntira olasiz.
  • child_process, execa va zx bilan tashqi buyruq chaqirasiz va Windows'dagi uchta tuzoqni bilasiz.
  • fs.glob, fs.cp, rename bilan ommaviy qayta nomlash va zaxira skriptini yozasiz.
  • Skriptni cron va Windows Task Scheduler bilan rejalashtirish yo'lini bilasiz.
  • valyuta uchun nashrdan oldingi tekshiruv skriptini (scripts/paket-tekshir.mjs) tushunasiz.

Oldin bilishingiz kerak: child_process: tashqi dasturlarni ishga tushirish, fs chuqur: kataloglar, stat, glob va watch, path va os: kross-platforma kod, Node'da CLI dastur anatomiyasi.

1. Nega bu kerak?

Dasturchi ishining bir qismi — takrorlanadigan mayda ishlar. Har nashrdan oldin: build, test, paket hajmini tekshirish. Har hafta: kesh papkasini zaxiralash. To'ydan keyin: 800 ta IMG_0001.JPG ni toy-001.jpg qilish.

An'anaviy vosita — bash skript. Muammo: Windows. cp -r, find, sed u yerda yo'q yoki boshqacha ishlaydi. Jamoangizda bitta Windows'chi bo'lsa, bash skript uning uchun ishlamaydi. Node esa hammada bor — siz uni allaqachon loyiha uchun o'rnatgansiz.

Ikkinchi sabab — xatolar. Bash'da rm -rf $PAPKA/ — o'zgaruvchi bo'sh bo'lsa, rm -rf / bo'ladi. JavaScript'da yo'l — qiymat, uni tekshirish, test yozish oson.

O'xshatish: retsept. Bash skript — faqat bitta oshxonadagi pechga mos retsept ("3-tugmani bosing"). Node skripti — har qanday oshxonada ishlaydigan retsept ("180 darajada 20 daqiqa").

2. Tashqi buyruq chaqirish

2.1 child_process va Windows tuzog'i

child_process darsida execFile va spawn ni ko'rgansiz. Skriptlarda eng ko'p kerak bo'ladigan buyruq — npm. Windows'da biz sinadik:

js
import { execFileSync } from "node:child_process";
try {
  execFileSync("npm", ["--version"], { encoding: "utf8" });
} catch (x) {
  console.log(x.code);
}
try {
  execFileSync("npm.cmd", ["--version"], { encoding: "utf8" });
} catch (x) {
  console.log(x.code);
}
text
ENOENT
EINVAL

Windows'da npm — bu npm.cmd fayli. Birinchi xato ENOENT: npm nomli bajariladigan fayl yo'q. Ikkinchisi EINVAL: Node xavfsizlik sababli .cmd faylni shell'siz ishga tushirmaydi. shell: true bilan ishlaydi, lekin kanon tajribasi (t25-child.txt) ko'rsatgan: Node 24 bunda DEP0190 ogohlantirishini beradi (argumentlar shell orqali o'tadi — injection xavfi). Linux'da execFileSync("npm", …) to'g'ridan ishlaydi (hujjat bo'yicha; sinamadik).

Kanon yechimi — npm'ning o'z JS faylini Node bilan chaqirish. npm run … ichida npm o'z yo'lini npm_execpath muhit o'zgaruvchisiga yozadi:

js
const npmJs = process.env.npm_execpath;
const args = [npmJs, "pack", "--dry-run", "--json"];
execFileSync(process.execPath, args);

process.execPath — hozirgi node fayli. Shell yo'q, .cmd yo'q, har OS'da bir xil.

2.2 execa

execa (10.1.1) — child_process ustidagi qulay qatlam. Windows'dagi .cmd muammosini ichkarida hal qiladi:

js
import { execa } from "execa";

const { stdout } = await execa("npm", ["--version"]);
console.log("npm:", stdout);
const r = await execa("node", ["-e", "process.exit(3)"], {
  reject: false,
});
console.log("exit:", r.exitCode, r.failed);

Biz Windows'da ishga tushirdik:

text
npm: 11.19.0
exit: 3 true

npm to'g'ridan ishladi. reject: false — exit kod 0 bo'lmasa xato tashlamaydi, natijani qaytaradi: failed: true va exitCode: 3. Sukut bo'yicha esa execa xato tashlaydi — skript birinchi muvaffaqiyatsiz qadamda to'xtaydi. Bu bash'dagi set -e ning yaxshi versiyasi.

2.3 zx va biz topgan Windows tuzog'i

zx (8.8.5, Google) — skriptni bash'ga o'xshatadi:

js
import { $ } from "zx";
const v = await $`node --version`;
console.log("node:", v.stdout.trim());
const n = await $`npm --version`;
console.log("npm:", n.stdout.trim());
const fayl = "bo'sh joyli nom.txt";
await $`echo salom > ${fayl}`;
console.log((await $`cat ${fayl}`).stdout.trim());

Git Bash ochiq terminalda — ishladi: node: v24.21.0, npm: 11.19.0, salom. ${fayl} — zx o'zgaruvchini avtomatik qo'shtirnoqqa oladi, shuning uchun bo'sh joyli nom buzilmadi. Bu — zx'ning asosiy qulayligi.

Lekin zx buyruqni bash orqali bajaradi. PATH'da Git Bash bo'lmagan Windows'da (oddiy PowerShell yoki cmd) biz shuni oldik:

text
<3>WSL (10 - Relay) ERROR: CreateProcessCommon:798: execvpe(/bin/bash) failed: No such file or directory

Nima bo'ldi? zx bash ni qidirdi va C:\Windows\System32\bash.exe ni topdi — bu WSL (Windows ichidagi Linux) ishga tushiruvchisi. Bizning mashinada WSL ichida bash yo'q edi, natijada tushunarsiz xato. Yechim — usePowerShell():

js
import { $, usePowerShell } from "zx";
usePowerShell();
const v = await $`node --version`;
console.log("node:", v.stdout.trim());

Bu ishladi (node: v24.21.0). Lekin endi buyruqlar PowerShell sintaksisida — cat, > boshqacha. Ya'ni zx'da "bitta skript hamma joyda" va'dasi buyruqlar ichida buziladi.

flowchart TD
  A["Skript tashqi buyruq chaqiradi"] --> B{"Faqat Node va npm?"}
  B -- "ha" --> C["child_process + process.execPath"]
  B -- "yo'q" --> D{"Ko'p buyruq, shell qulayligi kerak?"}
  D -- "ha" --> E["zx — bash bor joyda"]
  D -- "yo'q" --> F["execa — har OS'da"]

Tekshirib ko'ring: Jamoangizda hamma Mac'da, CI esa Linux. zx tanlash xavflimi?

Javob

Kam xavfli: hamma joyda bash bor va zx aynan shunday muhit uchun. Xavf keyin paydo bo'ladi — birinchi Windows'chi qo'shilganda. Shuning uchun README'da "Git Bash kerak" deb yozing yoki skript ichidagi buyruqlarni Node API'lariga (fs, execa) o'tkazing.

3. Fayllar bilan ommaviy ish

3.1 O'rnatilganlari va kutubxonalar

Ish Node 24 da Kutubxona (versiya — npm view, 2026-10-23)
Naqsh bo'yicha qidirish fs.glob fast-glob, tinyglobby 0.2.17
Papkani nusxalash fs.cp(…, { recursive: true }) fs-extra 11.4.1 (copy)
Ichma-ich papka yaratish mkdir(…, { recursive: true }) fs-extra (ensureDir)
O'chirish rm(…, { recursive: true, force: true }) rimraf

Bir necha yil oldin chap ustun bo'sh edi — shuning uchun eski loyihalarda o'ng ustundagilarni ko'rasiz. Masalan fs.glob Node 22 da eksperimental bo'lib kelgan va hujjatga ko'ra faqat Node 24.0 da barqaror (stable) deb belgilangan. Yangi skriptda avval chap ustun. valyuta kanoni ham fs.glob ishlatgan (fs chuqur).

3.2 Ommaviy qayta nomlash

Vazifa: rasmlar/ dagi har xil yozilgan .jpg fayllarni tartib bilan toy-01.jpg, toy-02.jpg … qilish, .txt ga tegmaslik.

js
import { mkdir, writeFile, rename, glob, rm } from "node:fs/promises";
import { join, basename, extname } from "node:path";

// sinov papkasi: telefondan tushgan to'y rasmlari
await rm("rasmlar", { recursive: true, force: true });
await mkdir("rasmlar");
const nomlar = ["IMG_0003.JPG", "IMG_0001.jpg", "IMG_0002.Jpg",
  "izoh.txt"];
for (const n of nomlar) await writeFile(join("rasmlar", n), "");

const rasmlar = [];
for await (const f of glob("rasmlar/*")) {
  if (extname(f).toLowerCase() === ".jpg") rasmlar.push(f);
}
rasmlar.sort();

let n = 1;
for (const eski of rasmlar) {
  const raqam = String(n++).padStart(2, "0");
  const yangi = join("rasmlar", `toy-${raqam}.jpg`);
  await rename(eski, yangi);
  console.log(`${basename(eski)} → ${basename(yangi)}`);
}
await rm("rasmlar", { recursive: true });

Konsolda:

text
IMG_0001.jpg → toy-01.jpg
IMG_0002.Jpg → toy-02.jpg
IMG_0003.JPG → toy-03.jpg

Uch tafsilot:

  • Kengaytmani o'zimiz tekshirdik, glob("rasmlar/*.jpg") emas. Sabab — biz topgan farq: Windows'da *.jpg naqshi A.JPG, b.jpg, c.Jpg uchalasini topdi (fayl tizimi katta-kichik harfni farqlamaydi). Linux'da esa faqat b.jpg ni topadi (hujjat bo'yicha; sinamadik). extname(f).toLowerCase() ikkala OS'da bir xil.
  • sort() — glob tartibni va'da qilmaydi. Tartib muhim bo'lsa, o'zingiz saralang.
  • basename — Windows'da glob rasmlar\IMG_0001.jpg qaytaradi (teskari chiziq), Linux'da /. Chiqishda faqat nomni ko'rsatdik.

Skript boshida papkani tozalab yaratdik — uni necha marta ishga tushirsangiz ham natija bir xil. Bu xususiyatning nomi idempotentlik: takror ishga tushirish holatni buzmaydi. Avtomatik skript uchun juda muhim — u ikki marta ishga tushib qolishi mumkin.

Diqqat: Ommaviy rename — qaytarib bo'lmaydigan amal. Haqiqiy skriptda avval quruq ishga tushirish (dry run) qiling: --sinov bayrog'i bilan faqat "nima bo'lardi"ni chiqarsin. Va nom to'qnashuvini tekshiring: toy-01.jpg allaqachon bor bo'lsa, rename uni jim ustiga yozadi (hujjat bo'yicha, Windows'da ham Linux'da ham almashtiradi).

3.3 Zaxira skripti

Kesh papkasini sana bilan nomlangan nusxaga ko'chirish:

js
import { mkdir, writeFile, cp, readdir, rm } from "node:fs/promises";
import { join } from "node:path";

// sinov: kesh papkasi
await rm("kesh", { recursive: true, force: true });
await rm("zaxiralar", { recursive: true, force: true });
await mkdir("kesh/tarix", { recursive: true });
await writeFile("kesh/kurslar.json", "[]");
await writeFile("kesh/tarix/USD-2026-10-01.json", "[]");

function zaxiraNomi(sana) {
  return `kesh-${sana.toISOString().slice(0, 10)}`;
}

const nom = zaxiraNomi(new Date("2026-10-23T03:00:00Z"));
const manzil = join("zaxiralar", nom);
await cp("kesh", manzil, { recursive: true });

const fayllar = await readdir(manzil, { recursive: true });
console.log(nom);
console.log(fayllar.map((f) => f.replaceAll("\\", "/")).sort());
await rm("kesh", { recursive: true });
await rm("zaxiralar", { recursive: true });

Konsolda:

text
kesh-2026-10-23
[ 'kurslar.json', 'tarix', 'tarix/USD-2026-10-01.json' ]

Misolda sanani qat'iy berdik — natija har kuni bir xil bo'lsin. Haqiqiy skriptda zaxiraNomi(new Date()). Nom YYYY-MM-DD ko'rinishida — alifbo tartibi vaqt tartibiga teng, eng eski zaxira ro'yxat boshida. replaceAll("\\", "/") — Windows yo'llarini bir xil ko'rinishga keltirish (path va os).

Haqiqiy zaxirada yana ikki narsa kerak: eski zaxiralarni o'chirish (masalan, 7 kundan eskisini — mashqda) va natijani logga yozish.

Tekshirib ko'ring: Zaxira nomini kesh-23.10.2026 (kun.oy.yil) qilsak nima yomon?

Javob

Alifbo tartibi buziladi: kesh-01.11.2026 (1-noyabr) kesh-23.10.2026 dan oldin turadi. "Eng eskisini o'chir" mantiqi noto'g'ri faylni o'chiradi. YYYY-MM-DD (ISO 8601) — matn sifatida saralanganda ham to'g'ri tartib beradi.

4. Vaqt bo'yicha ishga tushirish

4.1 cron (Linux, macOS)

cron — Linux'ning vaqt rejalashtiruvchisi. crontab -e da bitta qator:

text
0 3 * * * cd /home/ali/valyuta && /usr/bin/node scripts/zaxira.mjs >> zaxira.log 2>&1

Besh maydon: daqiqa (0), soat (3), oy kuni, oy, hafta kuni (* — har qanday). Ya'ni har kuni 03:00 da. Hujjat bo'yicha; biz Linux'da sinamadik.

Ikki tuzoq: cron'da PATH qisqa — node ni to'liq yo'l bilan yozing (which node), va joriy papka uy papkasi — cd kerak. >> zaxira.log 2>&1 — stdout va stderr faylga, aks holda xatolarni hech kim ko'rmaydi.

4.2 Windows Task Scheduler

Windows'da — Task Scheduler (Vazifalar rejalashtiruvchisi). Buyruq qatoridan:

text
schtasks /Create /SC DAILY /ST 03:00 /TN "valyuta zaxira" /TR "node C:\Users\…\valyuta\scripts\zaxira.mjs"

Hujjat bo'yicha keltirildi — biz bu vazifani yaratmadik (zavod qoidasi: tizimda doimiy sozlama qoldirilmaydi). Grafik oynada ham shu: "Create Basic Task" → kunlik → dastur node, argument — skript yo'li.

4.3 Node ichida: node-cron

Uzoq yashaydigan Node jarayoni (server) ichida vazifa kerak bo'lsa — node-cron kabi kutubxonalar cron sintaksisini Node ichida bajaradi. Bu faqat jarayon ishlab turganda ishlaydi: server o'chsa, vazifa ham yo'q. Bitta skript uchun OS rejalashtiruvchisi ishonchliroq. Backend qismlarida fon vazifalari mavzusida qaytamiz.

5. CLI qadami: nashrdan oldingi tekshiruv skripti (holat 55 ga)

5.1 Muammo

Har nashrdan oldin qo'lda: npm pack --dry-run ro'yxatiga qarash ("src/ kirib qolmadimi? README bormi?"), hajmni tekshirish, --version ni package.json bilan solishtirish. Qo'lda — unutiladi. npm paket darsida publint va attw ni ko'rgansiz; ular paket tuzilmasini tekshiradi. Bizga esa o'z loyihamizning qoidalari kerak.

5.2 scripts/paket-tekshir.mjs

Kanon skripti (to'liq, .mjs — build'siz, Node'ning o'zi):

js
// Nashrdan oldin paketni tekshirish (bash o'rniga Node — Windows/Linux'da
// bir xil): npm pack --dry-run --json ro'yxati, hajm, bin, versiya.
// Ishga tushirish: npm run paket:tekshir (prepublishOnly ichida ham)
import { execFileSync } from "node:child_process";
import { readFileSync } from "node:fs";

const CHEGARA_KB = 50;
const paket = JSON.parse(readFileSync("package.json", "utf8"));
// Windows'da npm.cmd ni shell'siz ishga tushirib bo'lmaydi (EINVAL) —
// npm'ning o'z JS faylini node bilan chaqiramiz
const npmJs = process.env.npm_execpath;
if (!npmJs) throw new Error("npm orqali ishga tushiring: npm run paket:tekshir");
const [natija] = JSON.parse(execFileSync(process.execPath, [npmJs, "pack", "--dry-run", "--json", "--ignore-scripts"], { encoding: "utf8" }));
const fayllar = natija.files.map((f) => f.path);
const xatolar = [];
for (const kerak of ["dist/bin.js", "dist/index.js", "dist/index.d.ts", "fixtures/kurslar-2026-10-23.json", "README.md", "LICENSE"]) {
  if (!fayllar.includes(kerak)) xatolar.push(`paketda yo'q: ${kerak}`);
}
for (const f of fayllar) if (/^(src|test|scripts)\//.test(f) || f.endsWith(".tgz") || f.startsWith(".env")) xatolar.push(`paketga tushmasligi kerak: ${f}`);
if (natija.size > CHEGARA_KB * 1024) xatolar.push(`hajm ${String(natija.size)} bayt > ${String(CHEGARA_KB)} kB`);
if (!readFileSync(paket.bin.valyuta, "utf8").startsWith("#!/usr/bin/env node")) xatolar.push(`${paket.bin.valyuta}: shebang yo'q`);
const versiya = execFileSync(process.execPath, [paket.bin.valyuta, "--version"], { encoding: "utf8" }).trim();
if (versiya !== paket.version) xatolar.push(`--version ${versiya}, package.json ${paket.version}`);

console.log(`${natija.name}@${natija.version}: ${String(fayllar.length)} fayl, ${(natija.size / 1024).toFixed(1)} kB (ochilgan ${(natija.unpackedSize / 1024).toFixed(1)} kB)`);
if (xatolar.length) {
  for (const x of xatolar) console.error(`XATO: ${x}`);
  process.exitCode = 1;
} else console.log("Paket tayyor.");

Kod qatorlari kanondagidek (uzun qatorlar — aynan iqtibos; faqat bosh izohni bo'ldik). Bo'laklab:

Qism Nima tekshiradi
npm pack --dry-run --json npm aynan qaysi fayllarni qadoqlashi — JSON ro'yxat
--ignore-scripts prepack (build) qayta ishga tushmasin
majburiy ro'yxat dist, .d.ts, fixture, README, LICENSE bor
taqiq ro'yxati src/, test/, scripts/, .tgz, .env — yo'q
hajm 50 kB dan oshmasin
shebang CLI anatomiyasi darsidagi tuzoqqa qarshi
--version package.json bilan bir xil (eski build qolmagan)

Xato bo'lsa — har biri stderr'ga va process.exitCode = 1. Bitta xatoda to'xtamaydi: hammasini birdan ko'rsatadi.

5.3 package.json ga ulash

json
"paket:tekshir": "node scripts/paket-tekshir.mjs",
"prepublishOnly": "npm run check && npm run paket:tekshir"

prepublishOnly — npm uni npm publish dan oldin o'zi chaqiradi. Xato bo'lsa, nashr bo'lmaydi. Tekshiruv endi esdan chiqmaydi.

Biz yakuniy holat nusxasida ishga tushirdik (Node 24.21.0, npm 11.19.0):

text
> valyuta@1.0.0 paket:tekshir
> node scripts/paket-tekshir.mjs

valyuta@1.0.0: 61 fayl, 29.4 kB (ochilgan 110.1 kB)
Paket tayyor.

29.4 kB — npm pack ko'rsatgan 30.2 kB bilan bir xil hajm: skript 1024 ga, npm 1000 ga bo'ladi.

5.4 Bitta ESLint tuzatishi

Skript qo'shilgach npm run lint yiqildi: typescript-eslint'ning tur bilan tekshiruvi .mjs faylni "not found by the project service" deb rad etdi — u tsconfig loyihasida yo'q. Kanon tuzatishi — .mjs ni tur tekshiruvsiz fayllar ro'yxatiga qo'shish (eslint.config.js):

diff
-  { files: ["**/*.js"], extends: [tseslint.configs.disableTypeChecked] },
+  { files: ["**/*.js", "**/*.mjs"], extends: [tseslint.configs.disableTypeChecked], languageOptions: { globals: { process: "readonly", console: "readonly" } } },

globals — process va console ni ESLint tanisin.

Bu o'zgarishlar holat 55 ga kiradi. Commit — CLI'ni tarqatish darsida (README va LICENSE bilan birga). Hozircha commit qilmang.

6. Ko'p uchraydigan xatolar

6.1 ENOENT / EINVAL — Windows'da npm

execFile("npm") → ENOENT, execFile("npm.cmd") → EINVAL. Yechim: process.execPath + npm_execpath, yoki execa.

6.2 WSL … execvpe(/bin/bash) failed

zx Git Bash'siz Windows'da WSL'ning bash.exe sini topdi. Git Bash'ni PATH'ga qo'ying yoki usePowerShell().

6.3 Glob Windows'da "ko'proq" topadi

*.jpg Windows'da .JPG ni ham oladi, Linux'da — yo'q. Kengaytmani toLowerCase() bilan o'zingiz tekshiring.

6.4 Yo'llarni satr qo'shish bilan yasash

"zaxiralar/" + nom — Windows'da ishlaydi, lekin "C:\\" + … va / aralashib ketadi. Har doim path.join.

6.5 Cron'da "ishlamayapti", qo'lda — ishlaydi

Cron'ning PATH va joriy papkasi boshqa. To'liq yo'llar va logga yozish.

6.6 Skript xatoda 0 bilan tugadi

try/catch bilan xato "yutildi", exit 0. Rejalashtiruvchi va CI buni muvaffaqiyat deb biladi. Xatoda process.exitCode = 1.

7. Mashqlar

Mashqlar kurs/mashqlar/24/53-skriptlash/ da.

1-mashq (oson): to'g'ri vositani tanlang

Har vazifa uchun eng sodda vositani ayting: fs/child_process (o'rnatilgan), execa yoki zx.

  1. Papkadagi .log fayllarni sanash.
  2. Windows va Linux'da npm test ni chaqirib, exit kodni olish.
  3. Faqat Mac'chilar jamoasida git, docker, curl ni ketma-ket chaqiradigan 30 qatorlik skript.
  4. Kesh papkasini nusxalash.
Yechim
  1. fs.glob yoki readdir — tashqi dastur kerak emas.
  2. execa (yoki process.execPath + npm_execpath) — Windows'dagi .cmd muammosi tufayli.
  3. zx — hamma joyda bash bor, shell sintaksisi qulay.
  4. fs.cp(…, { recursive: true }) — Node 24 da o'rnatilgan.

2-mashq (o'rta): quruq ishga tushirish

«Ommaviy qayta nomlash» skriptini funksiyaga aylantiring: rejaTuz(nomlar, prefiks) — fayl nomlari ro'yxatidan [eski, yangi] juftlari ro'yxatini qaytarsin (fayl tizimiga tegmaydi). Faqat .jpg (katta-kichik harfsiz), saralangan, raqam ikki xonali. Agar yangi nom ro'yxatdagi boshqa faylning eski nomi bilan to'qnashsa — "TO'QNASHUV" deb belgilansin. Sinov: ["IMG_2.JPG", "toy-01.jpg", "IMG_1.jpg", "a.txt"], prefiks toy. Yordam: Set va has (Set).

Yechim
js
function rejaTuz(nomlar, prefiks) {
  const rasmlar = nomlar
    .filter((n) => n.toLowerCase().endsWith(".jpg"))
    .sort();
  const bor = new Set(nomlar);
  return rasmlar.map((eski, i) => {
    const yangi = `${prefiks}-${String(i + 1).padStart(2, "0")}.jpg`;
    const toqnash = yangi !== eski && bor.has(yangi);
    return [eski, toqnash ? `${yangi} (TO'QNASHUV)` : yangi];
  });
}

const nomlar = ["IMG_2.JPG", "toy-01.jpg", "IMG_1.jpg", "a.txt"];
for (const [eski, yangi] of rejaTuz(nomlar, "toy")) {
  console.log(`${eski} → ${yangi}`);
}

Konsolda:

text
IMG_1.jpg → toy-01.jpg (TO'QNASHUV)
IMG_2.JPG → toy-02.jpg
toy-01.jpg → toy-03.jpg

To'qnashuv topildi: IMG_1.jpg ni toy-01.jpg qilsak, mavjud toy-01.jpg ning ustiga yoziladi va bitta rasm yo'qoladi. Quruq ishga tushirish aynan shuni oldindan ko'rsatadi. Haqiqiy skriptda reja to'qnashuvsiz bo'lmaguncha rename chaqirilmaydi. Yana bir narsa: "IMG_1.jpg" < "IMG_2.JPG" < "toy-01.jpg" — katta harflar kichiklardan oldin saralanadi (Unicode tartibi).

3-mashq (qiyin): eski zaxiralarni tozalash

ochiriladiganlar(nomlar, bugun, kun) funksiyasini yozing: kesh-YYYY-MM-DD ko'rinishidagi nomlardan kun kundan eskilarini qaytarsin. Boshqa nomlarga (masalan README.txt, kesh-xato) tegmasin. Sinov: bugun 2026-10-23, 7 kun, nomlar kesh-2026-10-23, kesh-2026-10-16, kesh-2026-10-15, kesh-2026-09-30, kesh-xato, README.txt. Yordam: /^kesh-(\d{4}-\d{2}-\d{2})$/ va Date farqi millisekundlarda (Date va vaqt zonalari).

Yechim
js
const KUN_MS = 24 * 60 * 60 * 1000;

function ochiriladiganlar(nomlar, bugun, kun) {
  const boshi = new Date(`${bugun}T00:00:00Z`).getTime();
  const chegara = boshi - kun * KUN_MS;
  return nomlar.filter((nom) => {
    const m = /^kesh-(\d{4}-\d{2}-\d{2})$/.exec(nom);
    if (!m) return false; // bizning zaxira emas — tegmaymiz
    return new Date(`${m[1]}T00:00:00Z`).getTime() < chegara;
  });
}

const nomlar = [
  "kesh-2026-10-23",
  "kesh-2026-10-16",
  "kesh-2026-10-15",
  "kesh-2026-09-30",
  "kesh-xato",
  "README.txt",
];
console.log(ochiriladiganlar(nomlar, "2026-10-23", 7));

Konsolda:

text
[ 'kesh-2026-10-15', 'kesh-2026-09-30' ]

kesh-2026-10-16 — aynan 7 kun oldin, qoladi (<, <= emas). Chegarani qaysi tomonga qo'yish — kelishuv; muhimi uni test bilan qotirish. Eng muhim qator — if (!m) return false: o'chirish skripti faqat o'zi yaratgan narsaga tegsin. Sanalarni UTC'da (Z) hisobladik — vaqt mintaqasi va yozgi vaqt o'tishi kunni siljitmasin.

4-mashq: CLI qadami — paket:tekshir

valyuta loyihangizga scripts/paket-tekshir.mjs ni qo'shing (paket nomingiz @login/valyuta bo'lsa, paket.bin dagi kalit valyuta qoladi). package.json ga ikki skriptni va ESLint tuzatishini kiriting. Keyin ataylab buzing: package.json dagi "files" ga "src" qo'shing va npm run paket:tekshir ni ishga tushiring. Natijani ko'rib, qaytaring. Commit qilmang — u keyingi darslarda.

Yechim

Buzilgan holatda skript har src/*.ts fayli uchun xato qatorini stderr'ga chiqaradi: XATO: paketga tushmasligi kerak: src/…. Keyin exit 1 bilan tugaydi; sarlavha qatorida fayl soni va hajm ham oshadi. "files" ni qaytargandan keyin — …: 61 fayl, 29.4 kB … Paket tayyor. (sizdagi raqam README matningizga qarab biroz farq qiladi). Agar npm orqali ishga tushiring xatosi chiqsa — skriptni node scripts/… bilan to'g'ridan chaqirdingiz: npm_execpath faqat npm run ichida bor.

8. Real ishda

  • Monorepolar va kutubxonalar: React, Vite, Next.js repolarida scripts/ papkasi — nashr, changelog, versiya oshirish skriptlari, deyarli hammasi Node'da.
  • CI: GitHub Actions qadamlari ko'pincha node scripts/…mjs — bitta skript lokal va CI'da bir xil.
  • DevOps: zaxira, log tozalash, sertifikat yangilash — cron/systemd timer orqali.
  • Intervyu: "Kross-platforma skriptni qanday yozasiz?" — path.join, process.execPath, fs API'lari, .cmd tuzog'i va idempotentlik haqida gapira olsangiz, tajribali dasturchi ekaningiz ko'rinadi.

Xulosa

  • Node skripti — Windows, macOS va Linux'da bir xil; bash'dan xavfsizroq va sinash oson.
  • Windows'da npm — npm.cmd: execFile bilan ENOENT/EINVAL; yechim process.execPath + npm_execpath yoki execa.
  • zx qulay, lekin bash talab qiladi; Git Bash'siz Windows'da WSL xatosi, usePowerShell() — boshqa sintaksis.
  • fs.glob, fs.cp, rename, rm — o'rnatilgan; glob Windows'da katta-kichik harfni farqlamaydi, tartibni va'da qilmaydi.
  • Yaxshi skript: idempotent, quruq ishga tushirish rejimi bor, xatoda exit 1.
  • valyuta: scripts/paket-tekshir.mjs + prepublishOnly — 61 fayl, 29.4 kB … Paket tayyor.

Keyingi dars: Web scraping Node'da — sahifadan ma'lumot olish: cheerio, robots.txt, tezlik cheklovi va huquqiy chegaralar.

Manbalar

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Node.js skriptlar: bash o'rniga JavaScript bilan avtomatlashtirish (zx, execa, fs.glob) — IlmHamroh