技术解析

shell 变量赋值疑问,我没搜到才来问的
0
2021-07-02 18:55:54
idczone
书上说 shell 允许在一个命令之前立即发生一个或多个变量赋值,这些赋值为跟随着的命令更改环境变量,这个赋值的影响是暂时的。
那为什么:
int=100
int=10 echo $(($int - 10))
结果是 90 而不是 0
第二句你分行再试试 = =

是这样吗?
int=100
int=10;echo $(($int-10))

书上有道例题是这样的:(请看第 7 行)
1
2 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

大概是 CC=gcc-5.1 make 这种用法?

这个应该是因为参数会先被展开,你可以试试:
int=10 bash -c 'echo $[int-10]'
或者对比下下面这两行应该就明白了:
x=10 perl -le 'print $ARGV[0]' $x
x=10 perl -le 'print $ENV{"x"}'

主要是因为 shell 变量展开的顺序
第二句实际执行的是
int=10 echo $(($int-10)) -> int=10 echo $((100-10))
而不是直观的
int=10 echo $((10-10))

bash 的语法中可以在命令前设置子进程环境变量,它影响且只影响子进程的环境变量,而对父进程没有影响。
楼主中示例的问题出在, echo 是 bash 的内置命令,我理解他是运行在当前进程空间的,而非子进程。楼主可以试试把 echo 换成一个自己写的脚本让其打印变量看看。

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 的都应该知道,子作用域新建变量与父作用域变量重名时,子作用域变量会覆盖父作用域变量
当子作用域结束时,子作用域变量被销毁,父作用域变量恢复可见状态,所以出现以上情况。

谢谢这么耐心的回答;但我觉得还是展开顺序的问题,如果按照你所说的
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" 明显前一命令影响了后面的命令。

数据地带为您的网站提供全球顶级IDC资源
在线咨询
专属客服