$@表示脚本输入的全部参数,在bash脚本中,若$@增加引号("$@"),则包含空格的参数也会被保留,若不增加引号($@),则包含空格的参数会被拆分。
例:
# sa脚本内容如下: pre=: post=: printf "$pre%s$post\n" "$@" #printf "$pre%s$post\n" $@ # 注意$@增加引号和不加引号的区别 $ bash test.sh "a b" :a b: #:a: #:b:
1. 引号
对于单引号、双引号、转义字符开头的空格,命令行解析时将不会被拆分。
$ sa \ this "is a" 'demonstration of' \ quotes\ and\ espaces ## quotes\ and\ espaces中会转义空格 : this: :is a: :demonstration of: : : :quotes and espaces: $ ./sa "a double-quoted single quote, '" "a double-quoted double quote, \"" # 单引号中的转义字符将失效,而双引号中的转义将有效 :a double-quoted single quote, ': :a double-quoted double quote, ": $ ./sa 'a single-quoted double quotation mark, "' :a single-quoted double quotation mark, ": $ ./sa "First argument "'still the first argument' :First argument still the first argument: $ echo '\'line1\'\n\'line2\'' # 单引号中直接嵌套单引号,会有问题,即使增加转义符也无法正常执行 $ echo $'\'line1\'\n\'line2\'' # 增加$符号即可解决单引号中嵌套单引号的问题
2. 花括号
花括号作用于不带引号、以逗号分隔的列表或序列。当作为bash脚本的输入参数时,每个元素作为独立的参数。
$ ./sa {one,two,three} :one: :two: :three: $ ./sa {1..3} #bash3.0后增加 :1: :2: :3: $ ./sa {a..c} :a: :b: :c: $ ./sa pre{d,1}date # 花括号前后的字符串将包含在括号中的每个参数中 :preddate: :pre1date: $ ./sa {{1..3},{a..c}} # 花括号可以嵌套 :1: :2: :3: :a: :b: :c: $ ./sa {1..3}{a..c} # 连续多个的花括号将会逐个递归扩展 :1a: :1b: :1c: :2a: :2b: :2c: :3a: :3b: :3c: $ ./sa {01..13..3} # 4.0版本的bash具有更高特性:数值序列前缀加0、可以指定序列中的步长 :01: :04: :07: :10: :13: $ ./sa {a..h..3} # 也可应用于字母 :a: :d: :g:
3. 波浪号
$ ./sa ~ # 不带引号的~表示当前用户的家目录 :/home/music: $ ./sa ~root # ~后带用户名,将表示该用户的家目录 :/root: $ ./sa "~" "~root" # 引号中的~将不会扩展 :~: :~root: $ dir=~root $ dir2="~root" $ ./sa "$dir" "$dir2" :/root: :~root: $ ./sa ~ws # ~后的用户名若不存在,则不扩展 :~ws:
4. 参数和变量扩展
$ var=whatever $ ./sa "$var" :whatever: $ var=qwerty $ ./sa "${var}" :qwerty: # 通常情况下,{}可选,当位置参数大于9或变量明后紧跟字符时,需要增加{} $ first=Jane $ last=Johnson $ ./sa "$first_$last" #first_是有效变量名 :Johnson: $ ./sa "${first}_$last" :Jane_Johnson:
5. 算术扩展
$ ./sa "$(( 1 + 12 ))" "$(( 12 * 13 ))" "$(( 16 / 4 ))" "$(( 6 - 9 ))" "$(( 10 % 3 ))" :13: :156: :4: :-3: :1: $ ./sa "$(( 3 + 4 * 5 ))" "$(( (3 + 4) * 5 ))" :23: :35: # 将秒转为天、小时、分钟 secs_in_day=86400 secs_in_hour=3600 mins_in_hour=60 secs_in_min=60 days=$(( $1 / $secs_in_day )) secs=$(( $1 % $secs_in_day )) printf "%d:%02d:%02d:%02d\n" "$days" "$(( $secs / $secs_in_hour ))" \ "$(( ($secs / $mins_in_hour) % $mins_in_hour ))" "$(( $secs % $secs_in_min ))"
6. 命令替换
$ wc -l $( date +%Y-%m-%d ).log 1 2019-01-02.log $ wc -l `date +%Y-%m-%d`.log # ``与$()作用相同 1 2019-01-02.log
7. 分词
# 单词拆分基于内部字段分隔符(IFS)的值,默认IFS的为s' \t\n' $ var="this is a multi-word value" $ ./sa $var "$var" :this: :is: :a: :multi-word: :value: :this is a multi-word value: # 当IFS具有其默认值或未设置时,任何默认IFS字符序列都将作为单个分隔符读取 $ var=' spaced > out ' $ ./sa $var :spaced: :out: # 当IFS包含另一个字符和空格时如" :",该" :"可以拆分字段,但每个分空白字符也可以拆分字段,即":"单独也会进行拆分 $ IFS=' :' $ var="query : uiop : :: er" $ ./sa $var :query: :uiop: :: :: :er: # 当IFS仅包含非空字符时,则IFS中的每个字符拆分字段,且空格将保留 $ IFS=: $ var="qwery : uiop : :: er" $ ./sa $var :qwery : : uiop : : : :: : er:
8. 路径扩展
# 命令行中若包含*, ?, [,则将被当做文件匹配模式 $ ./sa h* # 匹配当前路径下以h开头的文件 :hw: $ ./sa *e # 匹配当前路径下以k结尾的文件 :datafile: :errorfile: $ ./sa ?a* # ?表示仅匹配一个字符 :datafile: :sa: # [aceg]表示匹配a,c,e,g中的任意一个 # [h-o]表示匹配h到o中的任意字符 # [[:lower:]]表示匹配小写字母
9. 进程替换
进程替换将为命令创建一个临时文件。<(commond)使命令的输出像文件名一样可用;>(commond)表示可以写入的文件名。
# totalsize在循环外不可用 $ ls -l | > while read perms links owner group size month day time file > do > printf "%10d %s\n" "$size" "$file" > totalsize=$(( ${totalsize:=0} + ${size:-0} )) > done $ echo ${totalsize-unset} ## print "unset" if variable is not set # 命令替换可以使totalsize在循环外可用 $ while read perms links owner group size month day time file > do > printf "%10d %s\n" "$size" "$file" > totalsize=$(( ${totalsize:=0} + ${size:-0} )) > done < <(ls -l *) $ echo ${totalsize-unset} 12879
10. 解析选项
shell脚本中以-连接的选项,可以使用内置的getops解析。格式为:getopts OPTSTRING var
例:parseopts脚本
progname=${0##*/} ## 获取脚本名称 # 默认值 verbose=0 filename= # 列出程序接收的选项列表,这些选项带参数,以:连接 optstring=f:v # while循环调用getopts,直到命令无更多的选项 # 每个选项存储在$opt中,对应的选项参数存储在OPTARG while getopts $optstring opt do case $opt in f) filename=$OPTARG ;; # $OPTARG包含选项对应的参数 v) verbose=$(( $verbose + 1 )) ;; *) exit 1;; esac done # 命令行中移除选项 ## $OPTIND指向下一个,且不解析参数 shift "$(( $OPTIND - 1 ))" # 检查是否有文件传入 if [ -n "$filename" ] then if [ $verbose -gt 0 ] then printf "Filename is %s\n" "$filename" fi else if [ $verbose -gt 0 ] then printf "No filename entered\n" >&2 fi exit 1 fi # 检查文件是否存在 if [ -f "$filename" ] then if [ $verbose -gt 0 ] then printf "Filename %s found\n" "$filename" fi else if [ $verbose -gt 0 ] then printf "File, %s, does not exist\n" "$filename" >&2 fi exit 2 fi # 如果选择了verbose选项,打印保留在命令中的数值参数 if [ $verbose -gt 0 ] then printf "Number of arguments is %d\n" "$#" fi
执行命令结果如下:
$ ./parseopts $ echo $? 1 $ ./parseopts -v No filename entered $ echo $? 1 $ ./parseopts -x ./parseopts: illegal option -- x $ ./parseopts -vf qwerty Filename is qwerty File, qwerty, does not exist $ ./parseopts -vf qwerty -- -x Filename is qwerty File, qwerty, does not exist $ ./parseopts -vf ~/.bashrc .bashrc .bashrc-anaconda3.bak $ ./parseopts -vf ~/.bashrc -- -x Filename is /home/music/.bashrc Filename /home/music/.bashrc found Number of arguments is 1