리눅스 명령행 인자: 명령어 옵션과 따옴표, 옵션 끝 표시, $1, $# 받기

สภาพแวดล้อมทดสอบตระกูล RHELAlmaLinux 9.8bash 5.1บัญชี root

อาร์กิวเมนต์บรรทัดคำสั่งของ Linux (command-line argument) คือค่าที่ส่งให้โปรแกรมโดยเขียนต่อท้ายชื่อคำสั่งและคั่นด้วยช่องว่าง เมื่อลองส่งออปชัน เครื่องหมายคำพูด ไวลด์การ์ด และ -- ให้สคริปต์ทดสอบที่พิมพ์อาร์กิวเมนต์ที่ได้รับออกมาตามจริงบนเซิร์ฟเวอร์ AlmaLinux 9 พบว่าผู้แยกอาร์กิวเมนต์คือเชลล์ ส่วนผู้อ่านออปชันคือแต่ละคำสั่งเอง

ผลลัพธ์ด้านล่างคือผลที่ได้จริงจาก bash 5.1 บนบัญชี root ของเซิร์ฟเวอร์ AlmaLinux 9.8 สคริปต์ทดสอบและไฟล์ตัวอย่างสร้างไว้ในโฟลเดอร์ทำงานแยกต่างหาก และพาธ /home/user/www เป็นค่าตัวอย่าง

1️⃣ อาร์กิวเมนต์บรรทัดคำสั่งของ Linux คืออะไร?

เมื่อเขียนในบรรทัดคำสั่งเช่น [คำสั่ง อาร์กิวเมนต์1 อาร์กิวเมนต์2] เชลล์จะแยกคำโดยใช้ช่องว่างเป็นตัวแบ่ง คำแรกจะเป็นโปรแกรมที่จะรัน ส่วนคำที่เหลือจะถูกส่งให้โปรแกรมนั้นตามลำดับเป็นอาร์กิวเมนต์

เพื่อดูอาร์กิวเมนต์ที่ถูกส่งไปด้วยตา เราสร้าง args.sh ไว้ในโฟลเดอร์ทำงาน $0 เก็บชื่อที่ใช้รัน $# เก็บจำนวนอาร์กิวเมนต์ และตั้งแต่ $1 เป็นต้นไปเก็บอาร์กิวเมนต์ตามลำดับ วงเล็บเหลี่ยมใส่ไว้ให้เห็นจุดเริ่มและจุดจบของแต่ละอาร์กิวเมนต์

#!/bin/bash
echo "\$0 = $0"
echo "\$# = $#"
i=1
for a in "$@"; do
  echo "\$$i = [$a]"
  i=$((i+1))
done

เราให้สิทธิ์รันแก่สคริปต์ แล้วรันโดยส่งคำสามคำ: a, b และ c

./args.sh a b c
$0 = ./args.sh
$# = 3
$1 = [a]
$2 = [b]
$3 = [c]

คำทั้งสามเข้าไปอยู่ใน $1, $2 และ $3 และ $# มีค่า 3 ถ้ารันโดยเขียนชื่อสคริปต์ต่อท้าย bash ค่าใน $0 จะเป็นชื่อตามที่เขียนคือ args.sh

bash args.sh a b c
$0 = args.sh
$# = 3
$1 = [a]
$2 = [b]
$3 = [c]

มีเพียง $0 ที่เปลี่ยนเป็น args.sh ส่วนอาร์กิวเมนต์สามตัวและจำนวนเหมือนผลก่อนหน้า

2️⃣ อาร์กิวเมนต์บรรทัดคำสั่งกับออปชันของคำสั่ง

คำสั่ง Linux มักเขียนตามลำดับ [คำสั่ง -ออปชัน อาร์กิวเมนต์] ออปชันเปลี่ยนวิธีทำงานของคำสั่ง ส่วนอาร์กิวเมนต์กำหนดไฟล์ พาธ หรือค่าที่คำสั่งจะจัดการ

ls -l /home/user/www

สำหรับเชลล์ -l ก็เป็นเพียงอาร์กิวเมนต์หนึ่งตัว เมื่อส่งคำเดียวกันให้ args.sh -l เข้าไปอยู่ใน $1 และพาธอยู่ใน $2

./args.sh -l /home/user/www
$0 = ./args.sh
$# = 2
$1 = [-l]
$2 = [/home/user/www]

ผู้ที่อ่าน -l เป็นออปชันคือ ls ดังนั้นตัวอักษรเดียวกันจึงมีความหมายต่างกันในแต่ละคำสั่ง -n ของ head กำหนดจำนวนบรรทัดที่จะพิมพ์ ส่วน -n ของ ls แสดงเจ้าของเป็นหมายเลขแทนชื่อ

ออปชันบางตัวรับคำที่อยู่ถัดไปทันทีเป็นค่าของตัวเอง ในคำสั่ง find -type ใช้ f และ -name ใช้ "*.txt" เป็นค่าของตัวเอง ค่าที่ติดกับออปชันแบบนี้เรียกว่าอาร์กิวเมนต์ของออปชัน

find . -type f -name "*.txt"

ในคำสั่งนี้ . ที่อยู่หน้าสุดคืออาร์กิวเมนต์ที่ไม่ได้ติดกับออปชันใด และใช้กำหนดโฟลเดอร์ที่เริ่มค้นหา

3️⃣ รูปแบบออปชันของคำสั่ง Linux

ออปชันสั้นที่ขึ้นต้นด้วยขีดหนึ่งตัวมีตัวอักษรเดียว และเขียนติดกันได้หลายตัว ก่อนอื่นเราแสดงโฟลเดอร์ site ในโฟลเดอร์ทำงานด้วย [ls -l -a]

ls -l -a site
total 8
drwxr-xr-x 3 root root  52 Oct 28 16:00 .
drwxr-xr-x 3 root root 137 Oct 29 01:20 ..
-rw-r--r-- 1 root root  17 Oct 28 16:00 .htaccess
drwxr-xr-x 2 root root  23 Oct 28 16:00 css
-rw-r--r-- 1 root root  14 Oct 28 16:00 index.html

จากนั้นนำไปเทียบด้วย diff กับผลลัพธ์ของ [ls -la] ที่เขียนสองตัวอักษรติดกัน

diff <(ls -l -a site) <(ls -la site) && echo same
same

ผลลัพธ์ทั้งสองไม่ต่างกันเลยแม้แต่ตัวอักษรเดียว ออปชันยาวที่ขึ้นต้นด้วยขีดสองตัวเขียนเป็นคำ และ --all ของ ls เป็นออปชันเดียวกับ -a ดังนั้น [ls -l --all site] ก็ให้ผลเหมือนกัน

ออปชันยาวรับได้แม้เขียนเพียงส่วนต้น ถ้าส่วนนั้นตรงกับออปชันเดียว --al ตรงกับสองออปชันคือ --all และ --almost-all ทำให้ ls ไม่พิมพ์รายการและจบด้วยรหัสออก 2

ls --al site
ls: option '--al' is ambiguous; possibilities: '--all' '--almost-all'
Try 'ls --help' for more information.

ออปชันที่รับค่าเขียนได้หลายแบบ เมื่อพิมพ์สองบรรทัดแรกของ a.txt ด้วย head ออปชันสั้นเขียนค่าติดกันเป็น [-n2] ได้

head -n2 a.txt
alpha
beta

ออปชันยาวเชื่อมค่าด้วยเครื่องหมายเท่ากับเป็น [--lines=2]

head --lines=2 a.txt
alpha
beta

ทั้งสี่แบบ รวมถึง [-n 2] และ [--lines 2] ที่เว้นวรรค ให้ผลสองบรรทัดคือ alpha และ beta

ls, head และ cat อ่านออปชันด้วยฟังก์ชัน getopt_long ของไลบรารี C เมื่อดูไฟล์โปรแกรมทั้งสามด้วย strings พบว่าทุกไฟล์มีชื่อฟังก์ชันนี้อยู่

เมื่อจะตรวจกฎเดียวกันในเชลล์ ให้ใช้คำสั่ง getopt ของ util-linux เขียนออปชันสั้นหลัง -o และออปชันยาวหลัง -l ส่วนโคลอนหลังตัวอักษรหมายถึงออปชันที่รับค่า

getopt -o lan: -l all,lines: -- -la -n 2 --all a.txt
 -l -a -n '2' --all -- 'a.txt'

-la ที่เขียนติดกันถูกแยกเป็น -l และ -a และ -n 2 ถูกรวมเป็น -n กับค่า 2 ส่วน a.txt ที่ไม่ใช่ออปชันถูกย้ายไปท้ายสุดหลัง --

4️⃣ อาร์กิวเมนต์ในเครื่องหมายคำพูดและไวลด์การ์ด

ชื่อไฟล์ที่มีช่องว่างต้องครอบด้วยเครื่องหมายคำพูดจึงจะถูกส่งเป็นอาร์กิวเมนต์เดียว

./args.sh "my file.txt" report.txt
$0 = ./args.sh
$# = 2
$1 = [my file.txt]
$2 = [report.txt]

เมื่อเขียนโดยไม่มีเครื่องหมายคำพูด my กับ file.txt ถูกแยกออกจากกัน และ $# กลายเป็น 3 ถ้าส่งแบบนี้ให้ ls หรือ cat จะหาไฟล์สองไฟล์ชื่อ my และ file.txt

./args.sh my file.txt report.txt
$0 = ./args.sh
$# = 3
$1 = [my]
$2 = [file.txt]
$3 = [report.txt]

ไวลด์การ์ดถูกเชลล์แปลงเป็นชื่อไฟล์ก่อนที่โปรแกรมจะได้รับ เมื่อส่ง *.txt ในโฟลเดอร์ทำงาน ไฟล์สี่ไฟล์ที่ลงท้ายด้วย .txt เข้าไปตามลำดับชื่อ

./args.sh *.txt
$0 = ./args.sh
$# = 4
$1 = [a.txt]
$2 = [b.txt]
$3 = [my file.txt]
$4 = [report.txt]

"*.txt" ที่ครอบด้วยเครื่องหมายคำพูดไม่ถูกเชลล์แปลง จึงเป็นอาร์กิวเมนต์เดียวตามตัวอักษร ออปชันอย่าง -name ของ find ที่โปรแกรมเปรียบเทียบแพตเทิร์นเอง ให้ส่งโดยครอบเครื่องหมายคำพูดแบบนี้

./args.sh "*.txt"
$0 = ./args.sh
$# = 1
$1 = [*.txt]

เครื่องหมายคำพูดว่างก็ถูกส่งเป็นอาร์กิวเมนต์ว่างหนึ่งตัว เมื่อรัน [./args.sh '' b] ค่า $# เป็น 2 และ $1 ว่าง

5️⃣ เครื่องหมายสิ้นสุดออปชันของคำสั่ง (--)

อาร์กิวเมนต์ที่ขึ้นต้นด้วยขีด เช่น ไฟล์ชื่อ -n คำสั่งจะอ่านเป็นออปชัน เมื่อรัน [ls -n] ในโฟลเดอร์ dash ที่มีไฟล์นั้นไฟล์เดียว ls รับ -n ไม่ใช่เป็นชื่อไฟล์แต่เป็นออปชันแสดงหมายเลข แล้วพิมพ์รายการในโฟลเดอร์

ls -n
total 4
-rw-r--r-- 1 0 0 10 Oct 28 16:00 -n

อาร์กิวเมนต์ที่เขียนหลัง -- จะไม่ถูกอ่านเป็นออปชัน แม้จะขึ้นต้นด้วยขีด [ls -- -n] พิมพ์เพียงชื่อไฟล์ -n

ls -- -n
-n

แม้ในคำสั่งเดียวกัน ตัวอักษรเดียวกันก็ถูกอ่านต่างกันก่อนและหลัง -- เมื่อส่ง -n ให้ cat สองครั้ง -n ตัวแรกถูกอ่านเป็นออปชันเลขบรรทัด ส่วน -n หลัง -- ถูกอ่านเป็นชื่อไฟล์ จึงได้เนื้อหาไฟล์ที่มีเลขบรรทัดกำกับ

cat -n -- -n
     1	dash file

อีกวิธีคือใส่พาธเป็น ./-n ในกรณีนี้อาร์กิวเมนต์ไม่ได้ขึ้นต้นด้วยขีด จึงถูกอ่านเป็นไฟล์ -n ตัวเดียวกัน

การอ่าน -- เป็นจุดสิ้นสุดออปชันก็เป็นกฎฝั่งคำสั่งเช่นกัน เมื่อส่งให้ args.sh ที่ไม่ได้อ่านออปชัน -- ก็เข้าไปอยู่ใน $1 ตามเดิม

./args.sh -- -a
$0 = ./args.sh
$# = 2
$1 = [--]
$2 = [-a]

ข้อตกลงยูทิลิตีของ POSIX กำหนดให้ -- ตัวแรกที่ไม่ใช่อาร์กิวเมนต์ของออปชันถือเป็นจุดสิ้นสุดออปชัน ดังนั้นในคำสั่งที่ทำตามข้อตกลงนี้ ชื่อไฟล์ที่ขึ้นต้นด้วย - จึงเขียนไว้หลัง --

6️⃣ รับอาร์กิวเมนต์บรรทัดคำสั่งในเชลล์สคริปต์

เชลล์สคริปต์อ่านอาร์กิวเมนต์ที่ได้รับผ่านพารามิเตอร์ตำแหน่งและพารามิเตอร์พิเศษ main(int argc, char *argv[]) ของโปรแกรม C ก็รับอาร์กิวเมนต์ชุดเดียวกัน เมื่อวางเทียบกันจะเป็นดังนี้

พารามิเตอร์ค่าที่เก็บอยู่ฟังก์ชัน main ในภาษา C
$0ชื่อสคริปต์ที่รันargv[0]
$1 ~ $9อาร์กิวเมนต์ตัวที่หนึ่งถึงตัวที่เก้าargv[1] ~ argv[9]
${10}ตั้งแต่อาร์กิวเมนต์ตัวที่สิบให้ครอบด้วยวงเล็บปีกกาargv[10]
$#จำนวนอาร์กิวเมนต์ ไม่นับ $0argc - 1
"$@"อาร์กิวเมนต์ทั้งหมด แยกทีละตัวตั้งแต่ argv[1] จนถึงตัวสุดท้าย
"$*"อาร์กิวเมนต์ทั้งหมดต่อกันเป็นหนึ่งเดียวด้วยช่องว่าง—

โปรแกรม Python ได้รับอาร์กิวเมนต์เดียวกันเป็นลิสต์ sys.argv เมื่อส่งอาร์กิวเมนต์สามตัวด้วย [python3 -c] my file.txt ที่ครอบด้วยเครื่องหมายคำพูดเข้าไปอยู่ในช่องเดียวของลิสต์ และช่องแรกเป็น -c

python3 -c 'import sys; print(sys.argv)' a "my file.txt" -l
['-c', 'a', 'my file.txt', '-l']

"$@" กับ "$*" ให้ผลต่างกันเมื่อครอบด้วยเครื่องหมายคำพูดคู่ เพื่อดูความต่างนี้ เราสร้าง star.sh ที่พิมพ์ทั้งสองรูปแบบตามลำดับ

#!/bin/bash
echo 'with "$@"'
printf '  [%s]\n' "$@"
echo 'with "$*"'
printf '  [%s]\n' "$*"

เรารัน star.sh โดยส่งอาร์กิวเมนต์สามตัว: a, "b c" และ d

./star.sh a "b c" d
with "$@"
  [a]
  [b c]
  [d]
with "$*"
  [a b c d]

"$@" ส่งอาร์กิวเมนต์สามตัวแยกกัน ส่วน "$*" ต่อด้วยช่องว่างแล้วส่งเป็นตัวเดียว

ดังนั้นเมื่อจะส่งอาร์กิวเมนต์ที่ได้รับต่อให้คำสั่งอื่นตามเดิม ให้ใช้ "$@"

shift ทิ้ง $1 และเลื่อนอาร์กิวเมนต์ที่เหลือไปข้างหน้าทีละหนึ่งตำแหน่ง ใช้ในสคริปต์ที่อ่านออปชันหนึ่งตัวก่อนแล้วจัดการส่วนที่เหลือเป็นรายการไฟล์

เราสร้าง shift.sh ที่พิมพ์ $1 และ $# ก่อนและหลัง shift

#!/bin/bash
echo "before shift: \$1 = $1, \$# = $#"
shift
echo "after shift:  \$1 = $1, \$# = $#"

เรารัน shift.sh โดยส่งอาร์กิวเมนต์สามตัว: -v, a.txt และ b.txt

./shift.sh -v a.txt b.txt
before shift: $1 = -v, $# = 3
after shift:  $1 = a.txt, $# = 2

ตั้งแต่อาร์กิวเมนต์ตัวที่สิบ ให้ครอบตัวเลขด้วยวงเล็บปีกกาเช่น ${10} เราสร้าง ten.sh ที่พิมพ์ $10 และ ${10} คู่กัน

#!/bin/bash
echo "\$10 = $10"
echo "\${10} = ${10}"

เราส่งอาร์กิวเมนต์ 11 ตัวตั้งแต่ a ถึง k ให้ ten.sh

./ten.sh a b c d e f g h i j k
$10 = a0
${10} = j

$10 ออกมาเป็น a0 คือ $1 ตามด้วย 0 ส่วน ${10} ออกมาเป็นอาร์กิวเมนต์ตัวที่สิบคือ j

🔲 ตัวจำลองอาร์กิวเมนต์บรรทัดคำสั่ง

ในตัวจำลองด้านล่าง คุณส่งเครื่องหมายคำพูด ไวลด์การ์ด และ -- ให้ args.sh ได้ และพิมพ์คำสั่งเองเพื่อดูว่า ls และ getopt อ่านออปชันอย่างไร และ star.sh กับ shift.sh รับอาร์กิวเมนต์อย่างไร

ตัวจำลองอาร์กิวเมนต์บรรทัดคำสั่งbash 5.1
[root@server work]#
เซิร์ฟเวอร์ AlmaLinux เสมือนที่ทำงานเฉพาะในเบราว์เซอร์ของคุณ args.sh พิมพ์อาร์กิวเมนต์ที่ได้รับพร้อมหมายเลข และ ↑↓ เรียกคำสั่งที่เคยใช้กลับมา

🔢 FAQ & เนื้อหาแนะนำ

เพดานความยาวรวมของอาร์กิวเมนต์และตัวแปรสภาพแวดล้อมตรวจได้ด้วย [getconf ARG_MAX] บนเซิร์ฟเวอร์ AlmaLinux 9.8 มีค่า 2097152 ไบต์ ถ้าเกินเพดานนี้ โปรแกรมจะไม่ทำงานและเกิดข้อผิดพลาด [Argument list too long] เมื่อมีไฟล์จำนวนมาก อย่าส่งทีเดียวด้วยไวลด์การ์ด ให้แบ่งส่งด้วย -exec ของ find หรือ xargs

อาร์กิวเมนต์ (argument) คือค่าที่ส่งจริงตอนรันคำสั่ง ส่วนพารามิเตอร์ (parameter) คือชื่อฝั่งผู้รับค่านั้น ในเชลล์สคริปต์ พารามิเตอร์ตำแหน่งอย่าง $1 และ $2 เป็นตัวรับอาร์กิวเมนต์ที่ส่งมา

getopts ที่มีอยู่ใน bash อ่านออปชันสั้นอย่าง -v ทีละตัว แต่ไม่อ่านออปชันยาว ถ้าต้องรับออปชันยาวอย่าง --verbose ด้วย ให้จัดอาร์กิวเมนต์ก่อนด้วยคำสั่ง getopt ที่ใช้ด้านบน

🔗 เนื้อหาแนะนำ
การขยายและการอ้างอิงของเชลล์ [Linux] การขยายของเชลล์ 6 แบบและการอ้างอิงของเชลล์ 3 แบบ บทความที่ใช้ echo ตรวจว่าเชลล์เปลี่ยนอาร์กิวเมนต์อย่างไรด้วยการขยายชื่อพาธ ทิลเดอ เลขคณิต และวงเล็บปีกกา ก่อนรันคำสั่ง และการอ้างอิงด้วยเครื่องหมายคำพูดคู่ เครื่องหมายคำพูดเดี่ยว และแบ็กสแลชที่หยุดการขยาย อ่านบทความ → เชลล์สคริปต์ (Shell Script)

ℹ️ ประกาศเรื่องลิงก์แอฟฟิลิเอต
เนื้อหาของเว็บไซต์นี้มีลิงก์แอฟฟิลิเอต หากผู้เข้าชมซื้อสินค้าหรือบริการผ่านลิงก์เหล่านี้ เว็บไซต์นี้จะได้รับค่าคอมมิชชันจากผู้ขาย ในกระบวนการนี้ ราคาที่ผู้ซื้อจ่าย(ราคาจะลดลงเมื่อมีโปรโมชัน↓)จะไม่สูงขึ้นแต่อย่างใด ราคา ส่วนลด และข้อมูลสต็อกที่แสดงเป็นข้อมูล ณ เวลาที่เขียนและอาจต่างจากความเป็นจริง โปรดตรวจสอบกับผู้ขายอีกครั้งก่อนซื้อ การเลือกและการประเมินสินค้าเขียนตามเกณฑ์ของเราเอง และการได้รับค่าคอมมิชชันหรือไม่ไม่มีผลต่อลำดับการแนะนำหรือเนื้อหาการประเมิน

ใส่ความเห็น

อีเมลของคุณจะไม่แสดงให้คนอื่นเห็น ช่องข้อมูลจำเป็นถูกทำเครื่องหมาย *

Prove your humanity: 10   +   7   =