顯示具有 Bash 標籤的文章。 顯示所有文章
顯示具有 Bash 標籤的文章。 顯示所有文章

2018年5月8日 星期二

Bash Shell | 如何在 script 中引入 include 其它 script

當 script 越寫越複雜後,會想要把共用 function 獨立寫在一個 script,再利用像 C 語言的 #include 方式來引入,此時可以使用 builtin command source 來達成,source 也可以用同義符號 ( . ) 替代。

source filename

or

. filename


底下是一個會將參數累加起來的 function

範例:

共用 script (func.sh)

#!/bin/sh

sum()
{
        total=0
        count=0

        echo The PID of sum\(\) = $$

        if [ "$#" -lt "2" ]; then
                echo "The number of arguments should larger than 1."
                return 255;
        fi

        for i in $@
        do
                ((total += i))
                ((count += 1))

                echo -n $i

                if [ "$count" -lt "$#" ]; then
                        echo -n " + "
                fi
        done

        echo " = $total"

        return $total

}

測試 script (test.sh)

#!/bin/sh

source ./func.sh

echo The PID of $0 = $$
echo ""

sum 1 2
echo The return of sum\(\) = $?
echo ""

sum 3 4 5
echo The return of sum\(\) = $?
echo ""

sum 6
echo The return of sum\(\) = $?

結果:


The PID of ./test.sh = 2075

The PID of sum() = 2075
1 + 2 = 3
The return of sum() = 3

The PID of sum() = 2075
3 + 4 + 5 = 12
The return of sum() = 12

The PID of sum() = 2075
The number of arguments should larger than 1.
The return of sum() = 255

說明:

test.sh 在開頭使用 source 引入 func.sh 後呼叫了三次 sum(),從 PID 可以看出,sum() 是由執行 test.sh 的 process 所執行。

2017年12月26日 星期二

『Bash Shell』如何印出 IFS 變數的內容值與 word splitting 程式範例/完整說明

變數 IFS(Internal Field Separator)是 bash 在做 word splitting 時做為分隔符(delimiter)用,其預設值(default value)是 space, tab 與 newline,這三個字元每一個都可做為分隔符(delimiter)用。word splitting 是 bash 做完 expansions(parameter expansion, command substitution, arithmetic expansion)後,針對非 double quote 且有發生 expansions 的部分,進一步以變數 IFS 來做 word splitting。

範例:

#!/bin/sh

showParams()
{
        echo \$# = $#
        while [ "$1" ]
        do
                echo $1
                shift
        done

}

IFS=/
var1="1 2 3"
var2="a b c"
var3="d/e/f"

showParams "$var1" $var2 $var3

結果:

$# = 5
1 2 3
a b c
d
e
f


說明:
1. 將 IFS 設定為 '/'
2. "$var1" 因為被 double quote,所以 1 2 3 被當做一整個參數。
3. $var2 的內容值是 a b c,因為沒有包含 IFS 的 '/',所以也被當做一整個參數。
4. $var3 的內容值是 d/e/f,包含 IFS 的 '/',所以被 word splitting 成三個參數。

變數 IFS(Internal Field Separator)的預設值是 space, tab, newline,直接印也看不出所以然,但是可以透過 od 的幫助來顯示其內容值。

範例:

#!/bin/sh

echo -n "$IFS" | od -a -t x1 -c

結果:


0000000  sp  ht  nl
         20  09  0a
             \t  \n
0000003

說明:
1. -n 是為了讓 echo 不要丟出 newline。
2. "$IFS" 加上 double quote 才能讓 echo 得到 $IFS 的內容值。

如果修改過變數 IFS(Internal Field Separator)的值後,想將其設定回預設值,可以使用以下方式。

範例:

#!/bin/sh

IFS=$' \t\n'
echo -n "$IFS" | od -a -t x1 -c

結果:

0000000  sp  ht  nl
         20  09  0a
             \t  \n
0000003

2017年12月11日 星期一

Bash Shell | 如何使用管線 Pipeline


管線 Pipeline 可以將一個指令的輸出給另外一個指令當做輸入,格式如下

command1 [ | or |&  command2 ] ...

管線有兩種代表符號
1. ( | )
   將一個指令的輸出(stdout)給另一個指令當作輸入,底下是取得網路介面 IP 的範例

範例:

#!/bin/sh

echo "1. The output of ifconfig eth0:"
echo ""
ifconfig eth0
echo "---------------------------------------------------------"
echo "2. The output of ifconfig eth0 | grep \"inet addr\""
echo ""
ifconfig eth0 | grep "inet addr"
echo "---------------------------------------------------------"
echo "3. The output of ifconfig eth0 | grep \"inet addr\" | awk '{print \$2}'"
echo ""
ifconfig eth0 | grep "inet addr" | awk '{print $2}'
echo "---------------------------------------------------------"
echo "4. The output of ifconfig eth0 | grep \"inet addr\" | awk '{print \$2}' | awk -F':' '{print \$2}'"
echo ""
ifconfig eth0 | grep "inet addr" | awk '{print $2}' | awk -F':' '{print $2}'


結果:

1. The output of ifconfig eth0:

eth0      Link encap:Ethernet  HWaddr 08:00:27:08:DA:E6  
          inet addr:192.168.1.115  Bcast:192.168.1.255  Mask:255.255.255.0
          inet6 addr: fe80::a00:27ff:fe08:dae6/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:33355 errors:0 dropped:0 overruns:0 frame:0
          TX packets:5186 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:6077695 (5.7 MiB)  TX bytes:415741 (405.9 KiB)

---------------------------------------------------------
2. The output of ifconfig eth0 | grep "inet addr"

          inet addr:192.168.1.115  Bcast:192.168.1.255  Mask:255.255.255.0
---------------------------------------------------------
3. The output of ifconfig eth0 | grep "inet addr" | awk '{print $2}'

addr:192.168.1.115
---------------------------------------------------------
4. The output of ifconfig eth0 | grep "inet addr" | awk '{print $2}' | awk -F':' '{print $2}'

192.168.1.115

2. ( |& )
   將一個指令的輸出(stdout 與 stderr)給另一個指令當作輸入

範例:

#/bin/sh

unset var1
echo "1. The output of declare -p var1"
echo ""
declare -p var1
echo "---------------------------------------------------------"
echo "2. use |"
if declare -p var1 | grep "not found"; then
        echo "keyword is found"
else
        echo "keyword is not found"
fi
echo "---------------------------------------------------------"
echo "3. use |&"
if declare -p var1 |& grep "not found"; then
        echo "keyword is found"
else
        echo "keyword is not found"
fi


結果:

1. The output of declare -p var1

./pipelines.sh: line 24: declare: var1: not found
---------------------------------------------------------
2. use |
./pipelines.sh: line 27: declare: var1: not found
keyword is not found
---------------------------------------------------------
3. use |&
./pipelines.sh: line 34: declare: var1: not found
keyword is found

說明:

利用 declare 指令找不到參數名時會往 stderr 送出錯誤訊息的特性,範例中使用兩種不同管線 Pipelines 得到不同的結果。當使用 ( | ) 時,grep 因為沒有收到 declare 往 stderr 送出的錯誤訊息,因此結果是 keyword is not found,而使用 ( |& )時,因為 declare 的 stderr 也會一併給 grep,所以結果是 keyword is found。

2017年12月3日 星期日

『Bash Shell』如何使用內建指令 local Command 宣告區域變數 程式範例/完整說明

全域變數與區域變數的宣告有底下兩件事情要注意

1. 在 shell script 或 function 內宣告變數時,都是全域變數。

範例:

#!/bin/sh

var0="global_var0"

function local_var()
{
        declare -p var0
        var1="global_var1"
        declare -p var1
}

declare -p var0
local_var
declare -p var1

結果:

declare -- var0="global_var0"
declare -- var0="global_var0"
declare -- var1="global_var1"
declare -- var1="global_var1"

說明:

範例中使用 declare -p 來看變數是否存在,在 shell script 內宣告的 var0,在 local_var() 函式也是存在,表示 var0 是全域變數。而 var1 是在 local_var() 函式內宣告,但是離開 local_var() 函式後,var1 還是存在,表示 var1 也是全域變數。

2. 區域變數需要使用 local Command 來宣告,但只有函式 function 內可以使用 local Command 來宣告區域變數。

範例:



#!/bin/sh



function local_var()

{
        local var1="global_var1"
}

local_var
declare -p var1

local var0="global_var0"

結果:

./local_cmd2.sh: line 9: declare: var1: not found
./local_cmd2.sh: line 11: local: can only be used in a function

說明:

1. var1 是在 local_var() 函式內宣告,離開 local_var() 函式後,var1 不存在,表示 var1 是區域變數。

2. 在 shell script 內使用 local Command 宣告 var0 發生錯誤,因為 local Command 只能在函式 function 中被使用。

好站連結
1. http://linux.vbird.org/linux_basic/0320bash.php#variable_range

2017年12月2日 星期六

『Bash Shell』如何寫無窮迴圈 Infinite loop 程式範例/完整說明

無窮迴圈有一個很簡單的寫法,利用 builtin command ( : ) 與 while command。while command 的語法如下。

while test-commands; do consequent-commands; done

每當 test-commands 執行後的離開狀態(exit status)為零時,則執行 do 與 done 之間的 consequent-commands。

範例:

#/bin/sh

while :
do
        echo "This is a infinite loop."
        sleep 1
done

結果:

This is a infinite loop.
This is a infinite loop.

說明:

1. 此範例利用 builtin command ( : )的離開狀態(exit status)都會回傳零,表示成功的特性,搭配 while command 的迴圈功能,來達到無窮迴圈的效果。

2. 如要跳出這個無窮迴圈可以按 ctrl + c 把 script 停掉。

2017年12月1日 星期五

『Bash Shell』如何宣告變數 Variable 程式範例/完整說明

變數 Variable 用來儲存值 Value,變數的型態有四種
1. 整數 (Integer)
2. 索引式陣列 (Indexed Array)
3. 關聯式陣列 (Associative Array)
4. 字串 (String)

變數的型態以變數本身帶有的屬性來表示,屬性如下

1. 屬性 i: 有此屬性的變數是整數 (Integer)
2. 屬性 a: 有此屬性的變數是索引式陣列 (Indexed Array)
3. 屬性 A: 有此屬性的變數是關聯式陣列 (Associative Array)
4. 屬性 r: 有此屬性的變數表示變數值是唯讀 (Read-only)

注意:沒有 i, a, A 三種屬性的就算是字串型態


變數屬性要怎麼查詢可以參考如下文章

Bash Shell | 如何使用內建指令 declare Command

1. 宣告一個整數型態的變數時,使用 declare -i 後面加上變數名稱,語法如下


declare -i name[=value]


範例:


#!/bin/sh


declare -i var0=10


declare -i var1

var1=20

# The value of var2 will be zero if the value is not a number

declare -i var2=A

declare -p var0 var1 var2


結果:


declare -i var0="10"

declare -i var1="20"
declare -i var2="0"

說明:


因為 var2 宣告為整數型態,當 var2 的變數值被指定為非數字時,var2 的變數值變成0(此範例是指定為字元'A'),宣告為整數型態的好處是,做算術運算時不需要使用算數擴展 $(( expression ))。


範例:


#!/bin/sh


var0=10

var1=${var0}+1
echo $var1

declare -i var2=10 var3

var3=${var2}+1
echo $var3

declare -p var0 var1 var2 var3


var1=$(( var0 + 1 ))

echo $var1

declare -p var1


結果:


$var1 = 10+1

$var3 = 11
declare -- var0="10"
declare -- var1="10+1"
declare -i var2="10"
declare -i var3="11"
$var1 = 11
declare -- var1="11"

說明:

1. var0 與 var1 宣告為字串,因此 var1=${var0}+1 被當作字串連接處理,var1 結果為字串 10+1。
2. var2 與 var3 宣告為整數,因此 var3=${var2}+1 被當做整數加法處理,var3 的結果為整數 11。

2. 宣告一個索引式陣列的變數時,主要有兩種方式,語法如下

第一種

name[subscript]=value

第二種

declare -a name[=(value1 value2 ...)]

範例:


#!/bin/sh


array1[0]=a

array1[1]=b
array1[2]=c

declare -a array2=(d e f)

declare -p array1 array2


for i in {0..2}

do
    echo \$array1[$i] = ${array1[$i]}
done

for i in {0..2}
do
    echo \$array2[$i] = ${array2[$i]}
done

結果:



declare -a array1='([0]="a" [1]="b" [2]="c")'

declare -a array2='([0]="d" [1]="e" [2]="f")'

$array1[0] = a
$array1[1] = b
$array1[2] = c
$array2[0] = d
$array2[1] = e
$array2[2] = f

3. 宣告一個關聯式陣列的變數時,使用 declare -A 後面加上變數名稱,語法如下

declare -A name[=([key1]=value1 [key2]=value2 ...)]

範例:


#!/bin/sh


declare -A score_array=([william]=100 [neo]=80 [tom]=90)


declare -p score_array


echo \$score_array[william] = ${score_array[william]}

echo \$score_array[neo] = ${score_array[neo]}
echo \$score_array[tom] = ${score_array[tom]}

結果:


declare -A score_array='([tom]="90" [neo]="80" [william]="100" )'

$score_array[william] = 100
$score_array[neo] = 80
$score_array[tom] = 90

4. 宣告一個字串變數時,主要有兩種方式,語法如下

第一種

str1=[value]

第二種

declare str2[=value]

範例:

#!/bin/sh

str1="I am str1"


declare str2="I am str2"


declare str3

str3="I am str3"

declare -p str1 str2 str3


echo \$str1 = $str1

echo \$str2 = $str2
echo \$str3 = $str3

結果:

declare -- str1="I am str1"
declare -- str2="I am str2"
declare -- str3="I am str3"
$str1 = I am str1
$str2 = I am str2
$str3 = I am str3

5. 宣告唯讀屬性的變數,使用 declare -r 語法如下

declare -r name

範例:

#!/bin/sh

declare -ri var0=10
declare -ra array1=(a b c)
declare -rA score_array=([william]=100 [neo]=80 [tom]=90)
declare -r  str1="I am str1"

var0=11
array1[0]=A
score_array[neo]=60
str1="read-only"

結果:(對個別參數做設定時的錯誤訊息如下)

./declare_ro.sh: line 8: var0: readonly variable
./declare_ro.sh: line 9: array1: readonly variable
./declare_ro.sh: line 10: score_array: readonly variable
./declare_ro.sh: line 11: str1: readonly variable

好站連結
 1. http://linux.vbird.org/linux_basic/0320bash.php#variable

2017年11月30日 星期四

Bash Shell | 如何將程序的輸出入結果做為程式參數 Process Substitution

Process Substitution 有兩種使用方式
1. <(list)
   <(list) 整個可以視為一個檔名看待,因此可以當做另一個程式的參數使用,當然也可
   以當作 redirection 使用。想像 list 程式的輸出會存到一個暫存檔,讓另一個程
   式以參數的形式取得暫存檔,並讀取暫存檔的內容。

   使用多個參數的程式可以利用此功能,例如 cmp 會用到兩個參數,當要比較的內容是由
   其它程式的輸出而來,則不用先將其它程式的輸出分別存到暫存檔,再用 cmp 比較暫存
   檔。底下範例用來比較兩個目錄下是否有一樣的檔案名稱,但不支援子目錄的比較。

範例:

#!/bin/sh

mkdir -p folder1
mkdir -p folder2

touch folder1/file1
touch folder2/file2

# Disable POSIX mode because process substitution is not 
# available in POSIX mode
set +o posix

if cmp -s <(ls folder1) <(ls folder2); then
        echo "folder1 and folder2 are the same."
else
        echo "folder1 and folder2 are different."
fi

touch folder1/file2
touch folder2/file1

if cmp -s <(ls folder1) <(ls folder2); then
        echo "folder1 and folder2 are the same."
else
        echo "folder1 and folder2 are different."
fi

結果:

folder1 and folder2 are different.
folder1 and folder2 are the same.

2. >(list)
   >(list) 整個可以視為一個檔名看待,因此可以當做另一個程式的參數使用,當然也可
   以當作 redirection 使用,想像有一個暫存檔當做 list 程式的輸入,另一個程式
   以參數的形式取得暫存檔,對暫存檔的輸出都變成 list 程式的輸入。

Bash Shell | 如何使用內建指令 type Command

type 指令可以用來查詢某個指令的型態,可能的型態如下
1. 別名 (alias)
2. 函式 (function)
3. 內建指令 (builtin)
4. 檔案 (file)
5. 關鍵字 (keyword)

在終端機下執行底下指令,此範例執行 type 指令並傳入五個參數


範例:


type ls command_not_found_handle source cp if


結果:


ls is aliased to `ls --color=auto'

command_not_found_handle is a function
command_not_found_handle () 

    runcnf=1;
    retval=127;
    [ ! -S /var/run/dbus/system_bus_socket ] && runcnf=0;
    [ ! -x /usr/libexec/packagekitd ] && runcnf=0;
    if [ $runcnf -eq 1 ]; then
        /usr/libexec/pk-command-not-found $1;
        retval=$?;
    else
        echo "bash: $1: command not found";
    fi;
    return $retval
}
source is a shell builtin
cp is /bin/cp
if is a shell keyword

說明:


1. ls 是一個別名 (alias),也可以透過 alias 指令來查詢所有的別名。

2. command_not_found_handle 是一個函式 (function),也可以透過 set 看到函式原型
3. source 是 bash 的一個內建指令 builtin
4. cp 是一個檔案 (file),位置在 /bin 目錄底下
5. if 是一個 bash 的關鍵字 (keyword)

好站連結
1. http://linux.vbird.org/linux_basic/0320bash.php#bash_type

2017年11月6日 星期一

『Bash Shell』如何使用引號 ( " ) ( ' ) Quoting Mechanisms 程式範例/完整說明

  

Bash 有三種 quoting mechanisms

1. 跳脫字元 Escape Character  ( \ )

因為有些字元有特殊意義,如果要保留字元字面上的意義,可以在字元前加上跳脫字元。

在雙引號的字串中表示 ( " ) ( ` ) ( \ )


範例:

          
#!/bin/sh

echo "\""

echo "\`"
echo "\\"

結果:


"

`
\

特例:


如果在跳脫字元後接的是換行字元 (newline),則有連接行的意思


範例:


#!/bin/sh


echo "If you want to print a long message, you can use '\\newline' \

as a line continuation"

結果:


If you want to print a long message, you can use '\newline' as a line continuation


注意:


範例中紅色的跳脫字元加上換行,連接了下面一行


2. 單引號 Single Quotes ( ' )

被兩個單引號包起來的特殊字元不會被解釋為特殊意義,所以不需要加上跳脫字元

範例:


#!/bin/sh


echo ' " ` \ '


結果:


" ` \

          
特例:

單引號無法被放在兩個單引號中間,就算用跳脫字元也一樣不行,因為跳脫字元在兩個單引號中不會被解釋為跳脫字元的意義


範例:


#!/bin/sh


echo ' \' '


結果:


./test.sh: line 3: unexpected EOF while looking for matching `''

./test.sh: line 4: syntax error: unexpected end of file

 

3. 雙引號 Double Quotes ( " )

在兩個雙引號中的特殊字元會有特殊意義,所以如果想要保留特殊字元字面的意義,
可以在前面加上跳脫字元,因為此時跳脫字元會被解釋為跳脫字元的意義

範例;


#!/bin/sh


value=100


echo "$value `date`"

echo "\` \" \\ \A \a"

結果:


100 Mon Nov  6 22:20:02 CST 2017

` " \ \A \a

注意:


如果跳脫字元後不是特殊字元,則跳脫字元會失去其特殊意義,被當作字元被印出,如範例中的 \A 與 \a