zoukankan      html  css  js  c++  java
  • 变量和参数

    变量赋值时,'='左右都不能有空格。 语句结尾不需要分号;除了在变量赋值和for循环语句头中,bash中的变量使用必须在变量前加"$";以单引号括起来的变量将不被解释为变量,如'$str'被解释为字符串。

    1, 反斜杠

    当命令接收含有元字符如$,*,?等的参数时,就必须将这些元字符用引号引起来。可以用反斜杠将其转义。

    2. 单引号

    单引号强制shell将引号内的所有字符视为其字面意义。包括 *,`,?, $ 等。因此单引号内不可再内嵌单引号,即便反斜杠也没有特殊意义。

    3. 双引号

    双引号会确切的处理转义字符,变量,算术,命令替换(反引号括起来的命令)。双引号内的$,`,",等如需用到字面意义都需用""转义。

    注:形式为$(string)或`string`的表达式即为命令替换。

     4. let命令

    算术运算:可用test[], $((...)), 命令替换`expr $i + 1`, let命令四种方法。

    1:使用 expr 外部程式

    2:使用 $(( ))

    3:使用 $[ ]

    4:使用let 命令

     r=`expr 4 + 5`

    r=$(( 4 + 5 ))

    r=$[ 4 + 5 ]

    let n=n+1

    m=$[ m + 1]
    m=`expr $m + 1`
    m=$(($m + 1))
    let m=m+1

     使用算术操作符

    let "n = $n + 1" # let "n = n + 1" 也可以.

    : $((n = $n + 1))  # ":" 是必需的, 因为如果没有":"的话, Bash将会尝试把"$((n = $n + 1))"解释为一个命令.

    n=$(($n + 1)) 

    : $[ n = $n + 1 ] , ":" 是必需的, 因为如果没有":"的话,Bash将会尝试把"$[ n = $n + 1 ]"解释为一个命令.

    n=$[ $n + 1 ] # 即使"n"被初始化为字符串, 这句也能够正常运行.

    (( n = n + 1 ))   #这句是一种更简单方法.

    C风格的增量操作.

    let "n++" # let "++n" 也可以.
     (( n++ )) # (( ++n ) 也可以.
    $(( n++ )) # : $(( ++n )) 也可以.
     echo -n "$n "
     $[ n++ ] # : $[ ++n ]] 也可以.

    变量替换$:注意$variable事实上只是${variable}的简写形式. 在某些上下文中$variable可能会引起错误,这时候你就需要用${variable}了。

    被一对双引号(" ")括起来的变量替换是不会被阻止的. 所以双引号被称为部分引用, 有时候又被称为"弱引用". 但是如果使用单引号的话(' '), 那么变量替换就会被禁止了, 变量名只会被解释成字面的意思, 不会发生变量替换. 所以单引号被称为全引用, 有时候也被称为"强引用".

    强烈注意, 在赋值的的时候, 等号前后一定不要有空格.    如果出现空格会怎么样? "VARIABLE =value"14   脚本将尝试运行一个"VARIABLE"的命令, 带着一个"=value"参数.

    hello="A B C D"              echo $hello   # A B C D           echo "$hello"  # A B C D

    就象你看到的echo $hello 和 echo "$hello" 将给出不同的结果.引用一个变量将保留其中的空白, 当然, 如果是变量替换就不会保留了。

     变量赋值=,赋值操作(前后都不能有空白). 因为=和-eq都可以用做条件测试操作, 所以不要与这里的赋值操作相混淆.

    Bash变量是不区分类型的,本质上, Bash变量都是字符串. 但是依赖于具体的上下文, Bash也允许比较操作和整数操作. 其中的关键因素就是, 变量中的值是否只有数字。 Bash不能够处理浮点运算. 它会把包含小数点的数字看作字符串.

    特殊的变量类型:

    局部变量:这种变量只有在代码块或者函数中(参见函数中的局部变量)才可见.

    环境变量:这种变量将影响用户接口和shell的行为。

    如果一个脚本要设置一个环境变量, 那么需要将这些变量"export"出来, 也就是需要通知到脚本
    本地的环境. 这是export命令的功能.

    一个脚本只能够export变量到这个脚本所产生的子进程, 也就是说只能够对这个脚本所产生的命令和进程起作用. 如果脚本是从命令行中调用的, 那么这个脚本所export的变量是不能影响命令行环境的. 也就是说, 子进程是不能够export变量来影响产生自己的父进程的环境的.

    位置参数:从命令行传递到脚本的参数: $0, $1, $2, $3 . . .$0就是脚本文件自身的名字, $1 是第一个参数, $2是第二个参数, $3是第三个参数, 然后是第四个. [1] $9之后的位置参数就必须用大括号括起来了, 比如, ${10}, ${11}, ${12}.两个比较特殊的变量$*和$@ 表示所有的位置参数.

    shift命令会重新分配位置参数, 其实就是把所有的位置参数都向左移动一个位置.$1 <--- $2, $2 <--- $3, $3 <--- $4, 等等.原来的$1就消失了, 但是$0 (脚本名)是不会改变的. 如果传递了大量的位置参数到脚本中, 那么shift命令允许你访问的位置参数的数量超过10个, 当然{}标记法也提供了这样的功能.

     $0参数是由调用这个脚本的进程所设置的. 按照约定, 这个参数一般就是脚本的名字.

    $HOSTNAME返回主机名,和hostname命令返回结果相同。

    $HOSTTYPE,主机类型。就像$MACHTYPE, 用来识别系统硬件. $OSTYPE操作系统类型

    $IFS,内部域分隔符。$IFS处理其他字符与处理空白字符不同. IFS=:  , var=":a::b:c:::"  # [][a][][b][c][] [] [] ;IFS=" ",var=" a b c "#[a][b][c]

    $PWD工作目录(你当前所在的目录)

    $REPLY,当没有参数变量提供给read命令的时候, 这个变量会作为默认变量提供给read命令

    $! 运行在后台的最后一个作业的PID(进程ID)

    $$ 脚本自身的进程ID. $$变量在脚本中经常用来构造"唯一的"临时文件名 . 这么做通常比调用mktemp命令来的简单.

     引用

    引用的字面意思就是将字符串用双引号括起来. 它的作用就是保护字符串中的特殊字符不被shell或者shell脚本重新解释, 或者扩展. (我们这里所说的"特殊"指的是一些字符在shell中具有的特殊意义,而不是字符的字面意思, 比如通配符 -- *.)

    引用还可以改掉echo's不换行的"毛病".

    bash$ echo $(ls -l)
    total 8 -rw-rw-r-- 1 bo bo 13 Aug 21 12:57 t.sh -rw-rw-r-- 1 bo bo 78 Aug 21 12:57 u.sh
    
    
    bash$ echo "$(ls -l)"
    total 8
     -rw-rw-r--  1 bo bo  13 Aug 21 12:57 t.sh
     -rw-rw-r--  1 bo bo  78 Aug 21 12:57 u.sh

     

    引用变量:使用双引号还能够阻止单词分割(word splitting). [3] 如果一个参数被双引号扩起来的话, 那么这个参数将认为是一个单元, 即使这个参数包含有空白, 那里面的单词也不会被分隔开.  "单词分割(Word splitting)", 在这种上下文中, 意味着将一个字符串分隔成一些不连续的, 分离的参数.

    当在命令行中使用时, 如果在双引号中包含"!"的话, 那么会产生一个错误(译者注: 比如,echo "hello!"). 这是因为感叹号被解释成历史命令了. 但是如果在脚本中, 就不会存在这个问题, 因为在脚本中Bash历史机制是被禁用的.

    指定变量的类型: 使用declare或者typeset:

    declare或者typeset内建命令(这两个命令是完全一样的)允许指定变量的具体类型.

    -r 只读 :  declare -r var1,   (declare -r var1与readonly var1是完全一样的)

     -i 整型: declare -i number ,  如果把一个变量指定为整型的话, 那么即使没有expr或者let命令, 也允许使用特定的算术运算.

    n=6/3     echo "n = $n" # n = 6/3

    declare -i n ;     n=6/3;       echo "n = $n"   # n = 2 

    -a 数组: declare -a indices 变量indices将被视为数组.

    -f 函数: declare -f. 如果在脚本中使用declare -f, 而不加任何参数的话, 那么将会列出这个脚本之前定义的所有函数. declare -f function_name如果在脚本中使用declare -f function_name这种形式的话, 将只会列出这个函数的名字.

    -x export: declare -x var3 这句将会声明一个变量, 并作为这个脚本的环境变量被导出.

    -x var=$value: declare -x var3=373 declare命令允许在声明变量类型的同时给变量赋值.

    变量的间接引用

    假设一个变量的值是第二个变量的名字. 那么我们如何从第一个变量中取得第二个变量的值呢? 比如,如果a=letter_of_alphabet并且letter_of_alphabet=z, 那么我们能够通过引用变量a来获得z么? 这确实是可以做到的, 它被称为间接引用. 它使用eval var1=$$var2这种不平常的形式.

    a=letter_of_alphabet # 变量"a"的值是另一个变量的名字.

    letter_of_alphabet=z
    直接引用.:  echo "a = $a" # a = letter_of_alphabet 

    间接引用.eval a=$$a    echo "Now a = $a" # 现在 a = z
    $RANDOM: 产生随机整数

    $RANDOM是Bash的内部函数 (并不是常量), 这个函数将返回一个伪随机 [1] 整数, 范围在0 - 32767之间. 它不应该被用来产生密匙.

    双圆括号结构

    双圆括号结构
    与let命令很相似, ((...))结构允许算术扩展和赋值. 举个简单的例子, a=$(( 5 + 3 )), 将把变量"a"设为"5 + 3", 或者8. 然而, 双圆括号结构也被认为是在Bash中使用C语言风格变量操作的一种处理机制.

    (( a = 23 )) # C语言风格的变量赋值, "="两边允许有空格.  echo "a (initial value) = $a"
     (( a++ )) # C语言风格的后置自加.echo "a (after a++) = $a"

    数组: 新版本的Bash支持一维数组. 数组元素可以使用符号variable[xx]来初始化. 另外, 脚本可以使用declare -a variable语句来指定一个数组. 如果想解引用一个数组元素(也就是取值), 可以使用大括号, 访问形式为${variable[xx]}.

    area[11]=23   area[13]=37    area[51]=UFOs   echo ${area[11]} #  需要{大括号}.

    # 数组成员不一定非得是相邻或连续的.  数组的部分成员可以不被初始化.  数组中允许空缺元素, 实际上, 保存着稀疏数据的数组("稀疏数组")。# 没被初始化的数组成员打印为空值(null变量).  从0开始计算数组下标(也就是数组的第一个元素为[0], 而不是[1])

     另一种给数组变量赋值的方法. array_name=( XXX YYY ZZZ ... ), area2=( zero one two three four ),  array_name=([xx]=XXX [yy]=YYY ...), area3=([17]=seventeen [24]=twenty-four)

    for index in 1 2 3 4 5 
    do
      printf " %s " "${Line[index]}"
    done

    为了计算数组的元素个数, 可以使用${#array_name[@]}或${#array_name[*]}.${#array_name}是数组第一个元素的长度, 也就是${array_name[0]}的长度(字符个数).

    echo ${#array[1]}  # 第二个数组元素的长度. 
    echo ${#array[*]} #  数组中的元素个数. 
    echo ${#array[@]} # 数组中的元素个数.
    array2=( [0]="first element" [1]="second element" [3]="fourth element" )
    array=( zero one two three four five )
    echo ${array[0]} # 0
    echo ${array:0} #第一个元素的参数扩展,  从位置0(#0)开始(即第一个字符). 
    echo ${array:1} #ero 第一个元素的参数扩展, 从位置1(#1)开始(即第2个字符).

    大部分标准字符串操作都可以用于数组中.

    提取尾部的子串
    echo ${arrayZ[@]:0}  # one two three four five five  # 所有元素.
    echo ${arrayZ[@]:1} # two three four five five  # element[0]后边的所有元素.
     echo ${arrayZ[@]:1:2} # two three   # 只提取element[0]后边的两个元素.

    子串删除
    # 从字符串的开头删除最短的匹配,  匹配的子串也可以是正则表达式.
    echo ${arrayZ[@]#f*r} # one two three five five  # 匹配将应用于数组的所有元素.  匹配到了"four", 并且将它删除.
    # 从字符串的开头删除最长的匹配
    echo ${arrayZ[@]##t*e} # one two four five five # 匹配将应用于数组的所有元素.  匹配到了"three", 并且将它删除.
    # 从字符串的结尾删除最短的匹配
    echo ${arrayZ[@]%h*e} # one two t four five five# 匹配将应用于数组的所有元素. 匹配到了"hree", 并且将它删除.
    # 从字符串的结尾删除最长的匹配
    echo ${arrayZ[@]%%t*e} # one two four five five# 匹配将应用于数组的所有元素. 匹配到了"three", 并且将它删除.

    子串替换
     # 第一个匹配到的子串将会被替换
    echo ${arrayZ[@]/fiv/XYZ} # one two three four XYZe XYZe
    # 所有匹配到的子串都会被替换
    echo ${arrayZ[@]//iv/YY} # one two three four fYYe fYYe
    # 删除所有的匹配子串
    # 如果没有指定替换字符串的话, 那就意味着'删除'
    echo ${arrayZ[@]//fi/} # one two three four ve ve
    # 替换字符串前端子串
    echo ${arrayZ[@]/#fi/XY} # one two three four XYve XYve
    # 替换字符串后端子串
    echo ${arrayZ[@]/%ve/ZZ} # one two three four fiZZ fiZZ
    echo ${arrayZ[@]/%o/XX} # one twXX three four five five# 为什么?

    ${array_name[@]}或${array_name[*]}都与数组中的所有元素相关.同样的, 为了计算数组的元素个数, 可以使用${#array_name[@]}或{#array_name[*]}.${#array_name}是数组第一个元素的长度, 也就是${array_name[0]}的长度(字符个数)

    script_contents=( $(cat "$0") ) # 将这个脚本的内容($0) ,赋值给数组.

    空数组与包含有空元素的数组, 这两个概念不同

    array1=( '' ) # "array1"包含一个空元素.
    array2=( ) # 没有元素. . . "array2"为空.

    # 添加一个元素到这个数组. 
    array0=( "${array0[@]}" "new1" )
    array1=( "${array1[@]}" "new1" )
     array2=( "${array2[@]}" "new1" )
    
    # 或
     array0[${#array0[*]}]="new2"
     array1[${#array1[*]}]="new2"
     array2[${#array2[*]}]="new2"

    在子shell中设置和操作变量之后, 如果尝试在子shell作用域之外使用同名变量的话, 将会产生令人不快的结果.子shell和父shell看起来其实就是隔离的。

    将echo的输出通过管道传递给read命令可能会产生不可预料的结果. 在这种情况下, read命令的行为就好像它在子shell中运行一样. 可以使用set命令来代替。 

    通过管道将输出传递到任何循环中, 都会引起类似的问题.

    for f in $(find $HOME -type f -atime +30 -size 100k) # 这里没使用管道

    另一种方法:将脚本中读取变量的部分放到一个代码块中, 这样一来, 它们就能在相同的子shell中共享了.

    find $HOME -type f -atime +30 -size 100k | {
      foundone=false
      while read f

      ....

    }

    将数组传递给函数的技术, 然后"返回"一个数组给脚本的主体.使用命令替换将数组中的所有元素(元素之间用空格分隔)赋值给一个变量, 这样就可以将数组传递到函数中了. 之前提到过一种返回值的策略, 就是将要从函数中返回的内容, 用echo命令输出出来, 然后使用命令替换或者( ... )操作符, 将函数的输出(也就是我们想要得返回值)保存到一个变量中. 如果我们想让函数"返回"数组, 当然也可以使用这种策略.

    Pass_Array (){

      passed_array=( `echo "$1"` )
      echo "${passed_array[@]}

    }

    original_array=( element1 element2 element3 element4 element5 )

    argument=`echo ${original_array[@]}`

    returned_array=( `Pass_Array "$argument"` )

    利用双括号结构, 就可以让我们使用C风格的语法, 在for循环和while循环中, 设置或者增加变量.

    一项很有用的技术是, 重复地将一个过滤器的输出(通过管道)传递给这个相同的过滤器, 但是这两次使用不同的参数和选项. 尤其是tr和grep, 非常适合于这种情况.

    在错误的情况下, if-grep test可能不会返回期望的结果, 因为出错文本是输出到stderr上, 而不是stdout.将stderr重定向到stdout上, 就可以解决这个问题

    if ls -l nonexistent_filename 2>&1 | grep -q 'No such file ordirectory'

  • 相关阅读:
    四种会话跟踪技术的对比
    【转载】.NET中使用Redis
    【转载】Windows平台下利用APM来做负载均衡方案
    【转载】Windows平台分布式架构实践
    MVC插件式开发平台
    如何用JS和HTML 做一个桌面炒股小插件【原创】
    如果用HTML5做一个在线视频聊天【原创】
    BraveOS正式版发布,希望大家下载使用
    短期将不再更新更多内容,见谅!
    打造自己的移动绿色版 Python 环境
  • 原文地址:https://www.cnblogs.com/fly-xiang-zhao/p/3674665.html
Copyright © 2011-2022 走看看