Предоставляет ли bash поддержку для использования указателей?

12

Простой вопрос Есть ли в оболочке bash поддержка использования указателей при написании сценария оболочки?

Я знаком с нотацией расширения ${var[@]}при итерации по массиву $var, но не ясно, использует ли он указатели для итерации по индексам массива. Предоставляет ли bash доступ к адресам памяти, как на других языках?

Если bash не поддерживает использование указателей, что делают другие оболочки?

111 ---
источник

Ответы:

28

Указатель (на область памяти ) не очень полезен для чего-либо более высокого уровня, чем C, будь то Python или оболочка. Ссылки на объекты, конечно, полезны в языках высокого уровня, возможно, даже необходимы для построения сложных структур данных. Но в большинстве случаев мышление с точки зрения адресов памяти является слишком низким уровнем, чтобы быть очень полезным.

В Bash (и других оболочках) вы можете получить значения элементов массива с помощью ${array[index]}нотации, назначить их с помощью array[index]=...и получить количество элементов в массиве с помощью ${#array[@]}. Выражение внутри скобок является арифметическим выражением. В качестве готового примера мы можем добавить постоянный префикс для всех элементов массива:

for ((i=0 ; i < ${#array[@]} ; i++ )) ; do
    array[i]="foo-${array[i]}"
done

(Если бы мы заботились только о значениях, а не об индексах, все for x in "${array[@]}" ; do...было бы хорошо.)

С ассоциативными или разреженными массивами числовой цикл не имеет особого смысла, но вместо этого нам нужно было бы выбирать ключи / индексы массива ${!array[@]}. Например

declare -A assoc=([foo]="123" [bar]="456")
for i in "${!assoc[@]}" ; do 
    echo "${assoc[$i]}"
done 

В дополнение к этому, у Bash есть два способа косвенно указать на другую переменную:

  • косвенное расширение , используя в ${!var}синтаксис , который использует значение переменной, имя в var, и
  • namerefs , которые должны быть созданы с помощью declareвстроенной функции (или kshсовместимого синонима typeset). declare -n ref=varделает refссылку на переменную var.

Namerefs также поддерживают индексацию, в том случае, если у нас есть, arr=(a b c); declare -n ref=arr;то ${ref[1]}расширимся до b. Использование ${!p[1]}вместо этого принимает pв качестве массива и ссылается на переменную, названную его вторым элементом.

В Bash namerefs буквально означает, что ссылки по имени и использование nameref из функции будут использовать локальное значение именованной переменной. Это напечатает local value of var.

#!/bin/bash
fun() {
        local var="local value of var"
        echo "$ref";
}
var="global var"
declare -n ref=var
fun

BashFAQ также имеет более длинную статью о косвенности .

ilkkachu
источник
2
косвенное направление весьма полезно в языках более высокого уровня. например ссылки в Perl. Они не совпадают с указателями C, но они выполняют ту же основную функцию. Даже bash может обращаться к переменным косвенно ... но IMO, если вы начнете писать код, который значительно использует эту функцию, вам лучше начать с нуля с Perl или чего-то еще. См. Также mywiki.wooledge.org/BashFAQ/006
cas
2
@ CAS, о, конечно. Но, вероятно, лучше думать о них как об объектах , а не адресах памяти. (Даже в C, есть связанный тип.) Я хотел отметить и косвенное расширение, и namerefs, но у меня не было времени сделать это немедленно.
ilkkachu
Вероятно, стоит указать, что пример цикла for написан более естественно, for foo in "${array[@]}" ; do ... doneесли вам не нужен индекс для других целей.
Уилл Кроуфорд
@WillCrawford, точка. отредактировал пример и отметил.
ilkkachu
9

Нет, bashне имеет "указателей", но есть ссылки:

$ spam="fred"
$ declare -n tripe=spam
$ echo $tripe
fred
$ tripe=juki
$ echo $spam
juki

Со bashстраницы руководства :

Переменной может быть назначен атрибут nameref с помощью опции -n встроенных команд declareили localдля создания nameref или ссылки на другую переменную. Это позволяет косвенно манипулировать переменными. Всякий раз, когда на переменную nameref ссылаются, назначают, отменяют или изменяют ее атрибуты (кроме использования или изменения самого атрибута nameref), операция фактически выполняется над переменной, указанной значением переменной nameref. Nameref обычно используется в функциях оболочки для ссылки на переменную, имя которой передается в качестве аргумента функции. Например, если имя переменной передается функции оболочки в качестве первого аргумента, выполняется

declare -n ref=$1

внутри функции создается переменная nameref ref, значением которой является имя переменной, переданное в качестве первого аргумента. Ссылки и назначения для ref и изменения к его атрибутам рассматриваются как ссылки, назначения и модификации атрибутов для переменной, имя которой было передано как $ 1. Если управляющая переменная в цикле for имеет атрибут nameref, список слов может быть списком переменных оболочки, и ссылка на имя будет установлена ​​для каждого слова в списке, в свою очередь, при выполнении цикла. Переменным массива нельзя присвоить атрибут nameref. Тем не менее, переменные nameref могут ссылаться на переменные массива и подписанные переменные массива. Namerefs может быть сброшен с помощью опции -n unsetвстроенной функции. В противном случае, еслиunset выполняется с именем переменной nameref в качестве аргумента, переменная, на которую ссылается переменная nameref, будет не установлена.

hackerb9
источник
4

Нет, оболочки не используют «указатели» (как это понимается в C).

Массивы могут использовать индексы: echo "${array[2]}"но @в вашем примере это не «указатель». Это способ выразить «список значений массива». Что-то, что понимает парсер оболочки. Похож на способ:

$ echo "$@"

расширяется на весь список «Позиционные параметры».

NotAnUnixNazi
источник
2

Хотя целочисленные индексированные массивы bash могут быть определены и доступны итеративно, вот так;

declare -a obj
obj+=("val1")
obj+=("val2")

for item in ${obj[@]}; do
  echo "${obj[${item}]} ${item}"
done

Ассоциативные или строковые индексированные массивы в bash требуют следующего итеративного определения;

declare -A obj
obj["key1"]="val1"
obj["key2"]="val2"

for item in ${!obj[@]}; do
  echo "${obj[${item}]} ${item}"
done

Ответить на вопрос относительно указателей и использования одного из bash; внутренняя функциональность скомпилированного двоичного файла действительно использует указатели на память, выделенную в стеке, и предоставляет аналогичные функциональные возможности с использованием eval. См. [Косвенные ссылки] http://tldp.org/LDP/abs/html/ivr.html ).

Там будут драконы; использование evalследует использовать с осторожностью из-за последствий для безопасности

jas-
источник