Command-Line Arguments Syntax Guidelines

Santawat S
HiveGround
Published in
2 min readMar 28, 2024
ภาพประกอบไม่เกี่ยวข้องกับเนื้อหา

วันก่อน น้อง Junior full-stack JavaScript Dev ต้องเขียน script เพื่อ migrate data ใน MongoDB จึงตกลงกันว่าจะทำเป็น NodeJS command-line

ไม่นานผ่านไป ก็มี Pull Request มาให้ Review

Logic การทำงานของ script ถูกต้องตามที่ตกลงกัน แถมมาพร้อมกับ help message เรียบร้อยด้วย แต่หนึ่งเรื่องที่ติดใจระหว่างการ review ก็คือ การจัดการกับ command-line argument

Remark: Code JS ใน article นี้ เป็นตัวอย่างของ code ที่ไม่ได้ follow guideline — ไม่แนะนำให้ใครนำไปใช้นะครับ 🙅‍♂️🙅‍♂️

Help message กล่าวไว้ว่า

function displayUsage() {
console.log("node migrateFoo2Bar.js <dburl> <dbname> <foo> <bar>");
console.log(
"Example: node migrateFoo2Bar.js --uri=mongodb://localhost:27000 --db=mydb --foo=foo_collection --bar=bar_collection"
);
}

และมาพร้อมกับการจัดการ Argument ตามนี้

if (argv.length !== 6) {
console.log("ERROR: Invalid arguments");
displayUsage();
process.exit();
}
const ARG = argv.map((arg) => arg.replace(/--.*?=/, "").toString());
const URI = ARG[2];
const DATABASE = ARG[3];
const FOO_COLLECTION = ARG[4];
const BAR_COLLECTION = ARG[5];

ซึ่งมันไม่ตรงกับประสบการณ์การใช้ command line ที่ผ่านมา ก็เลยถือโอกาสไปค้นหา reference (ที่ควรจะไปหามาอ่านซัก 15 ปีก่อนหน้านี้) ก็ไปจบที่ตรงนี้ — Argument Syntax (The GNU C Library)

ใจความสำคัญก็คือ

  • Command line argument มี 2 ประเภท
    — Non-option: จำเป็นต้องใส่
    — Option: ใส่ก็ได้ ไม่ใส่ก็ได้
  • Option argument — ปกติจะเห็นกันอยู่ 2 รูปแบบ
    — Short form: ขึ้นต้นด้วยขีดกลาง (hyphen, -) ตามด้วยตัวอักษรตัวเดียว เช่น -v, -o stdout
    — Long form: ขึ้นต้นด้วยขีดกลาง 2 ขีด ( -- ) ตามด้วยชื่อที่เป็น alphanumeric characters แล้วก็ใช้เครื่องหมายเท่ากับ ( = ) เพื่อแบ่งระหว่างชื่อของ argument นั้น กับ value เช่น --verbose, --output=stdout
    การใส่ option argument ที่ไม่มีการใส่ value หลายๆ ตัว สามารถเขียนแยก หรือรวมกันก็ได้ เช่น -v -x -c สามารถเขียนเป็น -vxc ได้
  • Non-option argument
    — ตามปกติ จะใส่หลังจาก option argument (เพื่อความง่ายในการอ่าน และการ parse)
    — ต้องใส่เรียงตามลำดับ
  • อื่นๆ
    — (อันนี้ก็เพิ่งจะรู้เมื่อได้เข้ามาอ่าน reference นี่แหละ) ขีดกลาง 2 ตัวเฉยๆ เป็นการบอกว่า argument ทุกตัวหลังจากนี้ไม่ได้เป็น option argument แล้ว ต่อให้ขึ้นต้นด้วยขีดกลาง - ก็ตาม เช่น
    program.exe -s -option2=foo -- req_arg1 -req_arg2
    req_arg1
    และ -req_arg2 เป็น non-option arguments

ถ้าเราเปรียบเทียบการทำงานของ code migrateFoo2Bar กับ POSIX Argument Syntax Guidelines ก็จะเห็นได้ว่า

  • Argument ทั้ง 4 ตัว ต้องถูกใส่มาตามลำดับเท่านั้น (ตรงกับการเป็น non-option arguments)
  • Argument ทั้ง 4 ตัว เป็น non-option Argument แต่ขึ้นต้นด้วย -- ที่เป็นรูปแบบของ Long option argument (แต่ดันอยู่ในรูปของ Long-form option argument)

หลังจากที่บอกเรื่องนี้ให้น้อง junior dev ไป Pull Request ที่แก้มาก็มาพร้อมกับการใช้งาน NodeJS Commander ซึ่งช่วยทำให้เราจัดการทั้งในส่วนของ Help Message และการ Parse Argument ได้สะดวกขึ้น, code ก็อ่านง่ายขึ้น

ก็เป็นอันว่า PR นี้ก็ผ่านฉลุยไป

สุดท้ายนี้ ก็ต้องขอย้ำอีกครั้งว่า Code ใน article นี้ ไม่แนะนำให้เอาไปเป็นตัวอย่างนะครับ เพราะมันไม่ได้ follow guideline

แนะนำให้ไปใช้ module ที่มีคน provide ไว้ให้ และไปดูตัวอย่างในนั้น จะดีกว่า

NodeJS Commander — https://www.npmjs.com/package/commander

Python Argparse — https://docs.python.org/3/library/argparse.html

หรือถ้าจะต้องเขียน Shell Script ก็ไปใช้ getopts ได้นะครับ

#คนแก่ขี้บ่น

--

--