• 8) $(( )) 與 $( ) 還有${ } 差在哪?


                          shell十三问:http://bbs.chinaunix.net/thread-218853-1-1.html

    8) $(( )) 與 $( ) 還有${ } 差在哪? 

    我們上一章介紹了 ( ) 與 { } 的不同,這次讓我們擴展一下,看看更多的變化:$( ) 與 ${ } 又是啥玩意兒呢?

    在 bash shell 中,$( ) 與 ` ` (反引號) 都是用來做命令替換用(command substitution)的。
    所謂的命令替換與我們第五章學過的變量替換差不多,都是用來重組命令行:
    * 完成引號裡的命令行,然後將其結果替換出來,再重組命令行。
    例如:
    1. $ echo the last sunday is $(date -d "last sunday" +%Y-%m-%d)
    复制代码

    如此便可方便得到上一星期天的日期了... ^_^

    在操作上,用 $( ) 或 ` ` 都無所謂,只是我"個人"比較喜歡用 $( ) ,理由是:

    1,  ` ` 很容易與 ' ' ( 單引號)搞混亂,尤其對初學者來說。
    有時在一些奇怪的字形顯示中,兩種符號是一模一樣的(直豎兩點)。
    當然了,有經驗的朋友還是一眼就能分辯兩者。只是,若能更好的避免混亂,又何樂不為呢? ^_^

    2, 在多層次的復合替換中,` ` 須要額外的跳脫( \` )處理,而 $( ) 則比較直觀。例如:
    這是錯的:
    1. command1 `command2 `command3` `
    复制代码

    原本的意圖是要在 command2 `command3` 先將 command3 提換出來給 command 2 處理,
    然後再將結果傳給 command1 `command2 ...` 來處理。
    然而,真正的結果在命令行中卻是分成了 `command2 ` 與 `` 兩段。
    正確的輸入應該如下:
    1. command1 `command2 \`command3\` `
    复制代码


    要不然,換成 $( ) 就沒問題了:
    1. command1 $(command2 $(command3))
    复制代码

    只要你喜歡,做多少層的替換都沒問題啦~~~  ^_^

    不過,$( ) 並不是沒有斃端的...
    首先,` ` 基本上可用在全部的 unix shell 中使用,若寫成 shell script ,其移植性比較高。
    而 $( ) 並不見的每一種 shell 都能使用,我只能跟你說,若你用 bash2 的話,肯定沒問題...  ^_^

    接下來,再讓我們看 ${ } 吧... 它其實就是用來作變量替換用的啦。
    一般情況下,$var 與 ${var} 並沒有啥不一樣。
    但是用 ${ } 會比較精確的界定變量名稱的範圍,比方說:
    1. $ A=B
    2. $ echo $AB
    复制代码

    原本是打算先將 $A 的結果替換出來,然後再補一個 B 字母於其後,
    但在命令行上,真正的結果卻是只會提換變量名稱為 AB 的值出來...
    若使用 ${ } 就沒問題了:
    1. $ echo ${A}B
    2. BB
    复制代码


    不過,假如你只看到 ${ } 只能用來界定變量名稱的話,那你就實在太小看 bash 了﹗
    有興趣的話,你可先參考一下 cu 本版的精華文章:
    http://www.chinaunix.net/forum/viewtopic.php?t=201843

    為了完整起見,我這裡再用一些例子加以說明 ${ } 的一些特異功能:
    假設我們定義了一個變量為:
    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 的組數替換方法可參考如下方法:
    1. ${A[@]} 或 ${A[*]}
    复制代码
    可得到 a b c def (全部組數)
    1. ${A[0]}
    复制代码
    可得到 a (第一個組數),${A[1]} 則為第二個組數...
    1. ${#A[@]} 或 ${#A[*]}  
    复制代码
    可得到 4 (全部組數數量)
    1. ${#A[0]}
    复制代码
    可得到 1 (即第一個組數(a)的長度),${#A[3]} 可得到 3 (第四個組數(def)的長度)
    1. A[3]=xyz  
    复制代码
    則是將第四個組數重新定義為 xyz ...

    諸如此類的.... 
    能夠善用 bash 的 $( ) 與 ${ } 可大大提高及簡化 shell 在變量上的處理能力哦~~~  ^_^

    好了,最後為大家介紹 $(( )) 的用途吧:它是用來作整數運算的。
    在 bash 中,$(( )) 的整數運算符號大致有這些:
    + - * / :分別為 "加、減、乘、除"。
    % :餘數運算
    & | ^ !:分別為 "AND、OR、XOR、NOT" 運算。

    例:
    1. $ a=5; b=7; c=2
    2. $ echo $(( a+b*c ))
    3. 19
    4. $ echo $(( (a+b)/c ))
    5. 6
    6. $ echo $(( (a*b)%c))
    7. 1
    复制代码


    在 $(( )) 中的變量名稱,可於其前面加 $ 符號來替換,也可以不用,如:
    $(( $a + $b * $c)) 也可得到 19 的結果

    此外,$(( )) 還可作不同進位(如二進位、八進位、十六進位)作運算呢,只是,輸出結果皆為十進位而已:
    echo $((16#2a)) 結果為 42 (16進位轉十進位)
    以一個實用的例子來看看吧:
    假如當前的  umask 是 022 ,那麼新建文件的權限即為:
    1. $ umask 022
    2. $ echo "obase=8;$(( 8#666 & (8#777 ^ 8#$(umask)) ))" | bc
    3. 644
    复制代码


    事實上,單純用 (( )) 也可重定義變量值,或作 testing:
    a=5; ((a++)) 可將 $a 重定義為 6 
    a=5; ((a--)) 則為 a=4
    a=5; b=7; ((a < b)) 會得到  0 (true) 的返回值。
    常見的用於 (( )) 的測試符號有如下這些:
        <:小於

        >:大於

        <=:小於或等於

        >=:大於或等於

        ==:等於

      !=:不等於

    不過,使用 (( )) 作整數測試時,請不要跟 [ ] 的整數測試搞混亂了。(更多的測試我將於第十章為大家介紹)

    怎樣?好玩吧..  ^_^  okay,這次暫時說這麼多...
    上面的介紹,並沒有詳列每一種可用的狀態,更多的,就請讀者參考手冊文件囉...

    [ 本帖最后由 網中人 于 2008-12-3 02:39 编辑 ]
     
     
    君子博學而日參省乎己,則知明而行無過矣。

    :() { :|:& }; :      # <--- 這個別亂跑!好奇會死人的!
    echo '十人|日一|十十o' | sed 's/.../& /g'   # <--- 跟你講就不聽,再跑這個好了...

     一、.(source)
    .(点)与source命令一样,从文件中读取并执行命令,无论该文件是否都有可执行权限都能够正确的执行。且是在当前shell下执行,而不是产生一个子shell来执行(我们通常使用“./filename.sh”去执行一个文件是在当前shell下产生一个子shell去执行的)。所以在设置bash的环境的变量时,就必须用该命令或者source命令去执行设置的环境变量才会对当前shell生效,如下:

    for i in /etc/profile.d/*.sh ; do
        if [ -r "$i" ]; then
            . $i
        fi
    done


    二、:
    : 该命令什么都不做,但执行后会返回一个正确的退出代码,即exit 0。比如在if语句中,then后面不想做任何操作,但是又不能空着,这时就可以使用“:”来解决,如下:

    if [ "$i" -ne 1 ];then
        :
    else
        echo "$i is not equal 1"
    fi


    三、()
    () 将多个命令组合在一起执行,相当于一个命令组。

    四、{}
    {}  和()类似,也是将多个命令组合在一起。它们之间的区别是,()是在产生的子shell下执行,而{}是在当前的shell下执行。这与前面讲到是使用".  filename.sh"和"./filename.sh"的区别一样。举一个很简单的例子:
     

    # A=123
     
    (A=abc;echo $A);echo $A
    abc
    123
    # { A=abc;echo $A; };echo $A
    abc
    abc


    从上面的示例可以看出,当在()中赋值的变量,影响的只是自身的子shell,而不能将该值赋给父shell,因为“父亲不能继承儿子”。而在{}中赋值的变量,因为就在当前的shell执行的,所以就能改变原来变量的值。
    注意:()里面两边可以不使用空格,{}里面两边必须使用空格,且最后一个命令也需要以“;”结尾,表示命令结束。

    五、[](test)
    [] 与test命令一样,用于比较值以及检查文件类型。如下:
    1、[ "$A" = 123 ]:是字符串的测试,以测试 $A 是否为 1、2、3 这三个连续的"文字"。
    2、[ "$A" -eq 123 ]:是整数的测试,以测试 $A 是否等于"一百二十三"。
    3、[ -e "$A" ]:是关于文件的测试,以测试 123 这份"文件"是否存在。

    六、[[]]
    [[]]可以说是[]的“增强版”,它能够将多个test命令支持的测试组合起来,例如:

    # [[ (-d "$HOME") && (-w "$HOME") ]] && echo echo "home is a writable directory"  

    home is a writable directory


    至于这两者的区别有位仁兄已经写的很清楚了,我将其整理一下:

    数字测试: -eq -ne -lt -le -gt -ge,[[ ]]同 [ ]一致
    文件测试: -r、-l、-w、-x、-f、-d、-s、-nt、-ot,[[ ]]同 [ ]一致
    字符串测试: > < =(同==) != -n -z,不可使用“<=”和“>=”,[[ ]]同 [ ]
    <

    SPAN style="COLOR: rgb(0,1,2)">一致,但在[]中,>和<必须使用进行转义,即>和<
    逻辑测试: []为 -a -o ! [[ ]] 为&& || !
    数学运算: [] 不可以使用 [[ ]]可以使用+ - */ %
    组合: 均可用各自逻辑符号连接的数字(运算)测试、文件测试、字符测试


    拿这两者对字符串的测试举一个例子,如下:

    # [ a > 1 ] && echo ture || echo false
    ture
    # [[ a > 1 ]] && echo ture || echo false
    ture


    字符串的比较是根据相应的ASCII码来比较的,所以a>1是成立的。如果有兴趣也可以思考一下为什么会出现下面的结果?

    # [[ a > 1 ]] && echo ture || echo false
    ture


    七、(())
    (())专门来做数值运算,如果表达式求值为 0,则设置退出状态为 1;如果求值为非 0 值,则设置为 0。不需要对 (( 和 )) 之间的操作符转义。算术只对整数进行。除 0 会产生错误,但不会产生溢出。可以执行 C 语言中常见的算术、逻辑和位操作。如下:

    # ((i=1+99));echo $i
    100


    也能:

    # i=99;((i++));echo $i
    100


    除此之外,也可以使用$(())直接进行数值运算,如下:

    # echo $((2**3))
    8


    注意:使用 (( )) 时,不需要空格分隔各值和运算符,使用[]和[[ ]] 时需要用空格分隔各值和运算符。 
      
      
    组合判断: 
      
    if [ $a -ne 0 ] && [ $b -lt 3 ] || [ $c -gt 5 ] 
    then 
    #你在这里加其他操作 
    fi 
      
      
    ////////////////////////// 

  • 相关阅读:
    solopi使用方法
    APP耗电量
    Browsersync安装及使用和Nodejs安装
    python下载及安装教程(Windows系统,python3.8.6为例)和界面版本
    SoloXAPP实时性能本地监控平台使用
    ObjectiveC的数据类型
    伪类和伪元素的区别
    Promise
    bash:webpack: command not found
    OC快速入门
  • 原文地址:https://www.cnblogs.com/sysk/p/5670406.html
Copyright © 2020-2023  润新知