Какова структура данных $ @ в оболочке?

13

Мы обычно используем $@для представления всех аргументов, кроме $ 0. Однако я не знаю, что такое структура данных $@.

Почему $*при включении в двойные кавычки он ведет себя иначе , может кто-нибудь дать мне объяснение на уровне переводчика?

Его можно повторять в цикле for, поэтому он выглядит как массив. Тем не менее, он также может быть полностью повторен с простым echo $@, если это массив, будет показан только первый элемент. Из-за ограничений оболочки я не могу написать больше экспериментального кода для его выполнения.

Разница между этим постом : этот пост показывает, как $@ведет себя иначе, чем $*. Но меня интересует тип данных $@. Shell как интерпретирующий язык, такой как Python, должен представлять данные в соответствии с рядом фундаментальных типов. Или, другими словами, я хочу знать, как $ @ хранится в памяти компьютера.

Это строка, многострочная строка или массив?

Если это уникальный тип данных, возможно ли определить пользовательскую переменную как экземпляр этого типа?

davmos
источник
1
Возможный дубликат В чем разница между $ * и $ @?
Haxiel
@Хаксиэль, я так не думаю, я написал их различие внизу своего поста.
Давмос
Вы бы лучше обслужили, проверив разницу в результатах с printf '%s\n' "$@"и printf '%s\n' "$*". echoУтилита просто выводит свои аргументы, независимо от того , если они являются один или много. Оба являются массивами (строк), но они ведут себя по-разному, если заключены в двойные кавычки. Если бы любая из них была многострочной строкой, то они не смогли бы хранить многострочные строки (что они могут). Непонятно, какую проблему вы пытаетесь решить.
Кусалананда
2
Ваш вопрос эквивалентен вопросу о том, что представляет собой @varпеременная в Perl, с точки зрения ее базового хранилища. С точки зрения обычной Perl-программы это не имеет большого значения, кроме того, что она доступна в виде массива / списка (и того факта, что существуют контексты, в которых ожидается список).
Кусалананда

Ответы:

16

Это началось как взломать оболочку Борна. В оболочке Bourne разделение слов IFS было выполнено (после токенизации) для всех слов в контексте списка (аргументы командной строки или слова, в которых forциклы циклы). Если у тебя есть:

IFS=i var=file2.txt
edit file.txt $var

Это вторая линия будет tokenised в 3 -х слов, $varбудет расширен, и раскол + Glob будет сделано на всех трех слов, так что вы в конечном итоге работает edс t, f, le.txt, f, в le2.txtкачестве аргументов.

Цитирование частей этого предотвратит разделение + глобус. Оболочка Bourne изначально помнила, какие символы были заключены в кавычки, установив для них внутренний 8-й бит (это изменилось позже, когда Unix стал чистым 8-битным, но оболочка все еще сделала нечто похожее, чтобы запомнить, какой байт был заключен в кавычки).

Оба $*и $@были объединением позиционных параметров с пробелом между ними. Но была особая обработка, $@когда внутри двойных кавычек. Если $1содержится foo barи $2содержится baz, "$@"будет расширен до:

foo bar baz
^^^^^^^ ^^^

^символом выше, указывающим, для какого из символов установлен 8-й бит). Где первый пробел был заключен в кавычки (был установлен 8-й бит), но не второй (тот, который был добавлен между словами).

И это разделение IFS, которое заботится о разделении аргументов (при условии, что символ пробела находится в том виде, $IFSкак он есть по умолчанию). Это похоже на то, как $*в его предшественнике была расширена оболочка Mashey (сама основанная на оболочке Thomson, тогда как оболочка Bourne была написана с нуля).

Это объясняет , почему в Bourne оболочки вначале "$@"будет расширяться в пустую строку вместо ничего вообще , когда список позиционных параметров был пуст (вы должны были работать на него с ${1+"$@"}), поэтому он не держать пустые позиционные параметры и почему "$@"Didn не работает, когда $IFSне содержит пробела.

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

Оболочка Korn (на которой основана спецификация POSIX) изменила это поведение несколькими способами:

  • Разбиение IFS выполняется только в результате расширений без кавычек (не для буквальных слов, как editили file.txtв примере выше)
  • $*и $@соединяются с первым символом $IFSили пробелом, когда $IFSон пуст, за исключением того, что для заключенных в кавычки "$@"этот соединитель не заключен в кавычки , как в оболочке Bourne, а для заключенных в кавычки, "$*"когда IFSон пуст, позиционные параметры добавляются без разделителя.
  • она была добавлена поддержка массивов, и ${array[@]} ${array[*]}напоминает Борна $*и $@но начиная с Indice 0 вместо 1, и редкими (больше как ассоциативные массивы) , что означает на $@самом деле нельзя рассматривать как массив КШ (сравните с csh/ rc/ zsh/ fish/ , yashгде $argv/ $*нормальны массивы).
  • Пустые элементы сохраняются.
  • "$@"когда $#0 - теперь расширяется в пустую строку вместо пустой, "$@"работает, когда $IFSне содержит пробелов, кроме случаев, когда IFSпусто. $*Без кавычек без подстановочных знаков расширяется до одного аргумента (где позиционные параметры объединяются с пробелом), когда $IFSон пуст.

ksh93 исправил оставшиеся проблемы выше. В ksh93 $*и $@расширяется до списка позиционных параметров, разделенных независимо от значения $IFS, и затем далее split + globbed + brace-раскрытый в контекстах списка, $*соединенный с первым байтом (не символом) $IFS, "$@"в контекстах списка расширяется до списка позиционных параметров, независимо от значения $IFS. В не-списке контекста, как в var=$@, $@объединяется с пробелом независимо от значения $IFS.

bashМассивы разработаны после ksh. Различия:

  • без скобок - расширять без расширения без кавычек
  • первый символ $IFSвместо байта
  • некоторые различия в угловых случаях, например, расширение $*без кавычек в контексте без списка, когда $IFSпусто.

Хотя спецификация POSIX раньше была довольно расплывчатой, теперь она более или менее определяет поведение bash.

Это отличается от обычных массивов в том kshили ином bash:

  • Индексы начинаются с 1, а не с 0 (за исключением того, "${@:0}"что включает $0(не позиционный параметр, а в функциях дает вам имя функции или нет, в зависимости от оболочки и способа определения функции)).
  • Вы не можете назначать элементы индивидуально
  • это не редкость, вы не можете удалить элементы индивидуально
  • shift может быть использован.

В zshили yashгде массивах являются нормальными массивами (не редко, индексы начинаются в одном , как и во всех других оболочках , но КШ / Баше), $*рассматриваются как нормальный массив. zshимеет $argvпсевдоним для этого (для совместимости с csh). $*аналогично $argvили ${argv[*]}(аргументы, объединенные с первым символом, $IFSно все еще выделенные в контекстах списка). "$@"как "${argv[@]}"или "${*[@]}"}подвергается специальной обработке в стиле Korn.

Стефан Шазелас
источник
8

Однако я не знаю, что такое структура данных $@.

Это специальный параметр, который распространяется на значения позиционных параметров ... Но это придирчиво к терминологии.

Мы можем рассматривать позиционные параметры как части $@, поэтому он имеет ряд различных элементов ( $1, $2...), к которым можно обращаться независимо и которые называются последовательными натуральными числами. Это делает его чем-то, что обычно называют массивом.

Хотя синтаксис немного странный и даже ограниченный. Нет способа изменить отдельный элемент массива по отдельности. Вместо этого все должно быть установлено сразу. (Вы можете использовать, set -- "$@" fooчтобы добавить значение или set -- "${@:1:2}" foo "${@:3}"добавить значение в середине. Но вы в обоих случаях должны выписать весь результирующий список.)

Почему он ведет себя по-разному, $*когда включен в двойные кавычки,

Потому что они определены, чтобы вести себя по-другому.

Тем не менее, он также может быть полностью повторен с простым echo $@, если это массив, будет показан только первый элемент.

Если вы имеете в виду тот факт, что a=(foo bar asdf); echo $aбудет выводиться просто foo, то это в основном причуды синтаксиса оболочки и тот факт, что именованные массивы в стиле ksh были созданы позже, чем позиционные параметры и $@. Plain $a- это то же самое, ${a[0]}что и обратно-совместимое значение одного скалярного значения, независимо от того, aявляется ли он массивом или простой скалярной переменной.

@Знак со ссылкой на весь список был повторно с именованными массивами в том , что "${a[@]}"это способ получить весь список. По сравнению с именованными массивами $@ненужные скобки и скобки и имя просто пропускаются.

Или, другими словами, я хочу знать, как $@хранится в памяти компьютера.

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

Это строка, многострочная строка или массив?

Массив, в основном. Хотя он отличается от именованных массивов в стиле ksh, поскольку они могут иметь произвольные неотрицательные целые числа в качестве индексов, а не только последовательные, как в случае с $@. (То есть именованный массив может быть разреженным и иметь, например, индексы 1, 3а также 4с 0и 2без. Это невозможно с позиционными параметрами.)

Это не единственная строка, поскольку она может быть расширена до отдельных элементов, и вызов строк элементов также не является правильным, поскольку любая регулярная переменная или один из позиционных параметров (элементов $@) также могут содержать символы новой строки.

Если это уникальный тип данных, возможно ли определить пользовательскую переменную как экземпляр этого типа?

Но именованные массивы в любом случае, вероятно, более полезны.

ilkkachu
источник
1
+1. TL: DR $@не структура данных, это одна из немногих функций / операторов для расширения структуры данных позиционных параметров.
Питер Кордес