1. 程式人生 > >shell變數與運算

shell變數與運算

shell變數與運算

變數存在於記憶體中。假設變數str,設定或修改變數屬性時,不帶$號,只有引用變數的時才使用$號。也就是說在記憶體中,標記變數的變數名稱是str,而不是$str。

變數資料的儲存方式本身是有型別之分的,分為資料(整數、浮點型)和字元,在java等一些語言中,需要提前宣告變數是哪種型別。但是,在bash中變數比較寬鬆,不需要這麼做。
即,變數可以這麼分為:

  • 強型別:變數不經過強制轉換,它永遠是這個資料型別,不允許隱式的型別轉換。一般定義變數時必須指定型別、參與運算必須符合型別要求;呼叫未宣告變數會產生錯誤。如java,c#
  • 弱型別:語言的執行時會隱式做資料型別轉換。無須指定型別,預設均為字元型;參與運算會自動進行隱式型別轉換;變數無須事先定義可直接呼叫。
    如:bash 不支援浮點數,php

變數命名法則:
1、不能使程式中的保留字:例如if, for
2、只能使用數字、字母及下劃線,且不能以數字開頭
3、見名知義
4、統一命名規則:駝峰命名法

駝峰命名法:將變數名首字母大寫,可以全部單詞的首字母大寫,也可以部分單詞的首字母大寫,方便識別。如:StudentName。

一、變數型別

1.1 區域性變數

生效範圍為當前shell程序;對當前shell之外的其它shell程序,包括當前shell的子shell程序均無效。

檢視程序編號:

echo $$     # 當前程序的程序編號

echo $PPID  #父程序的程序編號

也可以通過
pstree -p  # 檢視父子程序樹

如:NAME=songtai,這個變數只對當前的shell有用,子shell或父shell無法使用。退出當前shell用exit命令

1、變數賦值:name=‘value’ 。
注意:注意等號左右沒有空格。如果有空格就是進行比較運算子的比較運算了。要想保留空格作為一個整體字串,用“”括起來。如:STR=“test space”。

注意:變數賦值也可以直接應用變數或命令:

1.直接引用字串:name=“root"
2.變數:name="$USER"
3.命令:name=`COMMAND` 或 name=$(COMMAND)

2、變數引用::${name} 或$name
如:

[[email protected] ~]#NAME="hello world"    

[[email protected] ~]#echo "we will say $NAME"   
we will say hello world

3、釋放變數:unset name ,注意變數名前不加字首$。
一般賦值完一個變數後,如果後邊不在使用該變數的話,直接釋放該變數。

4、檢視所有的變數:不接任何引數的set或者declare命令,輸出結果中包含了普通變數和環境變數。

5、臨時將普通變數升級為環境(全域性)變數: export name 或者賦值時 export name="value" ,這樣$name就可以在當前shell和子shell中使用,但是退出指令碼或者重新登入shell都會取消export效果。

6、定義只讀變數: readonly name 。這時將無法修改變數值也無法unset變數,只有重新登入shell才能繼續使用只讀變數。

1.2環境變數(全域性變數)

生效範圍為當前shell程序及其子程序。常用大寫字母
env export printenv可以檢視當前使用者的環境變數。
常見的環境變數:HOSTNAME、SHELL、HISTSIZE、USER、PATH、PWD、LANG、HOME、LOGNAME。

變數宣告、賦值:

export  name=VALUE   
declare  -x  name=VALUE

注意:區域性變數通過export升級為全域性變數,並且對子shell產生影響,但是對父shell不產生影響,父shell仍是原來的賦值。

set檢視全部變數,包括區域性變數、全域性變數。

1.3只讀變數

只能宣告,但不能修改和刪除
宣告只讀變數:

  • readonly name
  • declare -r name

檢視只讀變數:

  • readonly –p

    1.4 補充:()、{ }的用處

    ( list ):一次性執行執行(相當於開了個子程序bash,執行後又回到父程序
[[email protected] ~]#( umask 666;touch /data/f1 )   #相當於開了一個子shell,umask改為666後建立了一個檔案f1,然後再返回到父shell
[[email protected] ~]#ll -d /data/f1
----------. 1 root root 0 Nov 29 14:49 /data/f1

[[email protected] ~]#( name=songtai;echo $name )
songtai

{ list }:大括號直接作用於當前shell

[[email protected] ~]#name=songtai
[[email protected] ~]#(  name=sun;echo $name  )
sun
[[email protected] ~]#echo $name
songtai

[[email protected] ~]#{  name=sst;echo $name;  }    # 注意後邊跟;號
sst

[[email protected] ~]#x=1;echo $$;( echo $$;echo $x;x=2;echo $x );echo $x
13196
13196    # 注意;兩個子程序號相同,說明()開的子程序與重新開個子shell還是有不同
1
2
1

1.5位置變數和特殊變數

位置變數:在指令碼程式碼中呼叫通過命令列傳遞給指令碼的引數

$?:上一條程式碼執行的回傳指令,回傳0表示標準輸出,即正確執行,否則為標準錯誤輸出。
>> 對於指令碼中的回傳指令是0還是其他,看指令碼的最後一條命令,如果有標準輸出就是0.否則是其他數值;注意語法錯誤與命令錯誤的區分。
exit [n]:自定義退出狀態碼

$$:當前shell的PID。除了執行bash命令和shell指令碼時,$$不會繼承父shell的值,其他型別的子shell都繼承。

$BASHPID:當前shell的PID,這和"$$"是不同的,因為每個shell的$BASHPID是獨立的。而"$$"有時候會繼承父shell的值。

$!:最近一次執行的後臺程序PID。

$#:統計引數的個數。

[email protected]:所有單個引數,如"a""b""c""d"。

$*:所有引數的整體,如“abcd”。

$0:指令碼名。 注意:對軟連結,$0 顯示的是軟連結的名稱

$1……$n:引數位置。

示例:

vim  /bin/args.sh     # 編寫指令碼 /bin/args.sh   
#!/bin/bash
echo "1st arg is $1"
echo "2st arg is $2"
echo "3st arg is $3"
echo "4st arg is $4"
echo "all args is  $*"
echo "all args is [email protected]"
echo "the args number is $#"
echo "the script is `basename $0`"

[[email protected] ~]#args.sh   aaa nne  ewd  q
1st arg is aaa
2st arg is nne
3st arg is ewd
4st arg is q
all args is  aaa nne ewd q
all args is aaa nne ewd q
the args numbers is 4
the script is args.sh

shfit 換位置,預設換一個。
示例:

vim args.sh     # 編輯指令碼
#!/bin/bash
echo "1st arg is $1"
echo "2st arg is $2"
echo "3st arg is $3"
echo "4st arg is $4"
echo "all args is  $*"
echo "all args is [email protected]"
echo "the args numbers is $#"
echo "the script is $0"
shift

echo "1st arg is $1"
echo "2st arg is $2"
echo "3st arg is $3"
echo "4st arg is $4"
echo "all args is  $*"
echo "all args is [email protected]"
echo "the args numbers is $#"
echo "the script is $0"
shift

echo "1st arg is $1"
echo "2st arg is $2"
echo "3st arg is $3"
echo "4st arg is $4"
echo "all args is  $*"
echo "all args is [email protected]"
echo "the args numbers is $#"
echo "the script is $0"
shift                                      # 儲存退出指令碼


[[email protected] bin]#args.sh  a b c d       # 執行指令碼args.sh  a b c d
1st arg is a
2st arg is b
3st arg is c
4st arg is d
all args is  a b c d
all args is a b c d
the args numbers is 4          # 4個引數           
the script is /root/bin/args.sh
1st arg is b
2st arg is c
3st arg is d
4st arg is 
all args is  b c d
all args is b c d
the args numbers is 3         # 第一個引數去掉,後3個引數依次向前
the script is /root/bin/args.sh
1st arg is c
2st arg is d
3st arg is 
4st arg is 
all args is  c d
all args is c d
the args numbers is 2        # 依上一步的引數,第一個仍然去掉,後2個依次向前
the script is /root/bin/args.sh

注意:當引數達到10以上時,用 ${10} 表示第10個引數 ,否則系統會將10都城
字元1和0的組合。

實驗:編寫指令碼/root/bin/sumid.sh,計算/etc/passwd檔案中的第10個使用者和第20使用者的ID之和。

#!/bin/bash
UID1="`head -n$1 /etc/passwd | cut -d: -f3 |tail -n1`"
UID2="`head -n$2 /etc/passwd | cut -d: -f3 |tail -n1`"
sumid=$[$UID1+$UID2]
echo $sumid

此指令碼有個問題,直接執行sumid.sh不跟引數的話會報錯。改良如下:

#!/bin/bash
[ $# -ne 2 ] && echo "Args num must be 2" && exit   # 引數數量為2個,則接著執行後邊的命令;引數數量不為2,則執行“Args num must be 2”,然後退出!
UID1="`head -n$1 /etc/passwd | cut -d: -f3 |tail -n1`"
UID2="`head -n$2 /etc/passwd | cut -d: -f3 |tail -n1`"
sumid=$[$UID1+$UID2]
echo $sumid

繼續改良:

#!/bin/bash
[ $# -ne 2 ] && echo "Args num must be 2" && exit
[[ ! "$1" =~ ^[0-9]+$ ]] && echo "$1 is not digital" && exit
[[ ! "$2" =~ ^[0-9]+$ ]] && echo "$2 is not digital" && exit
UID1="`head -n$1 /etc/passwd | cut -d: -f3 |tail -n1`"
UID2="`head -n$2 /etc/passwd | cut -d: -f3 |tail -n1`"
sumid=$[$UID1+$UID2]
echo $sumid

二、運算

2.1算術預算

bash中的算術運算:help、let
+, -,*, /, %取模(取餘), **(乘方)

實現算術運算:

(1) let var=算術表示式

x=10
y=20
let z=x+y  或  let z=\$x+\$y
echo  $z
30

let x++
echo $x  
11

let ++x
echo $x
12

(2) var=$[算術表示式]

n=8
m=5
sum=$[n+m]   或   sum=$[$n+$m]
echo $sum
13 

(3) var=$((算術表示式))

(4) var=$(expr arg1 arg2 arg3 ...)
注意:
expr 1 + 2 字元之間有空格
expr 1 * 2 *需要轉義符

(5) declare –i var = 數值

declare -i x=10
declare -i y=20
echo -i z=x+y
echo $z
30

(6) echo ‘算術表示式’ | bc

注意:乘法符號有些場景中需要轉義

(7)bash有內建的隨機數生成器:$RANDOM(0-32767)
echo $[$RANDOM%50] :0-49之間隨機數
示例:

COLOR=$[RANDOM%7+31]  或  COLOR=$((RANDOM%7+31)) 
echo $COLOR  一直是一個數值,除非再執行一次上一條命令,就會換一個數值????
echo -e "\e[1;${COLOR}mcolor\e[0m"

COLOR="$[RANDOM%7+31]";echo -e "\e[1;${COLOR}mcolor\e[0m" color隨機變色

(8) 賦值
增強型賦值:+=, -=, *=, /=, %=
例如:
let x+=2 表示為x+2後自賦值

自增,自減:
let var+=1
let var++ x+1後自賦值
let var-=1
let var--

2.2 邏輯運算

1.真假表示
true : 1
false :0

非:!
!0=1
!1=0

2.&、| 運算

&: and,與運算;有一個假就全假,全是真才為真
0 & 0 = 0
0 & 1 = 0
1 & 0 = 0
1 & 1 = 1

| : or,或運算;全是假才是假,有一個真就是真
0 | 0 = 0
0 | 1 = 1
1 | 0 = 1
1 | 1 = 1

3.&& 短路與;|| 短路或

&& 短路與
0 && 0 = 0
0 && 1 = 0
1 && 0 = 0
1 && 1 = 1

cmd1 && cmd2 :

  • cmd1若為假,那麼cmd2就不需要執行,因為最終結果都為假
  • cmd1若為真,那麼cmd2需要執行,才能判斷最終結果。

|| 短路或
0 || 0 = 0
0 || 1 = 1
1 || 0 = 1
1 || 1 = 1

cmd1 || cmd2 :

  • cmd1若為假,那麼cmd2需要執行,才能判斷最終結果。
  • cmd1若為真,那麼cmd2不需要執行,因為最終結果都為真。

cmd1 && cmd2 || cmd3
cmd1為假,那麼cmd1&&cmd2 的結果必為假,所以需要執行cmd3,才能判斷最終結果。
cmd1為真,那麼要執行cmd2,才能判斷cmd1 && cmd2的結果,若cmd2為真,不執行cmd3,若cmd2為假,執行cmd3.
x=haha;y=xixi;[ $x = $y ] && echo equal || echo qual

^ XOR,抑或:
1 ^ 0 = 1
1 ^ 1 = 0
0 ^ 1 = 1
0 ^ 0 = 0
^異或的兩個值,相同為假,不同為真
^ 的應用示例:兩個數互換

a=4     # 4二進位制表示為 100
b=6    #  6二進位制表示為 110
let c=a^b
echo  $c
1

a、b的值互換示例:
a=4
b=6
a=$[a^b];b=$[a^b];a=[a^b];echo $a $b    # 第一個分號左側a為a^b的值;第二個分號左側b為第一個a的值與b抑或為4(a);第三個分號的[]中,a為a^b的值,b為4,所以左側的a為6(b)。
6 4

2.3 test

  • test EXPRESSION
  • [ EXPRESSION ]
  • [[ EXPRESSION ]]
    注意:EXPRESSION前後必須有空白字元

(1)比較字串

option:
-z STRING True if string is empty.

-n STRING True if string is not empty.

STRING1 = STRING2 True if the strings are equal.

STRING1 != STRING2 True if the strings are not equal. 不等於

STRING1 < STRING2 True if STRING1 sorts before STRING2 lexicographically. ascii碼是否小於ascii碼

STRING1 > STRING2 True if STRING1 sorts after STRING2 lexicographically.

STRING1 =~ STRING2 左側字串是否能夠被右側的PATTERN所匹配

  • 注意: 此表示式一般用於[[ ]]中;擴充套件的正則表示式
str1=aaa
str2=bbb
test $str1  $str2
echo $?
1
注意:
test $str1  $str2
[ $str1 = str2 ]
[[ str1 = str2 ]]    #這個三個表示式的效果相同,注意第二個與第三個裡面的空格!

實驗:判斷一個字串是否為空的字串:

[ -z string ]
[ x"$string" =  "x" ]
[ $string =   ]

(2)比較數字

-eq equal,相等
-ne not equal,不想等
-lt less than,小於
-le less equal,小於等於
-gt graeter than 大於
-ge greater equal 大於等於

實驗:寫指令碼,磁碟利用率大於80報警;

#!/bin/bash
DF_USER="`df | grep  "dev/sd" | tr -s " " % | cut -d% -f5 |sort -nr | head -n1`"
[ "$DF_USER" -ge 80 ] && echo warmming!!   # 注意:[] 內的變數要用“”引起來!!

(3) 檔案比較
> -a FILE True if file exists.
-b FILE True if file is block special.
-c FILE True if file is character special.
-d FILE True if file is a directory.
-e FILE True if file exists.
-f FILE True if file exists and is a regular file.
-g FILE True if file is set-group-id.
-h FILE True if file is a symbolic link.
-L FILE True if file is a symbolic link.
-k FILE True if file has its `sticky' bit set.
-p FILE True if file is a named pipe.
-r FILE True if file is readable by you.
-s FILE True if file exists and is not empty.
-S FILE True if file is a socket.
-t FD True if FD is opened on a terminal.檔案描述符是否在某終端已經開啟
-u FILE True if the file is set-user-id.
-w FILE True if the file is writable by you. 是否有寫許可權
-x FILE True if the file is executable by you.
-O FILE True if the file is effectively owned by you. 你是否屬於該檔案屬主
-G FILE True if the file is effectively owned by your group.
-N FILE True if the file has been modified since it was last read.檔案自從上一次被讀取之後是否被修改過
-u FILE: 是否存在且擁有suid許可權
-g FILE: 是否存在且擁有sgid許可權
-k FILE: 是否存在且擁有sticky許可權

FILE1 -nt FILE2 True if file1 is newer than file2 (according to modification date).
FILE1 -ot FILE2 True if file1 is older than file2.
FILE1 -ef FILE2 True if file1 is a hard link to file2.

實驗:建立使用者,如果該使用者存在則不建立,如果不存在建立之,並將“magedu”作為其密碼。

#1/bin/bash
[ $# -ne 1 ] &&  echo "your args is wrong" && exit
id $1 &> /dev/null && echo "$1 is exist" && exit  || useradd $1 && echo "magedu" | passwd --stdin  $1

#!/bin/bash
id $1 &> /dev/null
[ $? -eq 0 ] && echo "$1 is exist" && exit
useradd $1 && echo magedu | passwd --stdin $1

(4) bash的組合測試檔案
第一種方式:
COMMAND1 && COMMAND2 並且
COMMAND1 || COMMAND2 或者
! COMMAND 非
如:[[ -r FILE ]] && [[ -w FILE ]]

第二種方式:
EXPRESSION1 -a EXPRESSION2 並且
EXPRESSION1 -o EXPRESSION2 或者
! EXPRESSION
必須使用測試命令進行
示例:

[ -z “$HOSTNAME” -o $HOSTNAME "==\"localhost.localdomain" ] && hostname www.magedu.com
[ -f /bin/cat -a -x /bin/cat ] && cat /etc/fstab

三、read

使用read來把輸入值分配給一個或多個shell變數

-p 指定要顯示的提示
-s 靜默輸入,一般用於密碼
-n N 指定輸入的字元長度N
-d ‘字元’ 輸入結束符
-t N TIMEOUT為N秒

#!/bin/bash
read -p  "please input your name: " name
echo your name is $name

#!/bin/bash
read -p  "please input your name: " name
read -s -p "please input your passwd: " passwd
echo your name is $name
echo your passwd is $passwd

實驗:雞兔同籠;輸入總的頭數和總的腳數,算出雞、兔分別有幾隻。

#!/bin/bash
read -p "please input heads num: " H
read -p "please input feets num: " F
C=$[(4*H-F)/2]
R=$[(F-2*H)/2]
echo "the chicken num is $C"
echo "the rabbir num is $R"