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

2016-10-07 11:08:51 +08:00
 likeunix
书上说 shell 允许在一个命令之前立即发生一个或多个变量赋值,这些赋值为跟随着的命令更改环境变量,这个赋值的影响是暂时的。
那为什么:
int=100
int=10 echo $(($int - 10))
结果是 90 而不是 0
2561 次点击
所在节点    Linux
9 条回复
privil
2016-10-07 11:24:02 +08:00
第二句你分行再试试 = =
likeunix
2016-10-07 11:35:30 +08:00
@privil 是这样吗?
int=100
int=10;echo $(($int-10))
likeunix
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
2016-10-07 11:43:47 +08:00
大概是 CC=gcc-5.1 make 这种用法?
lululau
2016-10-07 11:48:51 +08:00
这个应该是因为参数会先被展开,你可以试试:

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

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

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

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

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

楼主中示例的问题出在, echo 是 bash 的内置命令,我理解他是运行在当前进程空间的,而非子进程。楼主可以试试把 echo 换成一个自己写的脚本让其打印变量看看。
liuxu
2016-10-08 10:08:53 +08:00
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
2016-10-08 11:13:17 +08:00
@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" 明显前一命令影响了后面的命令。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/310934

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX