리눅스 명령행 인자: 명령어 옵션과 따옴표, 옵션 끝 표시, $1, $# 받기
อาร์กิวเมนต์บรรทัดคำสั่งของ 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 sitetotal 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 samesameผลลัพธ์ทั้งสองไม่ต่างกันเลยแม้แต่ตัวอักษรเดียว ออปชันยาวที่ขึ้นต้นด้วยขีดสองตัวเขียนเป็นคำ และ --all ของ ls เป็นออปชันเดียวกับ -a ดังนั้น [ls -l --all site] ก็ให้ผลเหมือนกัน
ออปชันยาวรับได้แม้เขียนเพียงส่วนต้น ถ้าส่วนนั้นตรงกับออปชันเดียว --al ตรงกับสองออปชันคือ --all และ --almost-all ทำให้ ls ไม่พิมพ์รายการและจบด้วยรหัสออก 2
ls --al sitels: option '--al' is ambiguous; possibilities: '--all' '--almost-all'
Try 'ls --help' for more information.ออปชันที่รับค่าเขียนได้หลายแบบ เมื่อพิมพ์สองบรรทัดแรกของ a.txt ด้วย head ออปชันสั้นเขียนค่าติดกันเป็น [-n2] ได้
head -n2 a.txtalpha
betaออปชันยาวเชื่อมค่าด้วยเครื่องหมายเท่ากับเป็น [--lines=2]
head --lines=2 a.txtalpha
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 -ntotal 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] |
| $# | จำนวนอาร์กิวเมนต์ ไม่นับ $0 | argc - 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" dwith "$@"
[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.txtbefore 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 รับอาร์กิวเมนต์อย่างไร
🔢 FAQ & เนื้อหาแนะนำ
ไวลด์การ์ด
[อักขระพิเศษของเชลล์] ไวลด์การ์ดของ Linux (เมตาคาแรกเตอร์)
บทความที่สรุปพร้อมตัวอย่างวิธีเลือกชื่อไฟล์ด้วยอักขระไวลด์การ์ดอย่าง * และ ? และคลาสอักขระ
การรันหลายคำสั่ง
[Linux] 5 วิธีต่อคำสั่ง (เซมิโคลอน ไปป์ แอมเพอร์แซนด์..)
บทความที่สรุปห้าวิธีรันหลายคำสั่งในบรรทัดเดียวด้วยเซมิโคลอน ไปป์ &&, || และ &
การพิมพ์ข้อความ
คำสั่ง echo: From การขยายและการอ้างอิง
บทความที่สรุปวิธีพิมพ์ข้อความและค่าตัวแปรด้วย echo และออปชันของคำสั่งนี้
ℹ️ ประกาศเรื่องลิงก์แอฟฟิลิเอต
เนื้อหาของเว็บไซต์นี้มีลิงก์แอฟฟิลิเอต หากผู้เข้าชมซื้อสินค้าหรือบริการผ่านลิงก์เหล่านี้ เว็บไซต์นี้จะได้รับค่าคอมมิชชันจากผู้ขาย ในกระบวนการนี้ ราคาที่ผู้ซื้อจ่าย(ราคาจะลดลงเมื่อมีโปรโมชัน↓)จะไม่สูงขึ้นแต่อย่างใด ราคา ส่วนลด และข้อมูลสต็อกที่แสดงเป็นข้อมูล ณ เวลาที่เขียนและอาจต่างจากความเป็นจริง โปรดตรวจสอบกับผู้ขายอีกครั้งก่อนซื้อ การเลือกและการประเมินสินค้าเขียนตามเกณฑ์ของเราเอง และการได้รับค่าคอมมิชชันหรือไม่ไม่มีผลต่อลำดับการแนะนำหรือเนื้อหาการประเมิน