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

Môi trường thử nghiệmHọ RHELAlmaLinux 9.8bash 5.1Tài khoản root
Shell$1$#--

Đối số dòng lệnh Linux (command-line argument) là các giá trị được truyền cho chương trình bằng cách viết sau tên lệnh, cách nhau bằng dấu cách. Khi truyền tùy chọn, dấu ngoặc kép, ký tự đại diện và -- cho một script thử in nguyên các đối số nhận được trên máy chủ AlmaLinux 9, chúng tôi thấy việc tách đối số do shell làm, còn việc đọc tùy chọn do từng lệnh tự làm.

Kết quả bên dưới là đúng nguyên những gì chạy được bằng bash 5.1 trên tài khoản root của máy chủ AlmaLinux 9.8. Các script thử và tệp mẫu được tạo trong một thư mục làm việc riêng, còn đường dẫn /home/user/www là giá trị ví dụ.

1️⃣ Đối số dòng lệnh Linux là gì?

Khi bạn viết trên dòng lệnh kiểu như [lệnh đối-số-1 đối-số-2], shell tách các từ theo dấu cách. Từ đầu tiên trở thành chương trình sẽ chạy, các từ còn lại được truyền theo thứ tự cho chương trình đó làm đối số.

Để nhìn thấy các đối số được truyền, chúng tôi tạo args.sh trong thư mục làm việc. $0 chứa tên dùng để chạy, $# chứa số lượng đối số, còn từ $1 trở đi chứa lần lượt các đối số. Dấu ngoặc vuông được thêm vào để thấy rõ chỗ bắt đầu và kết thúc của từng đối số.

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

Chúng tôi cấp quyền thực thi cho script rồi chạy với ba từ: a, b và c.

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

Ba từ đi vào $1, $2 và $3, còn $# là 3. Nếu chạy bằng cách viết tên script sau bash, $0 chứa đúng tên đã viết là args.sh.

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

Chỉ có $0 đổi thành args.sh; ba đối số và số lượng vẫn giống kết quả trước.

2️⃣ Đối số dòng lệnh và tùy chọn lệnh

Lệnh Linux thường được viết theo thứ tự [lệnh -tùy-chọn đối-số]. Tùy chọn thay đổi cách lệnh hoạt động, còn đối số xác định tệp, đường dẫn hay giá trị mà lệnh xử lý.

ls -l /home/user/www

Với shell, -l cũng chỉ là một đối số. Khi truyền cùng các từ đó cho args.sh, -l vào $1 và đường dẫn vào $2.

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

Việc đọc -l thành tùy chọn là do ls làm. Vì vậy cùng một chữ nhưng mỗi lệnh lại hiểu khác nhau: -n của head đặt số dòng sẽ in, còn -n của ls hiển thị chủ sở hữu bằng số thay vì tên.

Có những tùy chọn lấy từ đứng ngay sau nó làm giá trị của mình. Trong lệnh find, -type dùng f và -name dùng "*.txt" làm giá trị; giá trị đi kèm tùy chọn như vậy gọi là đối số của tùy chọn.

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

Trong lệnh này, dấu . ở đầu là đối số không đi kèm tùy chọn nào và xác định thư mục bắt đầu tìm.

3️⃣ Các dạng tùy chọn lệnh Linux

Tùy chọn ngắn bắt đầu bằng một dấu gạch, chỉ gồm một chữ và có thể viết liền nhiều cái. Trước tiên chúng tôi liệt kê thư mục site trong thư mục làm việc bằng [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

Tiếp theo, chúng tôi dùng diff so sánh với kết quả của [ls -la], viết liền hai chữ.

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

Hai kết quả không khác nhau một ký tự nào. Tùy chọn dài bắt đầu bằng hai dấu gạch và viết thành từ; --all của ls là cùng tùy chọn với -a, nên [ls -l --all site] cũng cho cùng kết quả.

Tùy chọn dài vẫn được chấp nhận dù chỉ viết phần đầu, miễn là phần đó chỉ khớp với một tùy chọn. --al khớp với hai tùy chọn --all và --almost-all, nên ls không in danh sách và kết thúc với mã thoát 2.

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

Tùy chọn nhận giá trị có thể viết theo nhiều dạng. Khi in hai dòng đầu của a.txt bằng head, tùy chọn ngắn có thể viết liền giá trị thành [-n2].

head -n2 a.txt
alpha
beta

Tùy chọn dài nối giá trị bằng dấu bằng thành [--lines=2].

head --lines=2 a.txt
alpha
beta

Cả bốn dạng, kể cả [-n 2] và [--lines 2] viết cách, đều in ra hai dòng alpha và beta.

ls, head và cat đọc tùy chọn bằng hàm getopt_long của thư viện C. Xem ba tệp chương trình bằng strings thì cả ba đều chứa tên hàm này.

Để kiểm tra cùng quy tắc trên shell, dùng lệnh getopt của util-linux. Viết tùy chọn ngắn sau -o, tùy chọn dài sau -l; dấu hai chấm sau một chữ nghĩa là tùy chọn đó nhận giá trị.

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

-la viết liền được tách thành -l và -a, còn -n 2 được gộp thành -n với giá trị 2. a.txt không phải tùy chọn nên bị chuyển xuống cuối, sau --.

4️⃣ Đối số trong dấu ngoặc kép và ký tự đại diện

Tên tệp có dấu cách phải đặt trong dấu ngoặc kép thì mới được truyền thành một đối số.

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

Khi viết không có dấu ngoặc kép, my và file.txt bị tách riêng và $# thành 3. Nếu truyền như vậy cho ls hay cat, chúng sẽ tìm hai tệp tên là my và file.txt.

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

Ký tự đại diện được shell đổi thành tên tệp trước khi chương trình nhận. Khi truyền *.txt trong thư mục làm việc, bốn tệp có đuôi .txt được đưa vào theo thứ tự tên.

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

"*.txt" đặt trong dấu ngoặc kép không bị shell đổi, nên thành đúng một đối số nguyên văn. Với tùy chọn như -name của find, nơi chương trình tự so sánh mẫu, hãy truyền trong dấu ngoặc kép như vậy.

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

Dấu ngoặc kép rỗng cũng được truyền thành một đối số rỗng. Khi chạy [./args.sh '' b], $# là 2 và $1 rỗng.

5️⃣ Dấu kết thúc tùy chọn lệnh (--)

Đối số bắt đầu bằng dấu gạch, như tệp tên -n, sẽ bị lệnh đọc thành tùy chọn. Khi chạy [ls -n] trong thư mục dash chỉ chứa tệp đó, ls coi -n không phải tên tệp mà là tùy chọn hiển thị bằng số, rồi liệt kê thư mục.

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

Đối số viết sau -- không bị đọc thành tùy chọn dù bắt đầu bằng dấu gạch. [ls -- -n] chỉ in tên tệp -n.

ls -- -n
-n

Ngay trong một lệnh, cùng ký tự cũng được đọc khác nhau trước và sau --. Khi truyền -n cho cat hai lần, -n đầu được đọc là tùy chọn số dòng, còn -n sau -- được đọc là tên tệp, nên nội dung tệp hiện ra kèm số dòng.

cat -n -- -n
     1	dash file

Cũng có cách thêm đường dẫn và viết ./-n; khi đó đối số không bắt đầu bằng dấu gạch nên được đọc là cùng tệp -n.

Đọc -- là kết thúc tùy chọn cũng là quy tắc phía lệnh. Khi truyền cho args.sh, vốn không đọc tùy chọn, -- cũng vào nguyên vẹn $1.

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

Quy ước tiện ích POSIX quy định -- đầu tiên không phải đối số của tùy chọn được chấp nhận là kết thúc tùy chọn. Vì vậy, với các lệnh theo quy ước này, tên tệp bắt đầu bằng - được viết sau --.

6️⃣ Nhận đối số dòng lệnh trong shell script

Shell script đọc các đối số nhận được qua tham số vị trí và tham số đặc biệt. main(int argc, char *argv[]) của chương trình C cũng nhận cùng các đối số, nên đặt hai bên cạnh nhau sẽ như dưới đây.

Tham sốGiá trị chứa trong đóHàm main của C
$0Tên script đã chạyargv[0]
$1 ~ $9Đối số thứ nhất đến thứ chínargv[1] ~ argv[9]
${10}Từ đối số thứ mười trở đi đặt trong ngoặc nhọnargv[10]
$#Số lượng đối số, không tính $0argc - 1
"$@"Tất cả đối số, từng cái riêngTừ argv[1] đến hết
"$*"Tất cả đối số nối thành một bằng dấu cách—

Chương trình Python nhận cùng các đối số dưới dạng danh sách sys.argv. Khi truyền ba đối số bằng [python3 -c], my file.txt trong dấu ngoặc kép vào một phần tử của danh sách, còn phần tử đầu tiên là -c.

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

"$@" và "$*" cho kết quả khác nhau khi đặt trong dấu ngoặc kép. Để xem khác biệt này, chúng tôi tạo star.sh in lần lượt hai dạng.

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

Chúng tôi chạy star.sh với ba đối số: a, "b c" và d.

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

"$@" truyền ba đối số riêng rẽ, còn "$*" nối chúng bằng dấu cách rồi truyền thành một.

Vì vậy khi chuyển nguyên các đối số nhận được sang lệnh khác, hãy dùng "$@".

shift bỏ $1 và dồn các đối số còn lại lên trước một vị trí. Lệnh này dùng trong script đọc trước một tùy chọn rồi xử lý phần còn lại như danh sách tệp.

Chúng tôi tạo shift.sh in $1 và $# trước và sau shift.

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

Chúng tôi chạy shift.sh với ba đối số: -v, a.txt và b.txt.

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

Từ đối số thứ mười trở đi, đặt số trong ngoặc nhọn như ${10}. Chúng tôi tạo ten.sh in $10 và ${10} cạnh nhau.

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

Chúng tôi truyền 11 đối số, từ a đến k, cho ten.sh.

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

$10 ra a0, tức $1 thêm số 0 phía sau, còn ${10} ra đối số thứ mười là j.

🔲 Trình mô phỏng đối số dòng lệnh

Trong trình mô phỏng bên dưới, bạn có thể truyền dấu ngoặc kép, ký tự đại diện và -- cho args.sh, và tự gõ lệnh để xem ls và getopt đọc tùy chọn ra sao, star.sh và shift.sh nhận đối số thế nào.

Trình mô phỏng đối số dòng lệnhbash 5.1
[root@server work]#
Máy chủ AlmaLinux ảo chỉ chạy trong trình duyệt của bạn; args.sh in các đối số nhận được kèm số thứ tự, và ↑↓ gọi lại các lệnh đã dùng trước đó.

🔢 FAQ & nội dung đề xuất

Giới hạn tổng độ dài của đối số và biến môi trường được kiểm tra bằng [getconf ARG_MAX]; trên máy chủ AlmaLinux 9.8 là 2097152 byte. Vượt giới hạn này, chương trình không chạy và báo lỗi [Argument list too long]. Khi có rất nhiều tệp, đừng truyền một lần bằng ký tự đại diện mà hãy chia ra bằng -exec của find hoặc xargs.

Đối số (argument) là giá trị thực sự được truyền khi chạy lệnh, còn tham số (parameter) là tên ở phía nhận giá trị đó. Trong shell script, các tham số vị trí như $1, $2 nhận các đối số được truyền vào.

getopts có sẵn trong bash đọc từng tùy chọn ngắn như -v. Nó không đọc tùy chọn dài, nên khi cần nhận cả tùy chọn dài như --verbose, hãy sắp xếp đối số trước bằng lệnh getopt đã dùng ở trên.

ℹ️ Thông báo tiếp thị liên kết
Nội dung trên trang này có chứa liên kết affiliate (tiếp thị liên kết). Khi khách truy cập mua sản phẩm hoặc dịch vụ qua các liên kết này, trang sẽ nhận hoa hồng từ bên bán. Trong quá trình đó, số tiền người mua phải trả (sẽ giảm khi có chương trình khuyến mãi ↓) không hề tăng lên. Giá, khuyến mãi và tình trạng hàng được đăng theo thời điểm viết bài và có thể khác với thực tế, vì vậy hãy kiểm tra lại lần cuối với bên bán trước khi mua. Việc chọn và đánh giá sản phẩm dựa trên tiêu chí riêng của trang; việc có nhận hoa hồng hay không không ảnh hưởng đến thứ tự giới thiệu hay nội dung đánh giá.

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *

Prove your humanity: 8   +   3   =