【问题标题】:Why aren't positional variables changed when one runs a command every time为什么每次运行命令时位置变量不改变
【发布时间】:2023-04-06 11:40:01
【问题描述】:

我正在学习 shell 脚本,我遇到了这种情况。

我们说位置变量是环境变量,但是为什么每次执行命令时它们都不会改变。

看看这个

set v1set v2set v3set v4set
old=$#

#Just a random command
ls -l

new=$#

echo $old $new

它输出4 4。如果环境变量是全局的,为什么不是4 1,因为我运行了ls -l,它应该更新了位置变量?

【问题讨论】:

  • “位置变量是环境变量”是什么意思?
  • 当你运行像ls -l这样的命令时,无论在该命令执行期间发生什么(即使它涉及调用一个子shell,在这种情况下它不会)不会影响当前 shell 中的环境。
  • @Nate,请不要将人们指向 ABS; irc.freenode.org#bash 在帮助人们改掉他们从中习得的坏习惯方面遇到了很多麻烦。在避免演示代码中的不良做法方面,mywiki.wooledge.org/BashGuide 得到了更加警惕的维护。
  • @CharlesDuffy:很公平:我刚刚在 Google 上找到了第一个链接,该链接讨论了子shell 中的变量作用域。我已经删除了我发布的链接,所以人们会使用你的链接。
  • 位置参数 inside shell,由于参数被传递给它(或通过使用 set 内置)。在调用外部进程之前,它们没有被设置ls-l 都是直接传递给 exec* 系统调用之一的简单字符串。

标签: linux bash shell sh


【解决方案1】:

有趣的问题 - 你的观点很好。

为了理解它,您需要了解运行任何命令时会发生什么,例如ls -l。它与“变量已恢复或类似”没有任何关系......

当你要运行任何命令时,

  1. bashFORKS 自身分成两个相同的副本
  2. 一个副本(称为子副本)将自己替换为所需的命令(例如,ls -l
  3. 此时,child进程将获得位置变量$#的正确计数
  4. 记住 - 这发生在子进程中,第二个()进程对此一无所知
  5. 父母只是等到孩子完成(当然,HIS $# 不会改变,因为对于父母来说什么都没有发生 - 只是等待
  6. 当孩子 (ls -l) 完成后,父母继续奔跑 - 当然,他的 $# 没有理由改变...

ps:上面说的很简单。事实上,在分叉之后,它们并不完全相同,而是在一个数字上不同 - 父进程获取子进程的进程号,这个 nuber 的子进程为 '0'

【讨论】:

    【解决方案2】:

    如果环境变量是全局的,为什么不是 4 1

    我想您是在问为什么运行命令 ls -l 不会将位置参数从四个更改为一个,其中一个是 -l

    对于程序ls,它确实将它们设置为-l。当程序ls 查询它的位置参数时,它被告知有一个由-l 组成的单个参数。但是,一旦ls 终止,位置参数就会返回到它们之前的状态。

    如果环境变量是全局的,

    即使对于全局环境变量,子进程中对它们的更改永远不会出现在父进程中。环境变量的通信是单向的:仅从父母到孩子。

    例如:

    $ cat test1.sh
    echo "in $0, before, we have $# pos. params with values=$*"
    bash test2.sh calling test2 from test1
    echo "in $0, after , we have $# pos. params with values=$*"
    
    $ cat test2.sh
    echo "in $0, we have $# pos. params with values=$*"
    
    $ bash test1.sh -l
    in test1.sh, before, we have 1 pos. params with values=-l
    in test2.sh, we have 4 pos. params with values=calling test2 from test1
    in test1.sh, after , we have 1 pos. params with values=-l
    

    另外一个例子,这个例子表明孩子对环境变量的更改不会影响父母:

    $ cat test3.sh
    export myvar=1
    echo "in $0, before, myvar=$myvar"
    bash test4.sh
    echo "in $0, after,  myvar=$myvar"
    
    $ cat test4.sh
    export myvar=2
    echo "in $0, myvar=$myvar"
    
    $ bash test3.sh
    in test3.sh, before, myvar=1
    in test4.sh, myvar=2
    in test3.sh, after,  myvar=1
    

    【讨论】:

    • 谢谢,我对环境变量的理解完全错误。
    • 示范性地使用"values=$*" 并不是最佳实践——它将"foo" "bar""foo bar" 混为一谈。 printf '%q ' "$@" 清楚地表明了这种区别,就像(在较小程度上)说,printf '<%s>' "$@"
    【解决方案3】:

    我认为$# 不适用于交互式外壳。它在脚本中运行良好。试试这个。

    $ cat try.sh
    #!/bin/bash
    echo $*
    echo $#
    
    $ ./try.sh one
    one
    1
    
    $ ./try.sh one two
    one two
    2
    
    $ ./try.sh one two three
    one two three
    3
    

    【讨论】:

      猜你喜欢
      • 2016-01-17
      • 2015-04-02
      • 1970-01-01
      • 1970-01-01
      • 2015-10-14
      • 1970-01-01
      • 1970-01-01
      • 2013-05-30
      • 2023-01-04
      相关资源
      最近更新 更多