15

Simple question. Does the bash shell have any support for using pointers when writing a shell script?

I am familiar with expansion notation, ${var[@]} when iterating over the array $var, but it is not clear this is utilizing pointers to iterate over the array indices. Does bash provide access to memory addresses like other languages?

If bash does not support using pointers, what other shells do?

111---
  • 4,516
  • 3
  • 30
  • 52

4 Answers4

29

A pointer (to a location of memory) is not really a useful concept in anything higher-level than C, be it something like Python or the shell. References to objects are of course useful in high-level languages, perhaps even necessary for building complex data structures. But in most cases thinking in terms of memory addresses is too low level to be very useful.

In Bash (and other shells), you can get the values of array elements with the ${array[index]} notation, assign them with array[index]=... and get the number of elements in the array with ${#array[@]}. The expression inside the brackets is an arithmetic expression. As a made-up example, we could add a constant prefix to all array members:

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

(If we only cared about the values, and not the indexes, just for x in "${array[@]}" ; do... would be fine.)

With associative or sparse arrays, a numerical loop doesn't make much sense, but instead we'd need to fetch the array keys/indexes with ${!array[@]}. E.g.

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

In addition to that, Bash has two ways to point indirectly to another variable:

  • indirect expansion, using the ${!var} syntax, which uses the value of the variable whose name is in var, and
  • namerefs, which need to be created with the declare builtin (or the ksh-compatible synonym, typeset). declare -n ref=var makes ref a reference to the variable var.

Namerefs also support indexing, in that if we have arr=(a b c); declare -n ref=arr; then ${ref[1]} will expand to b. Using ${!p[1]} would instead take p as an array, and refer to the variable named by its second element.

In Bash, namerefs are literally that, references by name, and using a nameref from inside a function will use the local value of the named variable. This will print local value of var.

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

BashFAQ has a longer article on indirection, too.

ilkkachu
  • 138,973
  • 2
    indirection is quite useful in higher level languages. e.g. references in perl. They're not the same as C pointers but they serve the same basic function. Even bash can access variables indirectly...but IMO if you start writing code that makes significant use of the feature, you're better off starting from scratch with perl or something. See also http://mywiki.wooledge.org/BashFAQ/006 – cas Dec 28 '17 at 15:53
  • 2
    @cas, oh, absolutely. But it's probably better to think of them as pointing to objects, rather than memory addresses. (Even in C, there's a type involved.) I meant to note both indirect expansion and namerefs, but didn't have the time to do it immediately. – ilkkachu Dec 28 '17 at 17:32
  • It's probably worth pointing out that the for-loop example is more naturally written for foo in "${array[@]}" ; do ... done unless you need the index for other purpose(s). – Will Crawford Dec 29 '17 at 13:36
  • @WillCrawford, point. edited the example and noted. – ilkkachu Dec 29 '17 at 13:52
11

No, bash doesn't have "pointers", but it does have references:

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

From the bash man page:

A variable can be assigned the nameref attribute using the -n option to the declare or local builtin commands to create a nameref, or a reference to another variable. This allows variables to be manipulated indirectly. Whenever the nameref variable is referenced, assigned to, unset, or has its attributes modified (other than using or changing the nameref attribute itself), the operation is actually performed on the variable specified by the nameref variable's value. A nameref is commonly used within shell functions to refer to a variable whose name is passed as an argument to the function. For instance, if a variable name is passed to a shell function as its first argument, running

declare -n ref=$1

inside the function creates a nameref variable ref whose value is the variable name passed as the first argument. References and assignments to ref, and changes to its attributes, are treated as references, assignments, and attribute modifications to the variable whose name was passed as $1. If the control variable in a for loop has the nameref attribute, the list of words can be a list of shell variables, and a name reference will be established for each word in the list, in turn, when the loop is executed. Array variables cannot be given the nameref attribute. However, nameref variables can reference array variables and subscripted array variables. Namerefs can be unset using the -n option to the unset builtin. Otherwise, if unset is executed with the name of a nameref variable as an argument, the variable referenced by the nameref variable will be unset.

hackerb9
  • 1,569
4

No, shells do not use "pointers" (as understood in C).

Arrays could use indices: echo "${array[2]}" but the @ in your example is not really a "pointer". It is a way to express "the list of array values". Something that the shell parser understand. Similar to the way a:

$ echo "$@"

expands to all the "Positional Parameters" list.

2

While bash integer indexed arrays can be defined & accessed iteratively like so;

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

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

Associative or string based indexed arrays in bash require the following iterative definition;

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

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

To answer the question regarding pointers and using one from bash; the internal functionality of the compiled bash binary does indeed make use of pointers to memory allocated on the stack and does expose similar functionality with the use of eval. See [indirect references]http://tldp.org/LDP/abs/html/ivr.html)

There be dragons; use of eval should be used with caution due to security implications

jas-
  • 868