Bash 为什么我要在shell脚本中使用declare/typeset,而不仅仅是X=y?

Bash 为什么我要在shell脚本中使用declare/typeset,而不仅仅是X=y?,bash,Bash,我最近遇到了一个shell脚本,它使用 declare--FOO=”“这显然是在非bash shell中拼写为typeset--FOO=”“ 为什么我要这样做而不是简单的FOO=”“或导出FOO declare -i cnt=0 声明一个仅限整数的变量,该变量的运算速度更快,并且始终在算术上下文中求值 declare -l lower="$1" 声明一个variabl,该variabl自动将放入其中的任何内容小写,而无需访问任何特殊语法 declare -r unchangeable="$c

我最近遇到了一个shell脚本,它使用
declare--FOO=”“
这显然是在非bash shell中拼写为
typeset--FOO=”“

为什么我要这样做而不是简单的
FOO=”“
导出FOO

declare -i cnt=0
声明一个仅限整数的变量,该变量的运算速度更快,并且始终在算术上下文中求值

declare -l lower="$1"
声明一个variabl,该variabl自动将放入其中的任何内容小写,而无需访问任何特殊语法

declare -r unchangeable="$constant"
声明一个只读变量


查看一些有用的讨论-您可能不经常需要这些东西,但是如果您不知道可用的东西,您可能会比您应该做的更努力。

使用
declare
最重要的目的是控制作用域,或者使用无法访问的数组类型


使用函数局部变量 举个例子:

print_dashes() { for (( i=0; i<10; i++; do printf '-'; done; echo; }

while read -p "Enter a number: " i; do
  print_dashes
  echo "You entered: $i"
done
…现在
i
是本地的,因此新分配的值不会超过其调用时间


显式声明全局变量 相反,有时您希望声明一个全局变量,并向代码的读者清楚地表明您是有意这样做的,或者在这样做的同时也将某些东西声明为数组(或者
declare
会隐式指定全局状态)。你也可以这样做:

myfunc() {
  declare arg                     # make arg local
  declare -g -A myfunc_args_seen  # make myfunc_args_seen a global associative array
  for arg; do
    myfunc_args_seen["$arg"]=1
  done

  echo "Across all invocations of myfunc, we have seen the following arguments:"
  printf ' - %q\n' "${!myfunc_args_seen[@]}"
}

声明关联数组 可以只分配普通shell数组:
my\u arr=(一二三)

但是,关联数组的情况并非如此,它们被设置为字符串。对于这些,您需要声明:

declare -A my_arr=( ["one"]=1 ["two"]=2 ["three"]=3 )

使用
声明
排版
和/或
只读
的一个重要原因是代码划分和重用(即封装)。您可以在一个脚本中编写代码,其他脚本可以从中获取代码。 (请注意,声明的/typeset/readonly常量/变量/函数在子shell中失去其“只读性”,但当子脚本源于其定义脚本时,它们会保留它,因为源将脚本加载到当前shell中,而不是子shell。)

由于源代码将代码从脚本加载到当前shell中,因此名称空间将重叠。为了防止子脚本中的变量被其父脚本覆盖(反之亦然,具体取决于脚本的来源和使用的变量),可以声明一个只读变量,这样它就不会被覆盖


您必须注意这一点,因为一旦您声明某个只读内容,就无法取消其设置,因此您不希望声明可能在另一个脚本中自然重新定义的只读内容。例如,如果您正在编写一个具有日志函数的通用库,则可能不希望在名为
warn
error
info
的函数上使用
typeset-f
,因为其他脚本可能会使用该名称创建自己的类似日志函数。在这种情况下,实际上标准做法是在函数、变量和/或常量名称前面加上定义脚本的名称,然后将其设置为只读(例如,
my_script\u warn
my_script\u error
,等等)。这将保留定义脚本中代码逻辑中使用的函数、变量和/或常量的值,这样它们就不会被源脚本覆盖而意外失败。

在什么上下文中?在函数内部,除非声明,否则一切都是全局的,因此如果不声明变量,则泄漏scope.BTW,
declare--FOO=”“
相当于
FOO=“”
,而不是
export FOO=“”
;后者(将
FOO
存储为一个环境变量,以便
getenv()
调用可以在任何子进程中检索它…从而耗尽操作系统分配给环境的有限的每个进程空间,而不是在非常有限的堆内存中)是
declare-x FOO=“
declare
严格来说是巴什主义;虽然这个问题被标记为
sh
,但是基线POSIX sh不支持它。实际上,我不太鼓励
declare-I
/
declare-l
/等等;它们增加了进行代码读取或审阅时必须掌握的上下文数量,以便正确理解操作。有些人发现,读取变量总是小写的(尤其是在名称合理的情况下)要比读取小写扩展语法容易得多。每个人都有自己的。我也喜欢它的
local
同义词。你知道,我实际上认为这是进行关联的唯一方法,但是我的git bash模拟让我直接赋值,而不必预先声明。这让我很惊讶。我在CentOS 7虚拟机上测试了它,它也工作了。想知道它现在有多便携吗?很有趣。解释器默认使用索引数组,并将我的密钥全部视为0,我已经被它咬了很多次,因此我希望在信任任何自动行为之前有非常明确的文档。:)我更喜欢
声明
即使是简单的初始化,除非有理由不声明——但这只是工作中的个人强迫症。(我实际上认为它应该被称为“CDO”,所以字母应该按字母顺序排列。)我完全同意,如果没有非常具体的理由,声明本地人是正确的;我要谨慎得多的是,它用标志声明它们,让事情在用户背后发生(这样以后的语句可能会产生令人惊讶的副作用,声明必须考虑让读者准确理解后面的行)。
declare -A my_arr=( ["one"]=1 ["two"]=2 ["three"]=3 )