iszjw 发表于 2018-8-24 10:25:06

细读shell-3

  14. $(( )) 与 $( ) 还有${ } 差在哪?
  在 bash shell 中,$( ) 与 ` ` (反引号) 都是用来做命令替换用(command substitution
  的。所谓的命令替换与我们第五章学过的变量替换差不多,都是用来重组命令行:
  * 完成引号里的命令行,然后将其结果替换出来,再重组命令行。
  $ echo the last sunday is $(date -d "last sunday" +%Y-%m-%d)
  ` ` 很容易与 ' ' ( 单引号)搞混乱
  在多层次的复合替换中,` ` 须要额外的跳脱( \` )处理,而 $( ) 则比较直观
  command1 `command2 \`command3\` `
  ` ` 基本上可用在全部的 unix shell 中使用,若写成 shell script ,其移植性比较高。
  而 $( ) 并不见的每一种 shell 都能使用,我只能跟你说,若你用 bash2 的话,肯定没问题...
  再让我们看 ${ } 吧... 它其实就是用来作变量替换用的啦
  一般情况下,$var 与 ${var} 并没有啥不一样。
  但是用 ${ } 会比较精确的界定变量名称的范围,比方说:
  $ A=B
  $ echo $AB
  为了完整起见,我这里再用一些例子加以说明 ${ } 的一些特异功能:
  假设我们定义了一个变量为:
  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。 (非
  空值时不作处理)
  还有哦,${#var} 可计算出变量值的长度:
  ${#file} 可得到 27 ,因为 /dir1/dir2/dir3/my.file.txt 刚好是 27 个字节...
  
  15.数组array
  接下来,再为大家介稍一下 bash 的组数(array)处理方法。
  一般而言,A="a b c def" 这样的变量只是将 $A 替换为一个单一的字符串,
  但是改为 A=(a b c def) ,则是将 $A 定义为组数...
  bash 的组数替换方法可参考如下方法:
  ${A[@]} 或 ${A
[*]} 可得到 a b c def (全部组数)
  ${A} 可得到 a (第一个组数),${A} 则为第二个组数...
  ${#A[@]} 或 ${#A
[*]} 可得到 4 (全部组数数量)
  ${#A} 可得到 1 (即第一个组数(a)的长度),${#A} 可得到 3 (第四个组数(def)的
  长度)
  A=xyz 则是将第四个组数重新定义为 xyz ...
  
  最后为大家介绍 $(( )) 的用途吧:它是用来作整数运算的。
  在 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
  事实上,单纯用 (( )) 也可重定义变量值,或作 testing:
  a=5; ((a++)) 可将 $a 重定义为 6
  a=5; ((a--)) 则为 a=4
  a=5; b=7; ((a < b)) 会得到 0 (true) 的返回值。
  常见的用于 (( )) 的测试符号有如下这些:
  :大于
  =:大于或等于
  ==:等于
  !=:不等于
  
  16. $@与 $* 差在哪?
  在 shell script 中,我们可用 $0, $1, $2, $3 ... 这样的变量分别提取命令行中的如下部份:
  script_name parameter1 parameter2 parameter3 ...
  function 一样可以读取自己的(有别于 script 的) postitional parameter ,惟一例外
  的是 $0 而已。
  举例而言:假设 my.sh 里有一个 fucntion 叫 my_fun , 若在 script 中跑 my_fun fp1 fp2 fp3 ,那么,function 内的 $0 是 my.sh ,而 $1 则是 fp1 而非 p1 了...
  #!/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 &quot;fp2 fp3&quot;
  然后在 command line 中跑一下 script ,结果如下
  chmod +x my.sh
  ./my.sh p1 &quot;p2 p3&quot;
  $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 就是取消位置参数中最左边的参数( $0 不
  受影响)。
  其默认值为 1 ,也就是 shift 或 shift 1 都是取消 $1 ,而原本的 $2 则变成 $1、$3 变
  成 $2 ...
  若 shift 3 则是取消前面三个参数,也就是原本的 $4 将变成 $1 ...
  那,亲爱的读者,你说要 shift 掉多少个参数,才可用 $1 取得 ${10} 呢? ^_^
  因此,我们常在 shell script 里用如下方法测试 script 是否有读进参数:
  代码:
  [ $# = 0 ]
  假如为 0 ,那就表示 script 没有参数,否则就是有带参数...
  接下来就是 $@ 与 $* :
  精确来讲,两者只有在 soft quote 中才有差异,否则,都表示&quot;全部参数&quot;( $0 除外)。
  举例来说好了:
  若在 command line 上跑 my.sh p1 &quot;p2 p3&quot; p4 的话,
  不管是 $@ 还是 $* ,都可得到 p1 p2 p3 p4 就是了。
  但是,如果置于 soft quote 中的话:
  &quot;$@&quot; 则可得到 &quot;p1&quot; &quot;p2 p3&quot; &quot;p4&quot; 这三个不同的词段(word)﹔
  &quot;$*&quot; 则可得到 &quot;p1 p2 p3 p4&quot; 这一整串单一的词段。
  #!/bin/bash
  my_fun() {
  echo &quot;$#&quot;
  }
  echo 'the number of parameter in &quot;$@&quot; is '$(my_fun &quot;$@&quot;)
  echo 'the number of parameter in &quot;$*&quot; is '$(my_fun &quot;$*&quot;)
  后再执行 ./my.sh p1 &quot;p2 p3&quot; p4 就知道 $@ 与 $* 差在哪了 ... ^_^
  
  16.   return value(RV)
  Return Value(RV) 的取值为 0-255 之间,由程序(或 script)的作者自行定议:
  * 若在 script 里, 用 exit RV 来指定其值,若没指定,在结束时以最后一道命令之 RV 为值。
  * 若在 function 里,则用 return RV 来代替 exit RV 即可。
  bash 的 test 目前支持的测试对像只有三种:
  以 A=123 这个变量为例
  * [ &quot;$A&quot; = 123 ]:是字符串的测试,以测试 $A 是否为 1、2、3 这三个连续的&quot;文字
  * [ &quot;$A&quot; -eq 123 ]:是整数的测试,以测试 $A 是否等于&quot;一百二十三&quot;。
  * [ -e &quot;$A&quot; ]:是关于文件的测试,以测试 123 这份&quot;文件&quot;是否存在。
  代码:
  $ unset A
  $ [ $A = abc ]
  [: =: unary operator expected
  
  16.   && 与 || 都是用来&quot;组建&quot;多个 command line 用的:
  * command1 && command2 :其意思是 command2 只有在 RV 为 0 (true) 的条件
  执行。
  * command1 || command2 :其意思是 command2 只有在 RV 为非 0 (false) 的条件
  下执行。
  $ A=123
  $ [ -n &quot;$A&quot; ] && echo &quot;yes! it's ture.&quot;
  yes! it's ture.
  $ unset A
  $ [ -n &quot;$A&quot; ] && echo &quot;yes! it's ture.&quot;
  $ [ -n &quot;$A&quot; ] || echo &quot;no, it's NOT ture.&quot;
  no, it's NOT ture.
  (注:[ -n string ] 是测试 string 长度大于 0 则为 true 。)
  上例的第一个 && 命令行之所以会执行其右边的 echo 命令,是因为上一个 test 送回了
  的 RV 值﹔
  但第二次就不会执行,因为为 test 送回非 0 的结果...
  同理,|| 右边的 echo 会被执行,却正是因为左边的 test 送回非 0 所引起的。
  事实上,我们在同一命令行中,可用多个 && 或 || 来组建呢:
  代码:
  $ A=123
  $ [ -n &quot;$A&quot; ] && echo &quot;yes! it's ture.&quot; || echo &quot;no, it's NOT ture.&quot;
  yes! it's ture.
  $ unset A
  $ [ -n &quot;$A&quot; ] && echo &quot;yes! it's ture.&quot; || echo &quot;no, it's NOT ture.&quot;
  no, it's NOT ture.
  
  下面的判断是:当 $A 被赋与值时,再看是否小于 100 ,否则送出 too big! :
  $ A=123
  $ [ -n &quot;$A&quot; ] && [ &quot;$A&quot; -lt 100 ] || echo 'too big!'
  too big!
  若我将 A 取消,照理说,应该不会送文字才对啊(因为第一个条件就不成立了)...
  $ unset A
  $ [ -n &quot;$A&quot; ] && [ &quot;$A&quot; -lt 100 ] || echo 'too big!'
  too big!
  

页: [1]
查看完整版本: 细读shell-3