اذهب إلى المحتوى

استخدام الوضع التفاعلي والتعامل مع سطر الأوامر في Node.js


Ola Abbas

سنتعرّف في هذا المقال على الوضع التفاعلي REPL في Node.js الذي نستخدمه في الطرفية لتقييم تعبير برمجي مكتوب بلغة javascript، كما سنتعلّم كيفية التعامل مع سطر الأوامر في Node.js من تمرير وسائط منه وإرسال مخرجات إليه وقبول مدخلات منه.

استخدام الوضع التفاعلي REPL

يسمى الوضع التفاعلي في Node.js باسم REPL اختصارًا إلى اقرأ-قيّم-اطبع-كرر Read-Evaluate-Print-Loop، أي اقرأ وقدِّر قيمة التعبير البرمجي ثم اطبع الناتج وكرر العملية، وهو طريقة رائعة لاستكشاف ميزات Node.js بطريقة سريعة.

استعملنا الأمر node سابقًا لتشغيل أحد السكربتات:

node script.js

أما إذا حذفنا اسم الملف، فسندخل في الوضع التفاعلي REPL:

node

وعندما تجرب الأمر السابق في الطرفية عندك، فسيحدث ما يلي وسيبقى الأمر في وضع السكون وينتظر منك إدخال تعبير ما:

$ node
Welcome to Node.js v14.15.0.
Type ".help" for more information.
> 
اقتباس

ملاحظة: إذا لم تكن تعرف كيف تفتح الطرفية عندك، فيمكنك البحث في جوجل عن ذلك مع ذكر نظام التشغيل الخاص بك، وانظر مقال "مدخل إلى طرفيّة لينكس".

إذا أردنا أن نكون أكثر دقةً، فإن الوضع التفاعلي ينتظر منّا إدخال شيفرة JavaScript، لذا لنبدأ بسطر بسيط:

> console.log('test')
test
undefined
>

القيمة الأولى المطبوعة test هي ناتج الأمر الذي طلبنا منه طباعة سلسلة نصية إلى الطرفية، ثم حصلنا على القيمة undefined وهي القيمة المعادة من تنفيذ console.log()‎، ويمكننا الآن إدخال سطر JavaScript جديد.

استخدام الزر tab للإكمال التلقائي

من أجمل مزايا الوضع التفاعلي REPL هو أنه تفاعلي، ففي أثناء كتابك للشيفرة إذا ضغطت على زر tab، فسيحاول الوضع التفاعلي الإكمال التلقائي لما كتبته ليوافق اسم متغير عرفته مسبقًا.

استكشاف كائنات JavaScript

جرِّب إدخال اسم كائن من كائنات JavaScript مثل Number وأضف إليه نقطةً ثم اضغط على tab، إذ سيعرض لك الوضع التفاعلي جميع الخاصيات والتوابع التي يمكنك الوصول إليها في ذاك الكائن.

JavaScript object 1.png

استكشاف الكائنات العامة

يمكنك معاينة الكائنات العامة بكتابة global.‎ ثم الضغط على الزر tab:

JavaScript object 2.png

المتغير الخاص _

إذا كتبت _ بعد شيفرة ما، فسيؤدي ذلك إلى طباعة ناتج آخر عملية.

الأوامر ذوات النقط

يملك الوضع التفاعلي REPL بعض الأوامر الخاصة وكلها تبدأ بنقطة . وهي:

  • ‎.help: يظهر المساعدة للأوامر ذوات النقط.
  • ‎.editor: تفعيل وضع المحرر، وذلك لكتابة شيفرة JavaScript متعددة الأسطر بسهولة، وبعد دخولك في هذا الوضع وكتابتك للشيفرة المطلوبة، فيمكنك إدخال Ctrl+D لتنفيذ الأمر الذي كتبت.
  • ‎.break: إذا كتبت الأمر ‎.break عند كتابتك لتعبير متعدد الأسطر، فستلغي أي مدخلات قادمة، ومثله مثل الضغط على Ctrl+C.
  • ‎.clear: إعادة ضبط سياق الوضع التفاعلي إلى كائن فارغ وإزالة أي تعابير متعددة الأسطر جرت كتابتها.
  • ‎.load: تحميل ملف JavaScript بمسار نسبي إلى مجلد العمل الحالي.
  • ‎.save: حفظ ما أدخلته في الجلسة التفاعلية إلى ملف مع تمرير مسار الملف بعد كتابة ‎.save.
  • ‎‎.exit: الخروج من الوضع التفاعلي وهو يماثل الضغط على Ctrl+C مرتين.

يعرِف الوضع التفاعلي REPL متى تكتب تعبيرًا متعدد الأسطر دون الحاجة إلى تشغيل الأمر ‎.editor، فإذا بدأت مثلًا بكتابة حلقة تكرار مثل هذه:

[1, 2, 3].forEach(num => {

ثم ضغطت على زر enter، فسيعرف الوضع التفاعلي أنك تكتب تعبيرًا متعدد الأسطر ويبدأ سطرًا جديدًا بثلاث نقط ، مما يشير إلى أنك ما زلت تعمل على القسم أو المقطع نفسه:

... console.log(num)
... })

إذا كتبت ‎.break في آخر السطر، فسيتوقف وضع تعدد الأسطر ولن يُنفَّذ التعبير الذي كتبته.

تمرير الوسائط من سطر الأوامر إلى Node.js

سنشرح في هذا القسم كيفية استقبال وسائط arguments في برنامج Node.js مُمرَّرة من سطر الأوامر، إذ يمكنك تمرير أي عدد من الوسائط أثناء تشغيل برنامج Node.js باستخدام الأمر:

node app.js

يمكن أن تكون الوسائط بمفردها، أو على شكل زوج من المفاتيح والقيم مثل:

node app.js ahmed

أو

node app.js name=ahmed

يجعل هذا طريقة الحصول على القيمة مختلفةً في شيفرة Node.js. إذ أنّ طريقة الحصول على الوسائط هي استخدام الكائن process المبني في Node.js، ففيه الخاصية argv التي هي مصفوفة تحتوي على جميع الوسائط الممررة عبر سطر الأوامر؛ ويكون الوسيط الأول هو المسار الكامل للأمر node، بينما الوسيط الثاني هو مسار الملف المُنفَّذ كاملًا، وجميع الوسائط الإضافية ستكون موجودةً من الموضع الثالث إلى آخره، كما يمكنك المرور على جميع المعاملات بما في ذلك مسار node ومسار الملف المُنفَّذ باستخدام حلقة تكرار:

process.argv.forEach((val, index) => {
  console.log(`${index}: ${val}`)
})

يمكنك الحصول على الوسائط الإضافية من خلال إنشاء مصفوفة جديدة تستثني أول قيمتين:

const args = process.argv.slice(2)

إذا كان لديك معامل بمفرده دون مفتاح مثل:

node app.js ahmed

فيمكنك الوصول إليه كما يلي:

const args = process.argv.slice(2)
args[0]

أما في حالة كان الوسيط هو مفتاح وقيمة كما في:

node app.js name=ahmed

فإن قيمة args[0]‎ هي name=ahmed، وستحتاج إلى تفسيرها، وأفضل طريقة هي استخدام المكتبة minimist التي تساعدنا بالتعامل مع الوسائط:

const args = require('minimist')(process.argv.slice(2))
args['name'] // ahmed

إرسال تطبيق Node.js المخرجات إلى سطر الأوامر

سنتعلم كيفية الطباعة إلى سطر الأوامر باستخدام Node.js بدءًا من الاستخدام الأساسي للتابع console.log حتى وصولنا إلى الأمور المعقدة.

طباعة المخرجات باستخدام الوحدة console

توفِّر Node.js الوحدة console التي توفر عددًا كبيرًا من الطرائق المفيدة في التعامل مع سطر الأوامر، وهي تشبه إلى حد ما الكائن console الموجود في المتصفحات، والتابع الأساسي الأكثر استخدامًا في هذه الوحدة هو التابع console.log()‎، الذي يطبع السلسلة النصية التي تمررها إليه إلى الطرفية، وإذا مررت كائنًا فسيعرضه على أساس سلسلة نصية، كما يمكنك تمرير قيمًا متعددةً إلى التابع console.log()‎ كما يلي، إذ ستطبع Node.js القيمتين x وy معًا:

const x = 'x'
const y = 'y'
console.log(x, y)

يمكنك أيضًا تنسيق السلاسل النصية بتمرير القيم ومُحدِّد التنسيق كما في المثال التالي:

console.log('My %s has %d years', 'cat', 2)

إذ أنّ محددات التنسيق format specifiers هي:

  • ‎%s: تنسيق المتغير على أساس سلسلة نصية.
  • %d أو %i: تنسيق المتغير على أساس عدد صحيح.
  • ‎%f: تنسيق المتغير على أساس عدد ذي فاصلة عشرية.
  • %O: تنسيق المتغير على أساس كائن كما في المثال البسيط الآتي:
console.log('%O', Number)

مسح محتوى الطرفية

يمسح التابع console.clear()‎ محتوى الطرفية، لكن يختلف سلوكه اعتمادًا على الطرفية المستخدَمة.

عد العناصر

يُعَدّ التابع console.count()‎ مفيدًا في بعض الحالات التي تريد فيها عدّ المرات التي طُبِعَت فيها السلسلة النصية وإظهار الرقم بجوارها، خذ هذا المثال:

const x = 1
const y = 2
const z = 3
console.count(
  'The value of x is ' + x + ' and has been checked .. how many times?'
)
console.count(
  'The value of x is ' + x + ' and has been checked .. how many times?'
)
console.count(
  'The value of y is ' + z + ' and has been checked .. how many times?'
)

يمكننا إحصاء عدد البرتقالات والتفاحات التي لدينا:

const oranges = ['orange', 'orange']
const apples = ['just one apple']
oranges.forEach(fruit => {
  console.count(fruit)
})
apples.forEach(fruit => {
  console.count(fruit)
})

طباعة تتبع مكدس الاستدعاء

قد تكون هنالك حالات من المفيد فيها طباعة تتبع مكدس الاستدعاء call stack trace لإحدى الدوال، وذلك للإجابة مثلًا على السؤال التالي: كيف وصلنا إلى هذا الجزء من الشيفرة، إذ يمكنك استخدام التابع console.trace()‎:

const function2 = () => console.trace()
const function1 = () => function2()
function1()

سيطبع مكدس الاستدعاء ما يلي، وهذا هو الناتج إذا جربنا الشيفرة السابقة في النمط التفاعلي REPL في Node.js:

Trace
    at function2 (repl:1:33)
    at function1 (repl:1:25)
    at repl:1:1
    at ContextifyScript.Script.runInThisContext (vm.js:44:33)
    at REPLServer.defaultEval (repl.js:239:29)
    at bound (domain.js:301:14)
    at REPLServer.runBound [as eval] (domain.js:314:12)
    at REPLServer.onLine (repl.js:440:10)
    at emitOne (events.js:120:20)
    at REPLServer.emit (events.js:210:7)

طباعة الزمن المستغرق

يمكنك ببساطة حساب مقدار الوقت الذي أخذته الدالة للتنفيذ باستخدام التابعين time()‎ وtimeEnd()‎ كما في المثال التالي:

const doSomething = () => console.log('test')
const measureDoingSomething = () => {
  console.time('doSomething()')
  // افعل شيئًا وقس الوقت المستغرق لتنفيذه
  doSomething()
  console.timeEnd('doSomething()')
}
measureDoingSomething()

مجرى الخرج القياسي stdout والخطأ القياسي stderr

رأينا أنّ التابع console.log()‎ رائع لطباعة الرسائل في الطرفية، وهذا ما يسمى مجرى الخرج القياسي standard output stream أو stdout؛ أما التابع console.error()‎ فسيطبع الرسائل المرسَلة إلى مجرى الخطأ القياسي stderr والتي لن تظهر في الطرفية وإنما ستظهر في سجل الخطأ.

طباعة المخرجات بألوان مختلفة

يمكنك أن تغيير لون المخرجات النصية في الطرفية باستخدام تسلسلات التهريب escape sequences، وتسلسل التهريب هو مجموعة من المحارف التي تُعرِّف لونًا معينًا في الطرفية مثل:

console.log('\x1b[33m%s\x1b[0m', 'hi!')

يمكنك تجربة ذلك باستخدام النمط التفاعلي في Node.js وستُعرَض السلسلة النصية hi!‎ باللون الأصفر، لكن هذه الطريقة ذات مستوى منخفض، فأبسط طريقة لتلوين المخرجات في الطرفية هي باستخدام مكتبة مثل Chalk، حيث يمكنها أيضًا إجراء عمليات التنسيق الأخرى إضافةً إلى تلوينها للنص مثل جعل النص غامقًا أو مائلًا أو مسطرًا تحته، كما يمكنك تثبيتها باستخدام npm install chalk ثم استخدامها:

const chalk = require('chalk')
console.log(chalk.yellow('hi!'))

من المؤكد أن استخدام chalk.yellow أكثر راحةً من محاولة تذكُّر شيفرات التهريب وستكون قابلية قراءة الشيفرة أفضل.

إنشاء شريط تقدم في الطرفية

تُعَدّ حزمة Progress حزمةً رائعةً لإنشاء شريط تقدُّم في الطرفية، حيث يمكنك تثبيتها باستخدام npm install progress ببساطة.

تُنشِئ الشيفرة التالية شريط تقدُّم بعشر خطوات، حيث ستكتمل خطوة كل 100 ميللي ثانية، وحين اكتمال الشريط سنصفِّر العداد:

const ProgressBar = require('progress')

const bar = new ProgressBar(':bar', { total: 10 })
const timer = setInterval(() => {
  bar.tick()
  if (bar.complete) {
    clearInterval(timer)
  }
}, 100)

قبول المدخلات من سطر الأوامر

يمكننا جعل تطبيق Node.js الذي يعمل من سطر الأوامر تفاعليًا باستخدام وحدة readline المبنية في أساس Node.js.

أصبحت Node.js بدءًا من الإصدار السابع توفِّر الوحدة readline التي تحصل على المدخلات من مجرى قابل للقراءة مثل process.stdin (مجرى الدخل القياسي stdin) سطرًا بسطر والذي يكون أثناء تنفيذ برنامج Node.js من سطر الأوامر هو الدخل المكتوب في الطرفية.

const readline = require('readline').createInterface({
  input: process.stdin,
  output: process.stdout
})


readline.question(`What's your name?`, (name) => {
  console.log(`Hi ${name}!`)
  readline.close()
})

تسأل الشيفرة السابقة المستخدِم عن اسمه، وبعد كتابة المستخدِم الاسم والضغط على enter سنرسل تحيةً له.

يُظهِر التابع question()‎ المعامل parameter الأول -أي السؤال- وينتظر مدخلات المستخدِم، ثم يستدعي دالة رد نداء عندما يضغط المستخدِم على زر enter. لاحظا أننا أغلقنا واجهة readline في دالة رد النداء، كما توفِّر الواجهة readline توابعًا كثيرةً متعددةً وسنتركك لاستكشافها من توثيقها؛ أما إذا احتجت إلى إدخال كلمة مرور، فمن الأفضل إظهار رمز * بدلًا منها، وأسهل طريقة لذلك هو استخدام الحزمة readline-sync التي تشبه readline في الواجهة البرمجية وتستطيع التعامل مع هذه الحالة دون عناء، والخيار الممتاز والمتكامل هو الحزمة inquirer.js، حيث يمكنك تثبيتها باستخدام npm install inquirer، كما يمكنك إعادة كتابة الشيفرة السابقة كما يلي:

const inquirer = require('inquirer')

var questions = [{
  type: 'input',
  name: 'name',
  message: "What's your name?",
}]

inquirer.prompt(questions).then(answers => {
  console.log(`Hi ${answers['name']}!`)
})

تسمح لك مكتبة inquirer.js بفعل أمور كثيرة مثل الأسئلة متعددة الخيارات وأزرار الانتقاء والتأكيدات وغير ذلك، كما من المفيد معرفة جميع الخيارات المتاحة أمامنا خصوصًا التي توفِّرها Node.js، لكن إذا أردت التعامل مع مدخلات سطر الأوامر كثيرًا، فالخيار الأمثل هو مكتبة inquirer.js.

ترجمة -وبتصرّف- للفصل Command Line من كتاب The Node.js handbook لصاحبه Flavio Copes.

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...