V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Distributions
Ubuntu
Fedora
CentOS
中文资源站
网易开源镜像站
likeunix
V2EX  ›  Linux

shell 变量赋值疑问,我没搜到才来问的

  •  
  •   likeunix · 2016-10-07 11:08:51 +08:00 via Android · 2561 次点击
    这是一个创建于 3002 天前的主题,其中的信息可能已经有所发展或是发生改变。
    书上说 shell 允许在一个命令之前立即发生一个或多个变量赋值,这些赋值为跟随着的命令更改环境变量,这个赋值的影响是暂时的。
    那为什么:
    int=100
    int=10 echo $(($int - 10))
    结果是 90 而不是 0
    9 条回复    2016-10-08 11:13:17 +08:00
    privil
        1
    privil  
       2016-10-07 11:24:02 +08:00
    第二句你分行再试试 = =
    likeunix
        2
    likeunix  
    OP
       2016-10-07 11:35:30 +08:00
    @privil 是这样吗?
    int=100
    int=10;echo $(($int-10))
    likeunix
        3
    likeunix  
    OP
       2016-10-07 11:39:26 +08:00
    书上有道例题是这样的:(请看第 7 行)

    1 #!/bin/bash
    2 #read-ifd:read fields from a file
    3 FILE=/etc/passwd
    4 read -p "Enter a user naem >" user_name
    5 file_info=$(grep "^$user_name" $FILE)
    6 if [[ -n "$file_info" ]];then
    7 IFS=":" read user pw uid gid name home shell <<< "$file_info"
    8 echo "User is $user"
    9 echo "UID is $uid"
    10 echo "GID is $gid"
    11 echo "Full Name is $name"
    12 echo "Home is $home"
    13 echo "Shell is $shell"
    14 else
    15 echo "No such user $user_name" >&2
    16 exit 1
    17 fi
    ooxxcc
        4
    ooxxcc  
       2016-10-07 11:43:47 +08:00
    大概是 CC=gcc-5.1 make 这种用法?
    lululau
        5
    lululau  
       2016-10-07 11:48:51 +08:00   ❤️ 1
    这个应该是因为参数会先被展开,你可以试试:

    int=10 bash -c 'echo $[int-10]'

    或者对比下下面这两行应该就明白了:

    x=10 perl -le 'print $ARGV[0]' $x

    x=10 perl -le 'print $ENV{"x"}'
    is
        6
    is  
       2016-10-07 11:50:35 +08:00   ❤️ 3
    主要是因为 shell 变量展开的顺序

    第二句实际执行的是
    int=10 echo $(($int-10)) -> int=10 echo $((100-10))
    而不是直观的
    int=10 echo $((10-10))
    ceyes
        7
    ceyes  
       2016-10-08 01:32:59 +08:00 via iPhone
    bash 的语法中可以在命令前设置子进程环境变量,它影响且只影响子进程的环境变量,而对父进程没有影响。

    楼主中示例的问题出在, echo 是 bash 的内置命令,我理解他是运行在当前进程空间的,而非子进程。楼主可以试试把 echo 换成一个自己写的脚本让其打印变量看看。
    liuxu
        8
    liuxu  
       2016-10-08 10:08:53 +08:00   ❤️ 1
    int=100
    int=10 echo $(($int - 10))
    echo $int

    sh -x 输出:
    + int=100
    + int=10 echo 90
    90
    + echo 100
    100

    -----------------------------------------
    int=100
    int=10 echo $((int=$int - 10))
    echo $int

    sh -x 输出:
    + int=100
    + int=10 echo 90
    90
    + echo 90
    90

    -----------------------------------------
    int=100
    xx=10 echo $((int=$int - 10))
    echo $int
    echo $xx

    sh -x 输出:
    + int=100
    + xx=10 echo 90
    90
    + echo 90
    90
    + echo

    ------------------------------------------
    这样的话看上去是这个意思:
    int=100
    int=10 echo $(($int - 10))
    echo int
    等同于:
    parent=100
    child=10 echo $(($parent-10))
    echo $parent

    输出:
    + parent=100
    + child=10 echo 90
    90
    + echo 100
    100
    --------------------------------------------
    下面这行
    int=10 echo $(($int - 10))
    其实是开了一个新的子环境,并且子环境可以访问全部父环境变量
    因为“允许在一个命令之前立即发生一个或多个变量赋值”
    所以先执行 echo 命令,然后做赋值 int=10
    其实这个 int 是子环境新建的一个变量,与主环境 int 是两个不同的变量
    换成 C 语言( linux 是 C 写的,所以设计思想往 C 靠拢):
    int num = 100;
    {
    printf("%d\n",num-10);
    int num = 10;
    }
    printf("%d\n",num);

    输出:
    90
    100
    学过 C 的都应该知道,子作用域新建变量与父作用域变量重名时,子作用域变量会覆盖父作用域变量
    当子作用域结束时,子作用域变量被销毁,父作用域变量恢复可见状态,所以出现以上情况。
    likeunix
        9
    likeunix  
    OP
       2016-10-08 11:13:17 +08:00 via Android
    @liuxu 谢谢这么耐心的回答;但我觉得还是展开顺序的问题,如果按照你所说的
    parent=100
    child=10 echo $(($parent-10))
    echo $parent
    那么 chile=10 永远也影响不了 echo $(($parent-10)),这就和"shell 允许在一个命令之前立即发生一个或多个变量赋值,这些赋值为跟随着的命令更改环境变量"不一致了。比如例题的第 7 行: IFS=":" read user pw uid gid name home shell <<< "$file_info" 明显前一命令影响了后面的命令。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5101 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 03:53 · PVG 11:53 · LAX 19:53 · JFK 22:53
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.