AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 778330
Accepted
RonJohn
RonJohn
Asked: 2024-06-14 15:46:23 +0800 CST2024-06-14 15:46:23 +0800 CST 2024-06-14 15:46:23 +0800 CST

为什么在外部作用域中将变量设置为只读会阻止定义具有相同名称的局部变量?

  • 772

我搜索了很久,也没找到任何关于此内容的信息。

在下面的代码中,Bar1其行为就像我期望“常规”变量的行为一样(因为help declare说它只-g在函数调用中“看到”):它根据范围(外部或内部)改变值。

Bar2,被“明确”声明,也像我期望的那样(gfhjnfd在外部范围内和blat内部范围内)。

但是Foo是全局不变的变量。 help declare只说“使 NAME 成为只读”。这个扩展(和不需要的)功能记录在哪里?

$ cat test.sh
#!/bin/bash

declare -r Foo=bar
declare -g Bar1=fdhtbn
declare Bar2=gfhjnfd

X()
{
    local Foo=$1
    local Bar1=snorkel
    local Bar2=$2
    echo Foo in X = $Foo
    echo Bar1 in X = $Bar1
    echo Bar2 in X = $Bar2
}

echo Foo, above X, = $Foo
echo Bar1, above X = $Bar1
echo Bar2, above X = $Bar2
echo
X baz blat
echo
echo Foo, below X, = $Foo
echo Bar1, below X = $Bar1
echo Bar2, below X = $Bar2

$ ./test.sh
Foo, above X, = bar
Bar1, above X = fdhtbn
Bar2, above X = gfhjnfd

./test.sh: line 9: local: Foo: readonly variable
Foo in X = bar
Bar1 in X = snorkel
Bar2 in X = blat

Foo, below X, = bar
Bar1, below X = fdhtbn
Bar2, below X = gfhjnfd
bash
  • 3 3 个回答
  • 1131 Views

3 个回答

  • Voted
  1. Stéphane Chazelas
    2024-06-14T19:30:32+08:002024-06-14T19:30:32+08:00

    在一个范围内是否readonly会阻止在子范围内声明同名变量(在具有动态范围的 shell 中,例如 bash)在不同的 shell 之间有所不同。

    这是在奥斯汀小组讨论中提出的,讨论内容是指定localPOSIX 中的变量范围sh(由于无法就此和其他很多方面达成共识,因此这一努力现已被放弃)。

    在 bash 中,这是设计使然,维护者表示他们不会对此做出改变(至少在默认环境中不会),因为这被认为是一种安全措施。

    readonly可以用于两个不完全兼容的用途:

    1. 作为一个开发工具来定义预计不会改变的全局常量,所以当您的代码尝试改变时您会收到警告,然后您可以修复您的代码(有点像constC,尽管这里它发生在运行时)。
    2. 作为受限 shell 中的安全措施(非常差的沙盒安全功能),其中一些特殊变量(例如$PATH,)$HISTFILE(尽管想要提供受限 shell 环境的管理员可能希望将更多变量设置为只读,例如LD_PRELOAD,PERL5LIB...)需要保持只读以防止用户绕过限制。

    在 bash 中,$PATH受限 shell 中对 (仅具有允许的命令) 的修改依赖于属性readonly,因此即使在子范围内也不能修改这些变量(包括它们的属性或范围列表),这一点很重要。

    在 zsh 中,$PATH和一些其他变量(包括LD_PRELOAD,但不包括PERL5LIB,PYTHONPATH...)受到限制,但不是通过只读属性限制的,因此readonly可以可靠地用于上面提到的第一个用法,但这意味着如果你的受限环境中有一些用perl或写成的命令python,用户可能能够通过PERL5LIB/PYTHONPATH环境变量绕过限制,例如,将这些变量设置为只读不会有帮助,因为用户可以typeset +r PERL5LIB删除该只读属性或在具有非只读属性的子范围中隐藏它。


    ¹有关各种 POSIX 类 shell 中的本地范围的更多详细信息,请参阅支持使用 `local` 关键字定义局部变量的 shell 列表。

    • 13
  2. Best Answer
    ruakh
    2024-06-15T06:52:49+08:002024-06-15T06:52:49+08:00

    这里的根本问题是关键字实际上local并没有创建局部变量,或者至少不是您所期望的那样。

    根据Bash 参考手册:

    local函数的局部变量可以用内置命令(局部变量)声明。通常,变量及其值在函数和其调用者之间共享。这些变量仅对函数及其调用的命令可见。当 shell 函数调用其他函数时,这一点尤为重要。

    和

    local只能在函数内使用;它使变量名的可见范围限制在该函数及其子函数内。

    (其中粗体表示我强调)。

    从计算机科学术语上讲,Bash 使用动态作用域而不是词法作用域;但从生活在 2024 年的典型程序员的角度来看,这实际上意味着它local不会创建局部变量,而只是暂时修改全局变量。它的特殊魔力在于,当函数返回时,它会自动恢复变量的原始值(和其他属性),这很有用,但并不是您所期望的“局部变量”的完整实现。

    因此,如果local可以覆盖readonly,则如下所示:

    readonly three=3
    
    print_three() {
      echo "$three"
    }
    
    print_four() {
      local three=4
    
      print_three
    }
    
    print_four
    

    将会打印4而不是3——变量three实际上不是只读的。


    正如 Stéphane Chazelas 在他的回答中指出的那样,一些 shell确实允许local覆盖readonly。但我认为,在任何local具有与 Bash 相同基本行为的 shell 中,这都是一个错误(即,临时修改全局变量而不是创建单独的局部变量),并且该行为确实与 的意图相冲突readonly。我只能假设他们的动机是让local变量更像真正的局部变量,但只要它们不是真正的局部变量,这就没有帮助。

    • 8
  3. Paul_Pedant
    2024-06-14T16:25:49+08:002024-06-14T16:25:49+08:00

    这记录local在

    www.gnu.org/software/bash/manual/bash.html#Bash-Builtins

    除非在函数外部使用 local、提供了无效名称或 name 是只读变量,否则返回状态为零。

    我确实觉得这很令人惊讶,因为第一句话就是:

    对于每个参数,都会创建一个名为 name 的局部变量,并分配值。

    这意味着将创建一个新变量,其作用域仅限于该函数。有一种观点认为,全局常量应该得到可靠保存。

    • 6

相关问题

  • 通过命令的标准输出以编程方式导出环境变量[重复]

  • 从文本文件传递变量的奇怪问题

  • 虽然行读取保持转义空间?

  • `tee` 和 `bash` 进程替换顺序

  • 运行一个非常慢的脚本直到它成功

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    模块 i915 可能缺少固件 /lib/firmware/i915/*

    • 3 个回答
  • Marko Smith

    无法获取 jessie backports 存储库

    • 4 个回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    user12345 无法获取 jessie backports 存储库 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl 为什么大多数 systemd 示例都包含 WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve