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

Środowisko testoweRodzina RHELAlmaLinux 9.8bash 5.1konto root
Powłoka$1$#--

Argumenty wiersza poleceń w Linuksie (command-line argument) to wartości przekazywane programowi przez wpisanie ich po nazwie polecenia, oddzielonych spacjami. Gdy na serwerze AlmaLinux 9 przekazaliśmy opcje, cudzysłowy, symbole wieloznaczne i -- do skryptu testowego, który wypisuje otrzymane argumenty bez zmian, okazało się, że argumenty dzieli powłoka, a opcje odczytuje każde polecenie samo.

Poniższe wyniki to dokładnie to, co otrzymano w bash 5.1 na koncie root serwera AlmaLinux 9.8. Skrypty testowe i przykładowe pliki utworzono w osobnym folderze roboczym, a ścieżka /home/user/www to wartość przykładowa.

1️⃣ Czym są argumenty wiersza poleceń w Linuksie?

Gdy w wierszu poleceń wpiszesz coś w rodzaju [polecenie arg1 arg2], powłoka podzieli tekst na słowa w miejscach spacji. Pierwsze słowo staje się programem do uruchomienia, a pozostałe trafiają po kolei do tego programu jako jego argumenty.

Aby zobaczyć przekazane argumenty, utworzyliśmy w folderze roboczym args.sh. $0 zawiera nazwę, pod którą go uruchomiono, $# liczbę argumentów, a od $1 kolejne argumenty. Nawiasy kwadratowe dodano, by było widać, gdzie każdy argument się zaczyna i kończy.

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

Nadaliśmy skryptowi prawo wykonywania i uruchomiliśmy go z trzema słowami: a, b i c.

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

Trzy słowa trafiły do $1, $2 i $3, a $# wyniosło 3. Jeśli uruchomisz skrypt, wpisując jego nazwę po bash, $0 zawiera nazwę dokładnie tak, jak ją wpisano, czyli args.sh.

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

Zmieniło się tylko $0 na args.sh; trzy argumenty i ich liczba były takie same jak wcześniej.

2️⃣ Argumenty wiersza poleceń a opcje poleceń

Polecenia Linuksa zapisuje się zwykle w kolejności [polecenie -opcja argument]. Opcje zmieniają sposób działania polecenia, a argumenty wskazują pliki, ścieżki lub wartości, na których ono pracuje.

ls -l /home/user/www

Dla powłoki -l to też tylko argument. Po przekazaniu tych samych słów do args.sh -l trafiło do $1, a ścieżka do $2.

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

Odczytanie -l jako opcji to zadanie ls. Dlatego ta sama litera znaczy co innego w różnych poleceniach: -n w head ustala liczbę wypisywanych wierszy, a -n w ls pokazuje właściciela jako numer zamiast nazwy.

Niektóre opcje biorą jako swoją wartość słowo stojące tuż za nimi. W poleceniu find -type bierze f, a -name bierze "*.txt" jako wartość; taka wartość przypisana do opcji to argument opcji.

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

Kropka . na początku tego polecenia to argument niezwiązany z żadną opcją, który wskazuje folder, od którego zaczyna się wyszukiwanie.

3️⃣ Formy zapisu opcji poleceń w Linuksie

Krótkie opcje zaczynają się od jednego myślnika, mają jedną literę i można je zapisywać razem. Najpierw wypisaliśmy folder site z folderu roboczego poleceniem [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

Następnie porównaliśmy to za pomocą diff z wynikiem [ls -la], w którym obie litery są zapisane razem.

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

Oba wyniki nie różniły się ani jednym znakiem. Długie opcje zaczynają się od dwóch myślników i zapisuje się je jako słowa; --all w ls to ta sama opcja co -a, więc [ls -l --all site] też dało ten sam wynik.

Długie opcje są przyjmowane, nawet gdy wpiszesz tylko ich początek, o ile pasuje on do jednej opcji. --al pasowało do dwóch opcji, --all i --almost-all, więc ls nie wypisało listy i zakończyło się kodem wyjścia 2.

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

Opcje przyjmujące wartość można zapisać na kilka sposobów. Przy wypisywaniu dwóch pierwszych wierszy pliku a.txt poleceniem head krótka opcja może mieć wartość dopisaną bez spacji, jak [-n2].

head -n2 a.txt
alpha
beta

Długa opcja łączy wartość znakiem równości, jak [--lines=2].

head --lines=2 a.txt
alpha
beta

Wszystkie cztery formy, także [-n 2] i [--lines 2] ze spacją, wypisały dwa wiersze alpha i beta.

ls, head i cat odczytują opcje funkcją getopt_long z biblioteki C. Przegląd trzech plików programów poleceniem strings pokazał, że każdy zawiera nazwę tej funkcji.

Aby sprawdzić te same reguły w powłoce, używa się polecenia getopt z util-linux. Po -o wpisuje się krótkie opcje, a po -l długie; dwukropek po literze oznacza, że opcja przyjmuje wartość.

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

Zapisane razem -la zostało rozdzielone na -l i -a, a -n 2 połączone w -n z wartością 2. a.txt, które nie jest opcją, przeniesiono na koniec, za --.

4️⃣ Argumenty w cudzysłowie i symbole wieloznaczne

Nazwę pliku ze spacją trzeba ująć w cudzysłów, aby przeszła jako jeden argument.

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

Bez cudzysłowu my i file.txt zostały rozdzielone, a $# wyniosło 3. Jeśli przekażesz to tak do ls lub cat, będą szukać dwóch plików o nazwach my i file.txt.

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

Symbole wieloznaczne powłoka zamienia na nazwy plików, zanim program je otrzyma. Po przekazaniu *.txt w folderze roboczym trafiły tam w kolejności alfabetycznej cztery pliki kończące się na .txt.

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

Ujęte w cudzysłów "*.txt" nie zostało zamienione przez powłokę i stało się jednym argumentem, znak w znak. Przy opcjach takich jak -name w find, gdzie program sam porównuje wzorzec, przekazuje się go właśnie tak, w cudzysłowie.

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

Pusty cudzysłów też przechodzi jako jeden pusty argument. Po uruchomieniu [./args.sh '' b] $# wyniosło 2, a $1 było puste.

5️⃣ Koniec opcji polecenia (--)

Argument zaczynający się od myślnika, na przykład plik o nazwie -n, polecenie odczytuje jako opcję. Po uruchomieniu [ls -n] w folderze dash, w którym jest tylko ten plik, ls potraktowało -n nie jako nazwę pliku, lecz jako opcję wyjścia numerycznego, i wypisało zawartość folderu.

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

Argumenty wpisane po -- nie są odczytywane jako opcje, nawet jeśli zaczynają się od myślnika. [ls -- -n] wypisało tylko nazwę pliku -n.

ls -- -n
-n

W jednym poleceniu te same znaki są odczytywane inaczej przed -- i po nim. Gdy przekazaliśmy -n do cat dwa razy, pierwsze -n zostało odczytane jako opcja numerów wierszy, a -n po -- jako nazwa pliku, więc zawartość pliku pojawiła się z numerami wierszy.

cat -n -- -n
     1	dash file

Można też dodać ścieżkę i zapisać ./-n; wtedy argument nie zaczyna się od myślnika i jest odczytywany jako ten sam plik -n.

Traktowanie -- jako końca opcji to także reguła po stronie polecenia. Po przekazaniu do args.sh, który nie odczytuje opcji, -- też trafiło bez zmian do $1.

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

Konwencje narzędzi POSIX stanowią, że pierwsze --, które nie jest argumentem opcji, przyjmuje się jako koniec opcji. Dlatego w poleceniach zgodnych z tymi konwencjami nazwy plików zaczynające się od - wpisuje się po --.

6️⃣ Odbieranie argumentów wiersza poleceń w skrypcie powłoki

Skrypt powłoki odczytuje otrzymane argumenty przez parametry pozycyjne i parametry specjalne. main(int argc, char *argv[]) w programie w C otrzymuje te same argumenty, więc zestawione obok siebie wyglądają tak.

ParametrZawarta wartośćFunkcja main w C
$0Nazwa uruchomionego skryptuargv[0]
$1 ~ $9Argumenty od pierwszego do dziewiątegoargv[1] ~ argv[9]
${10}Od dziesiątego argumentu w nawiasach klamrowychargv[10]
$#Liczba argumentów, bez $0argc - 1
"$@"Wszystkie argumenty, każdy osobnoOd argv[1] do końca
"$*"Wszystkie argumenty złączone spacjami w jeden—

Program w Pythonie otrzymuje te same argumenty jako listę sys.argv. Po przekazaniu trzech argumentów przez [python3 -c] ujęte w cudzysłów my file.txt trafiło do jednego elementu listy, a w pierwszym elemencie znalazło się -c.

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

"$@" i "$*" dają różne wyniki, gdy są ujęte w podwójny cudzysłów. Aby zobaczyć różnicę, utworzyliśmy star.sh, który wypisuje obie formy po kolei.

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

Uruchomiliśmy star.sh z trzema argumentami: a, "b c" i d.

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

"$@" przekazało trzy argumenty osobno, a "$*" połączyło je spacjami i przekazało jako jeden.

Dlatego gdy przekazujesz otrzymane argumenty bez zmian do innego polecenia, używaj "$@".

shift odrzuca $1 i przesuwa pozostałe argumenty o jedną pozycję do przodu. Stosuje się go w skryptach, które najpierw odczytują jedną opcję, a resztę traktują jako listę plików.

Utworzyliśmy shift.sh, który wypisuje $1 i $# przed shift i po nim.

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

Uruchomiliśmy shift.sh z trzema argumentami: -v, a.txt i b.txt.

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

Od dziesiątego argumentu liczbę ujmuje się w nawiasy klamrowe, jak ${10}. Utworzyliśmy ten.sh, który wypisuje obok siebie $10 i ${10}.

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

Do ten.sh przekazaliśmy 11 argumentów, od a do k.

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

$10 dało a0, czyli $1 z dopisanym 0, a ${10} dało dziesiąty argument, j.

🔲 Symulator argumentów wiersza poleceń

W symulatorze poniżej możesz przekazywać do args.sh cudzysłowy, symbole wieloznaczne i --, a także samodzielnie wpisywać polecenia, by zobaczyć, jak ls i getopt odczytują opcje oraz jak star.sh i shift.sh odbierają argumenty.

Symulator argumentów wiersza poleceńbash 5.1
[root@server work]#
To wirtualny serwer AlmaLinux działający tylko w przeglądarce; args.sh wypisuje otrzymane argumenty z numerami, a ↑↓ przywołuje wcześniej użyte polecenia.

🔢 FAQ & polecane treści

Górny limit łącznej długości argumentów i zmiennych środowiskowych sprawdza się poleceniem [getconf ARG_MAX]; na serwerze AlmaLinux 9.8 wynosił 2097152 bajty. Po jego przekroczeniu program się nie uruchamia i pojawia się błąd [Argument list too long]. Gdy plików jest bardzo dużo, nie przekazuj ich naraz symbolem wieloznacznym, tylko dziel je przez -exec w find lub xargs.

Argument (argument) to wartość faktycznie przekazywana przy uruchamianiu polecenia, a parametr (parameter) to nazwa, pod którą strona odbierająca otrzymuje tę wartość. W skrypcie powłoki parametry pozycyjne, takie jak $1 i $2, odbierają przekazane argumenty.

Wbudowane w bash getopts odczytuje po kolei krótkie opcje, takie jak -v. Długich opcji nie odczytuje, więc aby przyjmować też długie opcje, takie jak --verbose, najpierw uporządkuj argumenty użytym wyżej poleceniem getopt.

ℹ️ Informacja o linkach partnerskich
Treści tej witryny zawierają linki partnerskie. Jeśli odwiedzający kupi produkt lub usługę przez taki link, witryna otrzymuje od sprzedawcy prowizję. Kwota płacona przy tym przez kupującego (podczas promocji spada↓) nie wzrasta. Podane ceny, rabaty i dostępność odnoszą się do chwili pisania i mogą różnić się od stanu faktycznego, dlatego przed zakupem sprawdź je u sprzedawcy. Produkty wybieramy i oceniamy według własnych kryteriów, a prowizja nie wpływa ani na kolejność prezentacji, ani na treść ocen.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Wymagane pola są oznaczone *

Prove your humanity: 1   +   5   =