本文整理并转自CU上的帖子[学习共享] shell 十三問?,此贴是2003年发表的,但却是相当不错的linux基础知识汇集贴,原帖主使用的台湾风格,本文加以简体化和整理。
第6问:exec跟source差在哪?这次先让我们从CU Shell版的一个实例贴子来谈起吧:(论坛改版后原连接已经失效)例中的提问原文如下: cd /etc/aa/bb/cc 可以执行,但是把这条命令写入shell时shell不执行!这是什么原因呀!(意思是:运行脚本后并没有移到/etc/aa/bb/cc目录)
我当时如何回答暂时别去深究,先让我们了解一下行程(process)的观念好了。首先,我们所执行的任何程序,都是由父行程(parent process)所产生出来的一个子行程(child process),子行程在结束后,将返回到父行程去。此一现像在Linux系统中被称为fork。(为何要程为fork呢?嗯,画一下图或许比较好理解…^_^) 当子行程被产生的时候,将会从父行程那里获得一定的资源分配、及(更重要的是)继承父行程的环境﹗让我们回到上一章所谈到的“环境变量”吧: 所谓环境变量其实就是那些会传给子行程的变量。简单而言,“遗传性”就是区分本地变量与环境变量的决定性指标。
然而,从遗传的角度来看,我们也不难发现环境变量的另一个重要特征: 环境变量只能从父行程到子行程单向继承。换句话说:在子行程中的环境如何变更,均不会影响父行程的环境。
接下来,再让我们了解一下命令脚本(shell script)的概念。所谓的shell script讲起来很简单,就是将你平时在shell prompt后所输入的多行command line依序写入一个文件去而已。其中再加上一些条件判断、互动界面、参数运用、函数调用等等技巧,得以让script更加“聪明”的执行,但若撇开这些技巧不谈,我们真的可以简单的看成script只不过依次执行预先写好的命令行而已。 再结合以上两个概念(process script),那应该就不难理解如下这句话的意思了: 正常来说,当我们执行一个shell script时,其实是先产生一个sub-shell的子行程,然后sub-shell再去产生命令行的子行程。
然则,那让我们回到本章开始时所提到的例子再从新思考:cd /etc/aa/bb/cc 可以执行,但是把这条命令写入shell时shell不执行!这是什么原因呀!我当时的答案是这样的: 因为,一般我们跑的shell script是用subshell去执行的。从process的观念来看,是parent process产生一个child process去执行,当child结束后,会返回parent,但parent的环境是不会因child的改变而改变的。所谓的环境元数很多,凡举effective id,variable,workding dir等等…其中的workding dir(PWD)正是楼主的疑问所在:当用subshell来跑script的话,subshell的PWD会因为cd而变更,但当返回primary shell时,$PWD是不会变更的。
能够了解问题的原因及其原理是很好的,但是?如何解决问题恐怕是我们更感兴趣的﹗是吧?^_^ 那好,接下来,再让我们了解一下source 命令好了。当你有了fork的概念之后,要理解source就不难: 所谓source就是让script在当前shell内执行、而不是产生一个sub-shell来执行。
由于所有执行结果均于当前shell内完成,若script的环境有所改变,当然也会改变当前环境了﹗因此,只要我们将原本单独输入的script命令行变成source命令的参数,就可轻易解决前例提到的问题了。 比方说,原本我们是如此执行script的: ./my.script
现在改成这样即可: source ./my.script
或: ../my.script
说到这里,我想,各位有兴趣看看/etc底下的众多设定文件,应该不难理解它们被定义后,如何让其他script读取并继承了吧?若然,日后你有机会写自己的script,应也不难专门指定一个设定文件以供不同的script一起“共享”了…^_^ okay,到这里,若你搞得懂fork与source的不同,那接下来再接受一个挑战: 那exec 又与source/fork有何不同呢? 哦…要了解exec 或许较为复杂,尤其扯上File Descriptor的话…不过,简单来说: exec也是让script在同一个行程上执行,但是原有行程则被结束了。也就是简而言之:原有行程会否终止,就是exec与source/fork的最大差异了。
嗯,光是从理论去理解,或许没那么好消化,不如动手“实作 思考”来的印象深刻哦。下面让我们写两个简单的script,分别命名为1.sh及2.sh: 1.sh #!/bin/bashA=B
echo 'PID for 1.sh before exec/source/fork:$$'export A
echo '1.sh: \$A is $A'case $1 in
exec)
echo 'using exec…'
exec ./2.sh;;
source)
echo 'using source…'
../2.sh;;
*)
echo 'using fork by default…'
./2.sh;;
esac
echo 'PID for 1.sh after exec/source/fork:$$'echo '1.sh: \$A is $A'
2.sh #!/bin/bashecho 'PID for 2.sh: $$'echo '2.sh get \$A=$A from 1.sh'A=C
export A
echo '2.sh: \$A is $A'
然后,分别跑如下参数来观察结果: $ ./1.sh fork
$ ./1.sh source
$ ./1.sh exec
好了,别忘了仔细比较输出结果的不同及背后的原因哦…若有疑问,欢迎提出来一起讨论讨论~~~happy scripting!^_^ 第7问:( ) 与 { } 差在哪?嗯,这次轻松一下,不讲太多…^_^ 先说一下,为何要用()或{ }好了。许多时候,我们在shell操作上,需要在一定条件下一次执行多个命令,也就是说,要么不执行,要么就全执行,而不是每次依序的判断是否要执行下一个命令。或是,需要从一些命令执行优先次顺中得到豁免,如算术的2*(3 4)那样… 这时候,我们就可引入“命令群组”(command group)的概念:将多个命令集中处理。在shell command line中,一般人或许不太计较()与{ }这两对符号的差异,虽然两者都可将多个命令作群组化处理,但若从技术细节上,却是很不一样的: 若,你对上一章的fork与source的概念还记得了的话,那就不难理解两者的差异了。 要是在command group中扯上变量及其他环境的修改,我们可以根据不同的需求来使用()或{ }。 通常而言,若所作的修改是临时的,且不想影响原有或以后的设定,那我们就nested sub-shell,反之,则用non-named command group。 是的,光从command line来看,()与{ }的差别就讲完了,够轻松吧~~~ ^_^ 然而,若这两个meta用在其他command meta或领域中(如Regular Expression),还是有很多差别的。只是,我不打算再去说明了,留给读者自己慢慢发掘好了…我这里只想补充一个概念,就是function 。 所谓的function ,就是用一个名字去命名一个command group,然后再调用这个名字去执行command group。从non-named command group来推断,大概你也可以猜到我要说的是{ }了吧?(yes!你真聪明﹗^_^) 在bash中,function的定义方式有两种: 方式一: function function_name {
command1
command2
command3
…
}
方式二: fuction_name() {
command1
command2
command3
…
}
用哪一种方式无所谓,只是若碰到所定意的名称与现有的命令或别名(Alias)冲突的话,方式二或许会失败。但方式二起码可以少打function这一串英文字母,对懒人来说(如我),又何乐不为呢?…^_^ function在某一程度来说,也可称为“函式”,但请不要与传统编程所使用的函式(library)搞混了,毕竟两者差异很大。惟一相同的是,我们都可以随时用“已定义的名称”来调用它们… 若我们在shell操作中,需要不断的重覆执行某些命令,我们首先想到的,或许是将命令写成命令脚本(shell script)。不过,我们也可以写成function,然后在command line中打上function_name 就可当一舨的script来使用了。 只是若你在shell中定义的function ,除了可用unset function_name 取消外,一旦退出shell,function也跟着取消。 然而,在script中使用function却有许多好处,除了可以提高整体script的执行性能外(因为已被加载),还可以节省许多重覆的代码… 简单而言,若你会将多个命令写成script以供调用的话,那,你可以将function看成是script中的script…^_^ 而且,透过上一章介绍的source 命令,我们可以自行定义许许多多好用的function,再集中写在特定文件中,然后,在其他的script中用source将它们加载并反复执行。 若你是RedHat Linux的使用者,或许,已经猜得出/etc/rc.d/init.d/functions 这个文件是作啥用的了~~~ ^_^ okay,说要轻松点的嘛,那这次就暂时写到这吧。祝大家学习愉快﹗^_^ 第8问:(())与( ) 還有${ } 差在哪?我们上一章介绍了( )与{ }的不同,这次让我们扩展一下,看看更多的变化:()与{ }又是啥玩意儿呢? 在bash shell中,$() 与``(反引号)都是用来做命令替换用(command substitution)的。所谓的命令替换与我们第五章学过的变量替换差不多,都是用来重组命令行: 完成引号里的命令行,然后将其结果替换出来,再重组命令行。
例如: $ echo the last sunday is $(date -d 'last sunday' %Y-%m-%d)
如此便可方便得到上一星期天的日期了…^_^ 在操作上,用$() 或``都无所谓,只是我”个人”比较喜欢用$() ,理由是: command1 `command2 `command3` `
原本的意图是要在command2 command3 先将command3提换出来给command2处理,然后再将结果传给command1 command2… 来处理。然而,真正的结果在命令行中却是分成了command2 与``两段。正确的输入应该如下: command1 `command2 \`command3\` `
要不然,换成$() 就没问题了: command1 $(command2 $(command3))
只要你喜欢,做多少层的替换都没问题啦~~~ ^_^ 不过,$()并不是没有斃端的… 首先,`基本上可用在全部的unix shell中使用,若写成shell script,其移植性比较高。而$()并不见的每一种shell都能使用 ,我只能跟你说,若你用bash2`的话,肯定没问题…^_^ 接下来,再让我们看${} 吧…它其实就是用来作变量替换用的啦。一般情况下,$var 与${var} 并没有啥不一样。但是用${} 会比较精确的界定变量名称的范围,比方说: $ A=B
$ echo $AB
原本是打算先将$A的结果替换出来,然后再补一个B字母于其后,但在命令行上,真正的结果却只是会提换变量名称为AB的值出来…若使用${}就没问题了: $ echo ${A}B
BB
不过,假如你只看到${} 只能用来界定变量名称的话,那你就实在太小看bash了﹗为了完整起见,我这里再用一些例子加以说明${} 的一些特异功能。 假设我们定义了一个变量为: file=/dir1/dir2/dir3/my.file.txt
我们可以用${} 分别替换获得不同的值: ${file#*/} # 拿掉第一条/及其左边的字串:dir1/dir2/dir3/my.file.txt
${file##*/} # 拿掉最后一条/及其左边的字串:my.file.txt
${file#*.} # 拿掉第一个.及其左边的字串:file.txt
${file##*.} # 拿掉最后一个.及其左边的字串:txt
${file%/*} # 拿掉最后条/及其右边的字串:/dir1/dir2/dir3
${file%%/*} # 拿掉第一条/及其右边的字串:(空值)
${file%.*} # 拿掉最后一个.及其右边的字串:/dir1/dir2/dir3/my.file
${file%%.*} #拿掉第一个.及其右边的字串:/dir1/dir2/dir3/my
记忆的方法为: # 是去掉左边(在键盘上#在$之左边)
% 是去掉右边(在键盘上%在$之右边)
单一符号是最小匹配,两个符号是最大匹配。
${file:0:5} # 提取最左边的5个字节:/dir1
${file:5:5} # 提取第5个字节右边的连续5个字节:/dir2
我们也可以对变量值里的字串作替换: ${file/dir/path} # 将第一个dir提换为path:/path1/dir2/dir3/my.file.txt
${file//dir/path} # 将全部dir提换为path:/path1/path2/path3/my.file.txt
利用${} 还可针对不同的变数状态赋值(没设定、空值、非空值) ${file-my.file.txt} # 假如$file没有设定,则使用my.file.txt作传回值。(空值及非空值时不作处理)
${file:-my.file.txt} # 假如$file没有设定或为空值,则使用my.file.txt作传回值。(非空值时不作处理)
${file my.file.txt} # 假如$file设为空值或非空值,均使用my.file.txt作传回值。(没设定时不作处理)
${file: my.file.txt} # 若$file为非空值,则使用my.file.txt作传回值。(没设定及空值时不作处理)
${file=my.file.txt} # 若$file没设定,则使用my.file.txt作传回值,同时将$file赋值为my.file.txt。(空值及非空值时不作处理)
${file:=my.file.txt} # 若$file没设定或为空值,则使用my.file.txt作传回值,同时将$file赋值为my.file.txt。(非空值时不作处理)
${file?my.file.txt} # 若$file没设定,则将my.file.txt输出至STDERR。(空值及非空值时不作处理)
${file:?my.file.txt} # 若$file没设定或为空值,则将my.file.txt输出至STDERR。(非空值时不作处理)
Tips: 以上的理解在于,你一定要分清楚unset与null及non-null这三种赋值状态。一般而言,: 与null有关,若不带 : 的话,null不受影响,若带 : 则连null也受影响。
还有哦,${#var} 可计算出变量值的长度: ${#file} # 可得到27,因为/dir1/dir2/dir3/my.file.txt刚好是27个字节…
接下来,再为大家介稍一下bash的数组(array)处理方法。 一般而言,A='a b c def' 这样的变量只是将$A 替换为一个单一的字串,但是改为A=(a b c def) ,则是将$A定义为数组… bash的数组替换方法可参考如下方法: ${A[@]}或${A[*]} # 可得到a b c def(全部组数)
${A[0]} # 可得到a(第一个组数),${A[1]}则为第二个组数…
${#A[@]}或${#A[*]} #可得到4(全部组数数量)
${#A[0]} #可得到1(即第一个组数(a)的长度),${#A[3]}可得到3(第四个组数(def)的长度)
A[3]=xyz # 则是将第四个组数重新定义为xyz…
能够善用bash的$() 与${} 可大大提高及简化shell在变量上的处理能力哦~~~ ^_^ 好了,最后为大家介绍$(()) 的用途吧:它是用来作整数运算的。在bash中,$(()) 的整数运算符号大致有这些: - * /:分别为“加、减、乘、除”。%:余数运算。& | ^!:分别为“AND、OR、XOR、NOT”运算。 例: $ a=5;b=7;c=2
$ echo $((a b*c))
19
$ echo $(((a b)/c))
6
$ echo $(((a*b)%c))
1
在$(()) 中的变量名称,可于其前面加$(($a $b * $c))也可得到19的结果 此外,$(()) 还可作不同进位(如二进位、八进位、十六进位)作运算呢,只是,输出结果皆为十进制而已: echo $((16#2a)) # 结果为42(16进位转十进制)
以一个实用的例子来看看吧:假如当前的umask是022,那么新建文件的权限即为: umask022 echo “obase=8;You can't use 'macro parameter character #' in math mode (umask))))” | bc 644 事实上,单纯用(()) 也可重定义变量值,或作testing: a=5;((a )) #可将$a重定义为6 a=5;((a—)) 则为a=4 a=5;b=7;((a < b)) 会得到0(true)的返回值。 常见的用于(())的测试符号有如下这些: <:小于 >:大于 <=:小于或等于 >=:大于或等于 ==:等于 !=:不等于 不过,使用(()) 作整数测试时,请不要跟[ ] 的整数测试搞混乱了。(更多的测试我将于第十章为大家介绍)怎样?好玩吧..^_^ okay,这次暂时说这么多… 上面的介绍,并没有详列每一种可用的状态,更多的,就请读者参考手册文件啰… 第9问:@与* 差在哪?要说$@ 与$* 之前,需得先从shell script的positional parameter 谈起… 我们都已经知道变量(variable)是如何定义及替换的,这个不用再多讲了。但是,我们还需要知道有些变量是shell内定的,且其名称是我们不能随意修改的,其中就有positional parameter在内。在shell script中,我们可用0,1,2,3…这样的变量分别提取命令行中的如下部份: script_name parameter1 parameter2 parameter3…
我们很容易就能猜出$0 就是代表shell script名称(路径)本身,而$1 就是其后的第一个参数,如此类推… 须得留意的是IFS 的作用,也就是,若IFS 被quoting处理后,那么positional parameter也会改变。如下例: my.sh p1 'p2 p3' p4
由于在p2与p3之间的空白键被soft quote所关闭了,因此my.sh中的$2 是”p2 p3”而$3 则是p4… 还记得前两章我们提到fucntion 时,我不是说过它是script中的script吗?^_^ 是的,function一样可以读取自己的(有别于script的)postitional parameter,惟一例外的是$0而已。举例而言:假设my.sh里有一个function叫my_fun,若在script中跑my_fun fp1 fp2 fp3,那么function内的$0是my.sh,而$1则是fp1而非p1了…不如写个简单的my.sh script看看吧: #!/bin/bash
my_fun() {
echo '$0 inside function is '$0
echo '$1 inside function is '$1
echo '$2 inside function is '$2
}
echo '$0 outside function is '$0
echo '$1 outside function is '$1
echo '$2 outside function is '$2
my_fun fp1 'fp2 fp3'
然后在command line中跑一下script就知道了: chmod x my.sh
./my.sh p1 'p2 p3'
$0 outside function is ./my.sh
$1 outside function is p1
$2 outside function is p2 p3
$0 inside function is ./my.sh
$1 inside function is fp1
$2 inside function is fp2 fp3
然而,在使用positional parameter的时候,我们要注意一些陷井哦: 10不是替换第10个参数,而是替换第一个参数(1)然后再补一个0于其后!
也就是,my.sh one two three four five six seven eigth nine ten 这样的command line,my.sh里的$10不是ten而是one0哦…小心小心!要抓到ten的话,有两种方法: 方法一是使用我们上一章介绍的${ } ,也就是用${10} 即可。 方法二,就是shift了。用通俗的说法来说,所谓的shift就是取消positional parameter中最左边的参数$0 不受影响)。其预设值为1,也就是shift 或shift 1 都是取消$1 ,而原本的$2 则变成$1 ,$3 变成$2 ……若shift 3 则是取消前面三个参数,也就是原本的$4 将变成$1 那,亲爱的读者,你说要shift 掉多少个参数,才可用1取得{10}呢?^_^ okay,当我们对positional parameter有了基本概念之后,那再让我们看看其他相关变量吧。 首先是$# :它可抓出positional parameter的数量。以前面的my.sh p1 'p2 p3' 为例:由于p2与p3之间的IFS是在soft quote中,因此$# 可得到2的值。但如果p2与p3没有置于quoting中话,那$# 就可得到3的值了。同样的道理在function中也是一样的… 因此,我们常在shell script里用如下方法测试script是否有读进参数: [ $# = 0 ]
假如为0,那就表示script没有参数,否则就是有带参数… 接下来就是$@ 与$* :精确来讲,两者只有在soft quote中才有差异,否则,都表示“全部参数”($0 除外)。举例来说好了:若在command line上跑my.sh p1 'p2 p3' p4 的话,不管是$@ 还是$* ,都可得到p1 p2 p3 p4就是了。但是,如果置于soft quote中的话: 我们可修改一下前面的my.sh,使之内容如下: #!/bin/bash
my_fun() {
echo '$#'
}
echo 'the number of parameter in '$@' is '$(my_fun '$@')
echo 'the number of parameter in '$*' is '$(my_fun '$*')
然后再执行./my.sh p1 “p2 p3” p4就知道@与*差在哪了…^_^ the number of parameter in '$@' is 3
the number of parameter in '$*' is 1
第10问:&& 与 || 差在哪?好不容易,进入两位数的章节了…一路走来,很辛苦吧?也很快乐吧?^_^ 在解答本章题目之前,先让我们了解一个概念:return value ! 我们在shell下跑的每一个command或function,在结束的时候都会传回父行程一个值,称为return value。在shell command line中可用$? 这个变量得到最'新' 的一个return value,也就是刚结束的那个行程传回的值。Return Value(RV)的取值为0-255之间,由程序(或script)的作者自行定议: Return Value的作用,是用来判断行程的退出状态(exit status),只有两种: 0的话为“真”(true) 非0的话为“假”(false)
举个例子来说明好了:假设当前目录内有一份my.file的文件,而no.file是不存在的: $ touch my.file
$ ls my.file
$ echo $? # first echo
0
$ ls no.file
ls: no.file: No such file or directory
$ echo $? # second echo
1
$ echo $? # third echo
0
上例的第一个echo是关于ls my.file 的RV,可得到0的值,因此为true;第二个echo是关于ls no.file 的RV,则得到非0的值,因此为false;第三个echo是关于第二个echo $? 的RV,为0的值,因此也为true。 请记住:每一个command在结束时都会送回return value的! 不管你跑甚么样的命令…然而,有一个命令却是”专门”用来测试某一条件而送出return value以供true或false的判断,它就是test 命令了! 若你用的是bash,请在command line下打man test 或man bash 来了解这个test 的用法。这是你可用作参考的最精确的文件了,要是听别人说的,仅作参考就好…下面我只简单作一些辅助说明,其余的一律以man为准: 首先,test的表示式我们称为expression ,其命令格式有两种: test expression
or:
[ expression ]
(请务必注意[ ]之间的空白键﹗) 用哪一种格式没所谓,都是一样的效果。(我个人比较喜欢后者…)其次,bash的test目前支持的测试对像只有三种: 请初学者一定要搞清楚这三者的差异,因为test所用的expression是不一样的。 [ '$A' = 123 ] # 是字串的测试,以测试$A是否为1、2、3这三个连续的“文字”。
[ 'A' -eq 123 ] # 是整数的测试,以测试$A是否等于“一百二十三”。
[ -e '$A' ] # 是关于文件的测试,以测试123这份“文件”是否存在。
第三,当expression测试为“真”时,test就送回0(true) 的return value,否则送出非0(false) 。若在expression之前加上一个! (感叹号),则是当expression为“假时”才送出0,否则送出非0。 同时,test也允许多重的复合测试: expression1 -a expression2 # 当两个exrepssion都为true,才送出0,否则送出非0。
expression1 -o expression2 # 只需其中一个exrepssion为true,就送出0,只有两者都为false才送出非0。
例如: [ -d '$file' -a -x '$file' ]
是表示当$file是一个目录、且同时具有x权限时,test才会为true。 第四,在command line中使用test时,请别忘记命令行的“重组”特性,也就是在碰到meta时会先处理meta再重新组建命令行。(这个特性我在第二及第四章都曾反复强调过)比方说,若test碰到变量或命令替换时,若不能满足expression格式时,将会得到语法错误的结果。举例来说好了:关于[ string1 = string2 ] 这个test格式,在=号两边必须要有字串,其中包括空(null)字串(可用soft quote或hard quote取得)。 假如$A目前没有定义,或被定议为空字串的话,那如下的写法将会失败: $ unset A
$ [ $A = abc ]
[:=:unary operator expected
这是因为命令行碰到这个meta时,会替换A的值,然后再重组命令行,那就变成了:[ = abc ] 如此一来=号左边就没有字串存在了,因此造成test的语法错误﹗但是,下面这个写法则是成立的: $ [ '$A' = abc ]
$ echo $?
1
这是因为在命令行重组后的结果为:[ '' = abc ] 。由于=左边我们用soft quote得到一个空字串,而让test语法得以通过…读者诸君请务必留意这些细节哦,因为稍一不慎,将会导至test的结果变了个样! 若您对test还不是很有经验的话,那在使用test时不妨先采用如下这一个”法则”: 假如在test中碰到变量替换,用soft quote是最保险的﹗若你对quoting不熟的话,请重新温习第四章的内容吧…^_^
okay,关于更多的test用法,老话一句:请看man page吧!^_^ 虽然洋洋洒洒讲了一大堆,或许你还在嘀咕….那…那个return value有啥用啊?!问得好﹗ 告诉你:return value的作用可大了﹗若你想让你的shell变“聪明”的话,就全靠它了: 有了return value,我们可以让shell跟据不同的状态做不同的时情…
这时候,才让我来揭晓本章的答案吧~~~ ^_^ && 与 || 都是用来“组建”多个command line用的: 来,以例子来说好了: $ A=123
$ [ -n '$A' ] && echo 'yes!it's ture.'
yes!it's ture.
$ unset A
$ [ -n '$A' ] && echo 'yes!it's ture.'
$ [ -n '$A' ] || echo 'no,it's NOT ture.'
no,it's NOT ture.
(注:[ -n string ] 是测试string长度大于0则为true。) 上例的第一个&& 命令行之所以会执行其右边的echo命令,是因为上一个test送回了0的RV值﹔但第二次就不会执行,因为test送回非0的结果…同理,||右边的echo会被执行,却正是因为左边的test送回非0所引起的。 事实上,我们在同一命令行中,可用多个&&或||来组建呢: $ A=123
$ [ -n '$A' ] && echo 'yes!it's ture.' || echo 'no,it's NOT ture.'
yes!it's ture.
$ unset A
$ [ -n '$A' ] && echo 'yes!it's ture.' || echo 'no,it's NOT ture.'
no,it's NOT ture.
怎样,从这一刻开始,你是否觉得我们的shell是“很聪明”的呢?^_^ 好了,最后,布置一道习题给大家做做看… 下面的判断是:当$A 被赋与值时,再看是否小于100,否则送出too big!: $ A=123
$ [ -n '$A' ] && [ '$A' -lt 100 ] || echo 'too big!'
too big!
若我将A取消,照理说,应该不会送文字才对啊(因为第一个条件就不成立了)… $ unset A
$ [ -n '$A' ] && [ '$A' -lt 100 ] || echo 'too big!'
too big!
为何上面的结果也可得到呢?又,如何解决之呢?(提示:修改方法很多,其中一种方法可利用第七章介绍过的command group…)快﹗告我我答案﹗其余免谈… 原文链接:https://segmentfault.com/a/1190000004324819 原文链接:http://bbs./thread-218853-1-1.html
|