Являются ли переменные типа $ 0 и $ 1 переменными оболочки / среды?


17

Есть переменные в оболочке , как $0, $1, $2, $?и т.д.

Я попытался напечатать переменные оболочки и среды с помощью следующей команды:

set

Но этих переменных в списке не было.

Таким образом, в основном эти переменные не считаются переменными оболочки / среды, верно? (несмотря на то, что для их вывода вы должны предшествовать им $, как вы делаете это с переменными оболочки / окружения)


3
Позиционные параметры не являются переменными. Вы не export 3можете превратиться $3в переменную окружения. Вы не можете unset 3; и вы не можете присвоить $3новое значение, используя 3=val.
Каз

Ответы:


25

Переменные являются одним из трех различных вариантов параметров в оболочке.

  1. Переменная является параметром , чье имя является допустимым идентификатором оболочки; начинается с _или буквы, за которой следуют ноль или более букв, цифр или _.
  2. В позиционных параметрах пронумерованных параметры $1, $2...
  3. Все специальные параметры имеют односимвольные имена, и, кроме $0того, все они являются различными знаками препинания.

set отображает только переменные оболочки.

Подмножество переменных оболочки - это переменные среды, значения которых либо наследуются из среды при запуске оболочки, либо создаются путем установки exportатрибута с допустимым именем.


1
Обратите внимание, что setотображаются все параметры в zsh(не $ 1, $ 2 ... а $ *, $ @) и функции в bash и bosh. Некоторые оболочки, такие как ksh93 и более ранние версии dash, содержат env vars, которые не были сопоставлены с переменными оболочки. ( env 1=foo ksh -c setнапечатать 1=foo)
Стефан

11

Переменные среды и позиционные параметры

Прежде чем мы начнем обсуждать $INTEGERтип переменных, нам нужно понять, что они на самом деле и чем они отличаются от переменных среды. Такие переменные $INTEGERназываются позиционными параметрами. Это описано в стандарте POSIX (интерфейс переносимой операционной системы), раздел 2.1 (выделено мной):

  1. Оболочка выполняет функцию (см. «Команда определения функции»), встроенную (см. Специальные встроенные утилиты), исполняемый файл или сценарий, предоставляя имена аргументов в качестве позиционных параметров с номерами от 1 до n и имя команды (или в случае функции в сценарии - имя сценария) в качестве позиционного параметра пронумеровано 0 (см. Поиск и выполнение команд).

Напротив, такие переменные, как $HOMEи $PATHявляются переменными среды. Их определение описано в разделе 8 стандарта :

Переменные среды, определенные в этой главе, влияют на работу нескольких утилит, функций и приложений. Существуют и другие переменные среды, которые представляют интерес только для определенных утилит. Переменные среды, которые применяются только к одной утилите, определяются как часть описания утилиты.

Обратите внимание на их описание. Позиционные параметры должны появляться перед командой, т command positional_arg_1 positional_arg_2.... Е. Они предназначены для предоставления пользователю команды, что конкретно делать. Когда вы это сделаете echo 'Hello' 'World', он будет распечатать Helloи Worldстроки, потому что это позиционные параметры echo- вещи , которые вы хотите , echoчтобы работать дальше. И echoпостроен так, что он воспринимает позиционные параметры как строки, которые будут напечатаны (если только они не являются одним из необязательных флагов -n). Если вы сделаете это с другой командой, он может не понять, что HelloиWorldпотому что, возможно, он ожидает число. Обратите внимание, что позиционные параметры не «наследуются» - дочерний процесс не знает о позиционных параметрах родителя, если он явно не передан дочернему процессу. Часто вы видите, что позиционные параметры передаются с помощью сценариев-оберток, которые могут проверять уже существующий экземпляр команды или добавлять дополнительные позиционные параметры к реальной команде, которая будет вызвана.

Напротив, переменные среды предназначены для воздействия на несколько программ. Они являются переменными среды , потому что они установлены вне самой программы (подробнее об этом ниже). Определенные переменные среды, такие как HOMEили PATHимеют определенный формат, конкретное значение, и они будут означать то же самое для каждой программы. HOMEпеременная будет означать то же самое для внешней утилиты, например, для /usr/bin/findвашей оболочки (и, следовательно, для скрипта) - это домашний каталог с именем пользователя, под которым выполняется процесс. Обратите внимание, что переменные среды могут использоваться, например, для учета определенного поведения командыUIDПеременная окружения может использоваться, чтобы проверить, выполняется ли сценарий с правами root или нет, и соответственно перейти к определенным действиям. Переменные среды наследуются - дочерние процессы получают копию родительской среды. Смотрите также Если процессы наследуют родительскую среду, зачем нам экспорт?

Короче говоря, главное различие заключается в том, что переменные среды устанавливаются вне команды и не предназначены для изменения (обычно), в то время как позиционные параметры - это то, что должно обрабатываться командой, и они изменяются.


Не только концепция оболочки

В комментариях я заметил, что вы смешиваете терминал и оболочку и действительно рекомендовали бы вам прочитать о реальных терминалах, которые когда-то были физическими устройствами. В настоящее время «терминал», на который мы обычно ссылаемся, это окно с черным фоном и зеленым текстом на самом деле является программным процессом, процессом. Терминал - это программа, которая запускает оболочку, в то время как оболочка - это тоже программа, но та, которая читает то, что вы вводите для выполнения (то есть, если это интерактивная оболочка; неинтерактивные оболочки - это сценарии и sh -c 'echo foo'типы вызовов). Подробнее о снарядах здесь .

Это важное различие, но также важно признать, что терминал - это программа, и поэтому он придерживается тех же правил среды и позиционных параметров. Вы gnome-terminalкогда начали будет выглядеть на вашем SHELLпеременном окружение, и нерест соответствующей оболочки по умолчанию для вас, если вы не указали какую - либо другая команды с -e. Допустим, я изменил свою оболочку по умолчанию на ksh - kshвместо этого появится gnome-терминал bash. Это также пример того, как среда используется программами. Если я явно указать gnome-terminalс -eдля запуска конкретной оболочки - он будет делать это, но это не будет постоянным. Напротив, среда должна быть в основном неизменной (подробнее об этом позже).

Итак, как вы можете видеть, переменные окружения и позиционирования являются свойствами процесса / команды, а не просто оболочки. Когда дело доходит до сценариев оболочки, они также следуют модели, установленной языком программирования C. Возьмите, например, mainфункцию C, которая обычно выглядит

int main(int argc, char **argv)

где argcчисло аргументов командной строки и argvфактически массив параметров командной строки, а затем есть environфункция (в Linux man -e 7 environ) для доступа к таким вещам, как путь к домашнему каталогу пользователя, список каталогов, в PATHкоторых мы можем искать исполняемые файлы и т. д. Сценарии оболочки также моделируются аналогичным образом. В оболочечной терминологии, мы имеем позиционные параметры $1, $2и так далее, в то время как $#это количество позиционных параметров. Как насчет $0? Это имя самого исполняемого файла, который также смоделирован на языке программирования C - argv[0]будет именем вашего C "исполняемого файла". И это справедливо для большинства языков программирования и сценариев .

Интерактивные против неинтерактивных оболочек

Одна из вещей, которые я уже намекнул, - это различие между интерактивными и неинтерактивными оболочками . Подсказка, где вы вводите команды - это интерактивно, оно взаимодействует с пользователем. В отличие от этого, когда у вас есть сценарий оболочки или вы запускаете bash -c'', он не интерактивен.

И именно здесь различие становится важным. Оболочка, которую вы уже запускаете, представляет собой процесс, который был порожден позиционными параметрами (для bashоболочки входа в систему - один "..., чей первый символ нулевого аргумента - -, или тот, который запущен с параметром --login." ( Ссылка ) )

В отличие от сценариев и оболочек, запущенных с -cпараметром, могут использоваться аргументы $1и $2аргументы. Например,

$ bash -c 'echo $1; stat $2' sh 'Hello World' /etc/passwd
Hello World
  File: '/etc/passwd'
  Size: 2913        Blocks: 8          IO Block: 4096   regular file
Device: 801h/2049d  Inode: 6035604     Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2017-08-12 14:48:37.125879962 -0600
Modify: 2017-08-12 14:48:37.125879962 -0600
Change: 2017-08-12 14:48:37.137879811 -0600
 Birth: -

Обратите внимание, что я также использовал shтам, потому что небольшая особенность -cопции заключается в том, чтобы взять первый позиционный параметр и назначить его $0, в отличие от того, что обычно является именем программы.

Еще одна вещь, которую важно отметить, это то, что позиционные параметры - это то, что я называю «изменяемым». Обратите внимание, как мы впервые запустили bashсвои собственные позиционные параметры, но эти позиционные параметры стали параметрами для echoи stat. И каждая программа понимает это по-своему. Если мы передадим statстроку, Hello Worldа файла нет, Hello Worldэто приведет к ошибке; bashобрабатывает его как простую строку, но statожидает, что эта строка будет существующим именем файла. Напротив, все программы согласны с тем, что переменная окружения HOMEявляется каталогом (если программист не кодирует ее необоснованным образом).


Можем ли мы возиться с переменными среды и позиционными параметрами?

Технически, мы можем возиться с обоими, но мы не должны возиться с переменными среды, в то время как нам часто приходится предоставлять позиционные параметры. Мы можем запускать команды в оболочке с добавлением переменной, например:

$ hello=world bash -c 'echo $hello'
world

Мы также можем помещать переменные в среду просто используя export variable=valueоболочку или скрипт. Или мы можем запустить команду с полностью пустой средой с помощью env -c command arg1 arg2. Однако, как правило, не рекомендуется возиться с окружением, особенно с использованием переменных верхнего регистра или перезаписи уже существующих переменных окружения. Обратите внимание, что рекомендуется, хотя и не стандарт.

Для позиционных параметров способ их установки очевиден, просто добавьте их к команде, но также есть способы установить их другими способами , а также изменить список этих параметров с помощью shiftкоманды.

В заключение, цель этих двух различна, и они существуют по причине. Я надеюсь, что люди получили некоторое представление об этом ответе, и было весело читать его так же, как я написал этот ответ.


Примечание по команде set

Команда set, в соответствии с руководством ведет себя так (из руководства Bash, выделение добавлено):

Без параметров имя и значение каждой переменной оболочки отображаются в формате, который можно повторно использовать в качестве входных данных для установки или сброса текущих установленных переменных.

Другими словами, setрассматриваются переменные, специфичные для оболочки, например, некоторые из которых находятся в среде HOME. В отличие от команд, как envи printenvпосмотреть на фактическую переменную среды, с которой запускается команда. Смотрите также это .


«В интерактивной оболочке вы не можете ссылаться на $ 1, $ 2 и т. Д.» Существует ли прямая ссылка для этого? Я сталкивался со странными случаями, когда они устанавливаются в интерактивной оболочке, и я не уверен, будет ли это считаться «нестандартным» или нет.
user5359531

@ user5359531 Честно говоря, я не совсем уверен, откуда я это взял. Возможно, потому что когда я публиковал ответ в 2017 году, я, вероятно, ссылался на то, что вы не можете сделать что-то подобное, 1="foo"но позже я обнаружил, что по определению POSIX «слово» (то есть имя объекта, такого как переменная или функция) не может начинаться с цифрой (см. вопрос, который я разместил в теме ). Позиционные параметры, по-видимому, являются исключением из этого правила.
Сергей Колодяжный

@ user5359531 Я удалил часть ответа, так как он не особенно точен. Вы можете ссылаться $1, $2и так далее в интерактивной оболочке, и в том , что это делается с setкомандой, часто , чтобы обойти /bin/shограничения , не имеющие массивы. Спасибо, что обратили на это мое внимание. Я также собираюсь отредактировать ответ в течение следующих нескольких дней, так как он нуждается в некоторой дополнительной полировке и обновлении.
Сергей Колодяжный

Благодарю за разъяснение. Проблема , которую я имею, что с conda, при запуске source conda/bin/activate, он проверяет , является ли $1, $2и т.д., устанавливаются для того, чтобы определить , если он был запущен в качестве сценария с аргументами или нет. Это приводит к тому, что системы по какой-то причине устанавливаются в интерактивной среде. Я надеюсь выяснить, является ли это нестандартное поведение недостатком в системе для установки этих переменных в интерактивной среде или в программе для их использования, чтобы определить, был ли он запущен как скрипт.
user5359531

@ user5359531 Я бы посоветовал вам подать отчет об ошибке condaразработчикам или тому, кто является автором такого скрипта, так как проверка ${N}параметров - определенно неправильный путь. Есть вопросы по той же теме здесь и здесь , и более или менее переносимый способ, чтобы проверить , если ${0}это так же , как имя сценария, в то время как на bashсамом деле имеет переменные окружения для этой цели
Сергей Kolodyazhnyy

4

Эти $1, $2, $3, ..., ${10}, ${11}переменные называются позиционными параметрами и рассмотрены в Баш разделе руководства3.4.1

3.4.1 Позиционные параметры

Позиционный параметр - это параметр, обозначаемый одной или несколькими цифрами, отличными от одной цифры 0. Позиционные параметры назначаются из аргументов оболочки при его вызове и могут быть переназначены с помощью встроенной команды set. Позиционный параметр N может указываться как $ {N} или как $ N, когда N состоит из одной цифры. Позиционные параметры не могут быть назначены с помощью операторов присваивания. Встроенные команды set и shift используются для их установки и отмены (см. Команды встроенной оболочки). Позиционные параметры временно заменяются при выполнении функции оболочки (см. Функции оболочки).

Когда позиционный параметр, состоящий из более чем одной цифры, раскрывается, он должен быть заключен в фигурные скобки.

Что касается $?и $0эти специальные параметры рассматриваются в следующем разделе3.4.2

3.4.2 Специальные параметры

Оболочка обрабатывает несколько параметров специально. На эти параметры можно ссылаться только; присвоение им не допускается.

...

?

($?) Расширяется до состояния выхода последнего выполненного переднего плана конвейера.

0

($ 0) Расширяется до имени оболочки или сценария оболочки. Это устанавливается при инициализации оболочки. Если Bash вызывается с файлом команд (см. Shell Scripts), в качестве имени этого файла устанавливается $ 0. Если Bash запускается с параметром -c (см. «Вызов Bash»), тогда $ 0 устанавливается в качестве первого аргумента после строки, которая должна быть выполнена, если она есть. В противном случае ему присваивается имя файла, используемое для вызова Bash, как указано в нулевом аргументе.


4

$1, $2... являются позиционными параметрами , они не являются переменными, не говоря уже о переменных среды.

В Bourne, как оболочки терминологии, $somethingназывается параметром разложения (также охватывает ${something#pattern}и более в некоторых оболочках , как ${array[x]}, ${param:offset}, ${x:|y}и многие операторы расширения более).

Есть разные виды параметров:

  • переменные, как $foo,$PATH
  • позиционные параметры ( $1, $2... аргументы, полученные вашим скриптом)
  • другие специальные параметры , такие как $0, $-, $#, $*, $@, $$, $!, $?...

Имена переменных в Bourne, такие как оболочки, должны начинаться с одного алфавитного символа (любой, распознаваемый языковым стандартом или ограниченный a-zA-Z в зависимости от оболочки) и подчеркивания, за которым следует ноль или более буквенно-цифровых символов или подчеркиваний.

В зависимости от оболочки переменные могут иметь разные типы (скаляр, массив, хэш) или заданные определенные атрибуты (только для чтения, экспорт, строчные буквы ...).

Некоторые из этих переменных создаются оболочки или имеют особое значение для оболочки (например $OPTIND, $IFS, $_...)

Переменные оболочки, имеющие атрибут экспорта , автоматически экспортируются как переменные среды в команды, которые выполняет оболочка.

Переменная окружения - это понятие, отдельное от переменных оболочки. Экспорт переменной оболочки - это не единственный способ передать переменную среды для выполнения команды.

VAR=foo
export VAR
printenv VAR

передаст VARпеременную окружения printenvкоманде (которую мы говорим, чтобы напечатать ее содержимое), но вы также можете сделать:

env VAR=foo printenv VAR

или:

perl -e '$ENV{VAR}="foo"; exec "printenv", "VAR"'

например.

Переменные окружения могут иметь любое имя (может содержать любой символ, но =может даже быть пустым). Не рекомендуется давать имя, которое не совместимо с именем переменной оболочки типа Борна, переменной среды, но это возможно:

$ env '#+%=whatever' printenv '#+%'
whatever

Оболочки будут отображать переменные среды, которые они получают, в переменные оболочки только для тех переменных среды, чьи имена являются допустимыми переменными оболочки (а в некоторых оболочках игнорируют некоторые специальные, например $IFS).

Итак, пока вы сможете передавать 1переменную окружения в команду:

$ env '1=whatever' printenv 1
whatever

Это не означает, что вызов оболочки с этой переменной окружения установит значение $1параметра:

$ env '1=whatever' sh -c 'echo "$1"' script-name foo bar
foo

3

Нет, это параметры скрипта. Например, если вы называете свой скрипт как:

mynicescript.sh one two three

тогда внутри скрипта эти параметры будут доступны как

$1 = one
$2 = two
$3 = three

а $ 0 - это имя самого скрипта.

Поэтому, когда вы находитесь за пределами скрипта, эти переменные недоступны (кроме $ 0, который отображает / bin / bash - сама оболочка).


«Поэтому, когда вы находитесь за пределами скрипта, эти переменные недоступны», что вы подразумеваете под «вне скрипта» , потому что я могу видеть значения этих переменных в моем терминале.
user7681202

2
@ user7681202: Какие из них вы видите в своем терминале? $0будет указывать на ваш текущий процесс терминала (вероятно, bash) и $?является просто кодом завершения последнего процесса.
Jesse_b

Я пытался запустить gnome-terminalс аргументами ( gnome-terminal Hello World). Я мог видеть $0, но я не мог видеть $1и $2.
user7681202

@Jesse_b Спасибо, я добавил в ответ содержимое $ 0.
Ярослав Кучера

1
@ user7681202 gnome-терминал - это не оболочка, а эмулятор терминала (например, xterm, konsole и т. д.). Оболочка работает внутри терминала и может быть bash / sh / zsh / tcsh и многими другими. Сценарий представляет собой исполняемый файл с соответствующим заголовком (например, #! / Bin / bash) и содержимым, интерпретируемым оболочкой, указанной в маске. Обычно используется суффикс .sh
Ярослав Кучера
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.