bash是否支持使用指针?


12

简单的问题。bash shell在编写shell脚本时是否支持使用指针?

${var[@]}在数组上进行迭代时,我对扩展符号很熟悉$var,但是目前尚不清楚这是利用指针在数组索引上进行迭代。bash是否像其他语言一样提供对内存地址的访问?

如果bash不支持使用指针,那么其他shell呢?

Answers:


28

在比C更高级的任何级别中,指针(指向内存的位置)并不是一个有用的概念,无论它是Python还是Shell。对对象的引用在高级语言中当然有用,甚至可能对于构建复杂的数据结构也是必需的。但是在大多数情况下,根据内存地址进行思考的程度太低而无法使用。

在Bash(和其他shell)中,您可以使用${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-compatible同义词,typeset)。declare -n ref=var使得ref到的变量的引用var

Namerefs还支持索引编制,因为如果有的arr=(a b c); declare -n ref=arr;话,${ref[1]}它将扩展为b${!p[1]}相反,using 将p作为数组,并引用由其第二个元素命名的变量。

在Bash中,名称引用实际上是按名称引用,并且在函数内部使用名称引用将使用命名变量的局部值。这将打印local value of var

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

BashFAQ也有一篇关于间接的较长文章


2
间接语言在高级语言中非常有用。例如在Perl中的引用。它们与C指针不同,但它们具有相同的基本功能。甚至bash都可以间接访问变量 ...但是,如果IMO开始编写大量使用该功能的代码,则最好从头开始使用perl或其他东西。又见mywiki.wooledge.org/BashFAQ/006
中科院

2
@cas,哦,绝对。但是最好将它们视为指向对象,而不是指向内存地址。(即使在C语言中,也涉及到一种类型。)我的意思是要同时注意间接扩展和名称引用,但没有时间立即进行。
ilkkachu

可能值得指出的是,for foo in "${array[@]}" ; do ... done除非您需要将索引用于其他目的,否则for循环示例将更自然地编写。
Will Crawford

@WillCrawford,要点。编辑了示例并进行了注释。
ilkkachu

9

不,bash没有“指针”,但是有引用:

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

bash手册页:

可以使用-n选项declarelocal内置命令创建一个nameref或对另一个变量的引用,为该变量分配nameref属性。这允许变量被间接操纵。每当对nameref变量进行引用,分配,设置或修改其属性时(除了使用或更改nameref属性本身之外),实际上都会对nameref变量的值所指定的变量执行操作。在外壳函数中,通常使用nameref来引用变量,该变量的名称作为参数传递给该函数。例如,如果将变量名作为第一个参数传递给shell函数,则运行

declare -n ref=$1

函数内部将创建一个nameref变量ref,其值是作为第一个参数传递的变量名称。对ref的引用和赋值以及对其属性的更改被视为对名称作为$ 1传递的变量的引用,赋值和属性修改。如果for循环中的控制变量具有nameref属性,则单词列表可以是shell变量列表,并且将在执行循环时依次为列表中的每个单词建立名称引用。数组变量不能被赋予nameref属性。但是,nameref变量可以引用数组变量和下标数组变量。可以使用unset内置的-n选项取消设置Nameref 。否则,如果unset 如果以nameref变量的名称作为参数执行,则nameref变量引用的变量将不会设置。


4

不,shell不使用“指针”(如C所了解)。

数组可以使用索引:echo "${array[2]}"但是@在您的示例中,并不是真正的“指针”。这是表达“数组值列表”的一种方式。Shell解析器可以理解的内容。类似于以下方式:

$ echo "$@"

扩展到所有“位置参数”列表。


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中的指针的问题;bash二进制编译器的内部功能确实确实利用了指向分配在堆栈上的内存的指针,并且确实通过使用eval。参见[间接参考] http://tldp.org/LDP/abs/html/ivr.html

有龙;eval出于安全考虑,应谨慎使用

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.