天天看點

Bash程式設計(4) 參數與變量

1. 變量命名

變量命名隻能使用數字、下劃線、字母,且僅能以下劃線或字母開頭。

變量很少使用單個字母,單個字母一般用于循環或讀取一次性檔案的時候。

例:

while IFS=: read login a b c name e
do
    printf "%-12s %s\n" "$login" "$name"
done < /etc/passwd       

變量名最好能望名知意

2. 變量作用域

若腳本a調用腳本b,則a無法得知b中的變量,除非将b中的變量寫入環境變量中。

腳本中在變量前使用export内置指令,則可以将該變量設定為環境變量。

如:

var=whatever
export var

# bash中可簡寫為export var=whatever       

導出(export)一個變量并不是在任何地方都可以通路該變量,除了子程序外。

例:showvar用于檢驗變量是否被設定

if [[ ${x+Z} = Z ]] ## $x被設定
then
    echo ${x+Z}
    echo Z
    if [[ -n $x ]] ## $x非空
    then
        printf " \$x = %s\n" "$x"
    else
        printf " \$x is set but empty\n"
    fi
else
    printf " %s is not set\n" "\$x"
fi       

若導出了一個變量,其一直保留在環境中,除非直接unset指令。

$ unset x
$ mv variable showvar
$ ./showvar 
 $x is not set
$ x=3
$ ./showvar 
 $x is not set
$ export x=4
$ ./showvar 
 $x = 4
$ x=  ## bash中,對一個變量重新指派,并不會從環境變量中移除該變量
$ ./showvar 
 $x is set but empty       

設定在子shell中的變量對調用它的腳本不可見。子shell包含指令替換,如$(command) 或`command`; 管道中的所有元素;括号中的内容,如( command )

例:

printf "%s\n" ${RANDOM}{,,,,,} |
    while read num
    do
        (( num > ${biggest:=0} )) && biggest=$num
    done
printf "The largest number is: %d\n" "$biggest"       

執行該腳本,發現biggest為0,原因在于循環是管道的一部分,将會在子shell中執行它,而子shell中的變量對該執行腳本不可見。

bash4.2中,新的選項lastpipe,允許管道中最後的程序在目前shell中執行,通過調用:shopt -s lastpipe

3. Shell變量

shell自帶變量BASH_VERSION表示bash的版本。

例:

case $BASE_VERSION in [12].*) echo "You need at least bash3.0 to run this script" >&2; exit 2;;
esac       

提示符PS1,PS2用于指令行中的shell互動時,PS3在使用内置指令select時使用,PS4用于在執行跟蹤模式下,每行之前的列印。

Shell變量包含:

BASH BASHOPTS BASHPID BASH_ALIASES
BASH_ARGC BASH_ARGV BASH_CMDS BASH_COMMAND
BASH_EXECUTION_STRING BASH_LINENO BASH_REMATCH BASH_SOURCE
BASH_SUBSHELL BASH_VERSINFO BASH_VERSION COMP_CWORD
COMP_KEY COMP_LINE COMP_POINT COMP_TYPE
COMP_WORDBREAKS COMP_WORDS COPROC DIRSTACK
EUID FUNCNAME GROUPS HISTCMD
HOSTNAME HOSTTYPE LINENO MACHTYPE
MAPFILE OLDPWD OPTARG OPTIND
OSTYPE PIPESTATUS PPID PWD
RANDOM READLINE_LINE READLINE_POINT REPLY
SECONDS SHELLOPTS SHLVL UID
BASH_COMPAT BASH_ENV BASH_XTRACEFD CDPATH
CHILD_MAX COLUMNS COMPREPLY EMACS
FCEDIT FIGNORE FUNCNEST GLOBIGNORE
HISTCONTROL HISTFILE HISTFILESIZE HISTIGNORE
HISTSIZE HISTTIMEFORMAT HOME HOSTFILE
IFS IGNOREEOF INPUTRC LANG
LC_ALL LC_COLLATE LC_CTYPE LC_MESSAGES
LC_NUMERIC LC_NUMERIC LINES MAIL
MAILCHECK MAILPATH OPTERR PATH
POSIXLY_CORRECT PROMPT_COMMAND PROMPT_DIRTRIM PS1
PS2 PS3 PS4 SHELL
TIMEFORMAT TMOUT TMPDIR auto_resume
histchars       

4. 參數擴充

(1) ${var:-default}和${var-default}:使用預設值

## ${var:-default}用于檢查變量未被設定或為空,若為空或未被設定,則使用預設值
$ var=
$ ./sa ${var:-default}
:default:

## 如果取掉":",則${var-default}僅檢測變量是否為unset
$ ./sa ${var-default}
::
$ unset var
$ ./sa ${var-default}
:default:

# 為filename設定預設值
defaultfile=$HOME/.bashrc
## parse options here
filename=${filename:-"$defaultfile"}       

(2) ${var:+altername}, ${var+alternate}: 使用替換值

## $var設定但為空,是以替換值不生效
$ var=
$ ./sa "${var:+alternate}"
::

## $var被設定,且不為空,則替換值生效
$ ./sa ${var:+alternate}
:alternate:

## 取消冒号後,即使變量為空,隻要被設定,替換值将生效
$ var=
$ ./sa "${var+alternate}"  ## $var被設定
:alternate:
$ unset var
$ ./sa "${var+alternate}" ## $var未被設定
::
$ var=value
$ ./sa "${var+alternate}" ## $var被設定且非空
:alternate:       

常用于變量中增加字元串。例:

## 變量為空時,不想在變量中增加分隔符
$ var=
$ for n in a b c d e f g
> do
>     var="$var $n"
> done
$ ./sa "$var"
: a b c d e f g:

# 為了防止a前面的空格,可以使用參數擴充
$ var=
$ for n in a b c d e f g
> do
>    var="${var:+"$var "}$n" # $var被設定且非空時,則替換為"$var "
> done
$ ./sa "$var"
:a b c d e f g:

# 上面的方法是如下2個方法方法的簡寫
# 1
if [ -n "$var" ]
then
    var="$var $n"
else
    var=$n
fi
# 2
[ -n "$var" ] && var="$var $n" || var=$n       

(3) ${var:=default}, ${var=default}: 賦初始值

${var:=default}與${var:-default}類似,不同之處在于它也向變量賦初始值。

$ unset n
$ while :
> do
>     echo :$n:
>     [ ${n:=0} -gt 3 ] && break ## 如果未被設定或為空,則将$n設為0
>     n=$(( $n + 1 ))
> done
::
:1:
:2:
:3:
:4:       

 (4) ${var:?message}, ${var?message}: 如果為空或未被設定,則顯示錯誤資訊到标準錯誤輸出

$ ./checkarg 
./checkarg: line 2: 1: An argument is required
$ ./checkarg x
./checkarg: line 2: 2:  Two arguments are required
$ ./checkarg '' ''
./checkarg: line 5: 1:  A non-empty argument is required
$ ./checkarg x ''
./checkarg: line 5: 2: Two non-empty arguments are required
$ ./checkarg x x
Thank you.       

(5) ${#var}: 變量内容的長度

read passwd
if [ ${#passwd} -lt 8 ]
then
    printf "Password is too short: %d characters\n" "$#" >&2
    exit 1
fi       

(6)  ${var%PATTERN}: 從變量結尾移除最短比對

$ var=Toronto
$ var=${var%o*}
$ printf "%s\n" "$var"
Toront
$ printf "%s\n" "${var%o*}"
Tor

# dname, 列印檔案路徑的部分
case $1 in
    */*) printf "%s\n" "${1%/*}" ;;
    *) [ -e "$1" ] && printf "%s\n" "$PWD" || echo "." ;;
esac

$ ./dname /etc/passwd
/etc
$ ./dname bin
.       

(7) ${var%%PATTERN}: 從變量結尾移除最長比對

$ var=Toronto
[email protected]:~/songwang4/test$ ./sa "${var%%o*}"
:T:       

(8) ${var#PATTERN}: 從變量起始處移除最短比對

$ var=Toronto
$ ./sa "${var#*o}"
:ronto:       

(9) ${var##PATTERN}: 從變量起始處移除最長比對

scriptname=${0##*/} ## /home/chris/bin/script => script       

(10) ${var//PATTERN/STRING}: 使用STRING替換所有的PATTERN執行個體

## 密碼隐藏顯示
$ passwd=zx01.=+-a
$ printf "%s\n" "${passwd//?/*}"
*********

## 單斜杠"/"僅替換第一個比對的字元
$ printf "%s\n" "${passwd/a/*}"
zx01.=+-*       

(11) ${var:OFFSET:LENGTH}: 傳回$var的子串

OFFSET表示起始位置,LENGTH表示截取長度。

$ var=Toronto
$ ./sa "${var:3:2}"
:on:
$ ./sa "${var:3}"
:onto:

## 負數表示從字元串結尾開始
$ ./sa "${var: -3}"  ## 注意-3前面的空格,防止與變量預設值擴充混淆
:nto:       

(12) ${!var}: 間接引用

若變量中含有另一個變量的名稱,bash可以使用間接引用。

$ x=yes
$ a=x
$ ./sa ${!a}
:yes:

## 使用eval也可實作類似功能
$ eval "./sa \$$a"
:yes:       

 (13) ${var^PATTERN}: 大寫轉換

如果比對PATTERN,則var的第一個字元轉換為大寫;當為^^時,比對到PATTERN的所有字元轉換為大寫;如果PATTERN為空,則所有都比對。

$ var=toronto
$ ./sa "${var^}" ## PATTERN為空且為^,時,第一個字元大寫轉換
:Toronto:
$ ./sa "${var^[n-z]}"
:Toronto:

$ ./sa "${var^^[a-o]}" ## 比對從a到o的所有字元
:tOrONtO
$ ./sa "${var^^}"  ## 比對所有
:TORONTO:       

(14) ${var,PATTERN}:小寫轉換

與${var^PATTERN}用法相同,差別在于小寫轉換

$ var=TORONTO
$ ./sa "${var,,}"
:toronto:
$ ./sa "${var,,[N-Q]}"
:ToRonTo:       

 (15) ${var~}:大小寫取反

$ var=Toronto
$ ./sa "${var~}"
:toronto:
$ ./sa "${var~~}"
:tORONTO:       

 5. 位置參數

位置參數由$1,...${10}引用,全部參數可以使用"$@"或"$*",大于9的索引參數需要以大括号封裝。

不帶參數的shift指令将會移除第一個參數,并将剩餘的參數前移一位,如曆史的$2将會變為$1。帶參數的shift将會移動指定的大小。

$ shift 3 ## 移除前3個參數
$ shift "$#" ## 移除所有參數
$ shift "$(( $# -2 ))"  ## 保留最後2位參數

## 使用每一個參數,可以通過如下2種方法
## 1. 使用$@
 for param in "#@"  ## 或 for param
do
    : do something with $param
done

## 2. 使用shift
while (( $# ))
do
    : do something with $1
    shift
done       

6. 數組

(1) 整型索引數組

數組元素索引從0開始。Bash中的數組是稀疏的,即索引号可以不連續。

# BASH_VERSINFO表示目前運作shell的版本資訊,第一個元素表示主版本号,第二個表示次版本号
$ printf "%s\n" "${BASH_VERSINFO[0]}"
4
$ printf "%s\n" "${BASH_VERSINFO[1]}"
3      

可以使用*和@列印數組所有元素。當使用"*"且數組被引号括起,則數組元素不會拆分,若沒有引号則會被拆分;當使用"@"時,均會被拆分

$ printf "%s\n" "${BASH_VERSINFO[*]}" # 不拆分
4 3 48 1 release x86_64-pc-linux-gnu
$ printf "%s\n" ${BASH_VERSINFO[*]} # 拆分
4
3
48
1
release
x86_64-pc-linux-gnu
$ printf "%s\n" ${BASH_VERSINFO[@]} # 使用@時,預設均拆分,對僅限于數組元素間的拆分,而不會涉及到元素内容
4
3
48
1
release
x86_64-pc-linux-gnu

## 提取數組中第二個和第三個元素
$ printf "%s\n" "${BASH_VERSINFO[@]:1:2}"
3
48

## 當使用*或@時,"#"傳回數組長度,如果指定數組元素時,則傳回數組元素的長度
$ printf "%s\n" "${#BASH_VERSINFO[*]}"
6
$ printf "%s\n" "${#BASH_VERSINFO[2]}" "${#BASH_VERSINFO[5]}"
2
19

## 數組通過下标指派
$ name[0]=1
$ name[42]=2

## 非連續下标的好處,原因在于對它們的操作将變得簡單,直接取下一個未被設定的元素即可
$ unset a
$ a[${#a[@]}]="1 $RANDOM"
$ a[${#a[@]}]="2 $RANDOM"
$ a[${#a[@]}]="3 $RANDOM"
$ a[${#a[@]}]="4 $RANDOM"
$ printf "%s\n" "${a[@]}"
1 20457
2 19494
3 30222
4 19320

## 數組也可以僅使用如下一個指令生成
$ province=( Quebec Ontario Manitoba )
$ printf "%s\n" "${province[@]}"
Quebec
Ontario
Manitoba

## +=用于在數組索引末位添加值
$ province+=( Saskatchewan )
$ province+=( Alberta "British Columbia" "Nova Scotia" )
$ printf "%-25s %-25s %s\n" ${province[@]}
Quebec                    Ontario                   Manitoba
Saskatchewan              Alberta                   British
Columbia                  Nova                      Scotia
$ printf "%-25s %-25s %s\n" "${province[@]}"
Quebec                    Ontario                   Manitoba
Saskatchewan              Alberta                   British Columbia
Nova Scotia       

(2) 關聯數組 

bash 4後,允許使用字元串作為下标,且數組必須先聲明。

$ declare -A array
$ for subscript in a b c d e
> do
>     array[$subscript]="$subscrupt $RANDOM"
> done
$ printf ":%s:\n" "${array["c"]}" ## 列印單個元素
: 25475:
$ printf ":%s:\n" "${array[@]}"  ## 列印整個數組
: 26862:
: 32278:
: 25475:
: 12284:
: 22720:       

轉載于:https://www.cnblogs.com/mengrennwpu/p/10229685.html