리눅스 명령행 인자: 명령어 옵션과 따옴표, 옵션 끝 표시, $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]
$#Количество аргументов, $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" 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}"

Мы передали ten.sh 11 аргументов, от a до k.

./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.

Встроенная в bash команда getopts читает короткие опции вроде -v по одной. Длинные опции она не читает, поэтому, чтобы принимать и длинные опции вроде --verbose, сначала упорядочьте аргументы командой getopt, использованной выше.

🔗 Рекомендуемые материалы
Раскрытие и экранирование в оболочке [Linux] 6 видов раскрытия в оболочке и 3 вида экранирования Статья, в которой через echo проверено, как оболочка прямо перед запуском команды меняет аргументы раскрытием путей, тильды, арифметики и фигурных скобок и как двойные кавычки, одинарные кавычки и обратная косая черта останавливают раскрытие. Читать → Скрипт оболочки (Shell Script)

ℹ️ О партнёрских ссылках
Материалы этого сайта содержат партнёрские ссылки. Если посетитель покупает товар или услугу по такой ссылке, сайт получает комиссию от продавца. При этом сумма, которую платит покупатель (при акционных скидках сумма снижается ↓), не увеличивается. Указанные цены, скидки и наличие актуальны на момент написания и могут отличаться, поэтому перед покупкой уточняйте их у продавца. Товары выбираются и оцениваются по нашим собственным критериям, а комиссия не влияет ни на порядок, ни на содержание обзоров.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Докажите, что вы человек: 9   +   1   =