最近把Linux Bash编程的知识复习了一遍,大概梳理了一下,作个记录。html
LINUX shell的种类很是之多,可是目前用得最为普遍的仍是Bash,本文也是基于Bash的Shell环境。 下面是一个简单的示例:java
#! /bin/sh echo 'hello world!'
这就是一个最简单的shell脚本了。 第一行的#!
用来告诉系统,这个脚本用什么解释器来执行(说明:sh和bash自己是不一样的Shell,可是在我目前用得CentOS7版本sh和bash是等价的,sh是一个指向bash的符号连接)。 echo
命令输出文本到屏幕程序员
一种方式就是将脚本做为解释器的参数,如:shell
sh test.sh
第二种方式就是授予文件可执行权限编程
chmod +x test.sh 或者 chmod 755 test.sh 执行脚本 ./test.sh
Bash是一种弱类型的语言,你只须要直接定义变量名=value
便可。当须要引用这个变量的时候使用$var_name
或者${var_name}
便可。 $var_name
是${var_name}
的一种简写形式,可是在某些状况下只能使用${var_name}
,例如:数组
# 当须要链接两个变量 your_id=${USER}-on-${HOSTNAME} # 或者会与其余字符串写在一块儿的时候 echo '${num}instance running!'
因此建议在写shell的时候统一使用${var_name}
的风格。 bash中变量是弱类型的,那么无论你传给他的是字符串、数字或者数组他都通通会接收,有时候这就会致使一些问题,好比你想作个计算器,这时候传给了变量一个'ABCDEFG',那么这时候咱们就想这个变量只能接收数字。又或者你想定义一个常量,不想用户修改。那么以上这些咱们可使用declare
来声明变量的一些属性。 declare经常使用参数以下:bash
-r 定义一个只读变量,也可使用"readonly var_name"将变量定义为只读变量 -i 定义变量类型为整型 -a 定义变量类型为数组 -f 定义一个函数 -x 定义一个全局变量dom
当想删除一个变量的时候直接使用unset var_name
便可,注意这个命令不能删除只读变量。编程语言
那么如何向一个脚本传递参数呢? 当咱们想向一个脚本传递参数的时候,直接在执行脚本的命令后面跟参数便可:函数
sh ./test.sh parm1 parm2 parm3
在脚本中使用$n
(n是一个数字,如:$1获取第一个参数)来接收传入的参数便可。下面是可能常常用到的参数变量:
$0 当前脚本的文件名。 $n 传递给脚本或函数的参数。n是一个数字。例如,第一个参数是 $1 。 $# 传递给脚本或函数的参数个数。 $* 传递给脚本或函数的全部参数。 $@ 传递给脚本或函数的全部参数。被双引号 (" ")包含时与$*不一样。 $? 上个命令的退出状态,或函数的返回值。 $_ 上一个命令的最后一个参数
咱们常常用到的Linux命令中有一种叫option的东西,基本模式-optionname
或者--longoptionname
。若是在咱们编写脚本的时候须要option的话,简单的脚本直接把他当普通参数手工处理便可,面对比较复杂的可使用getopt或者getopt。 这里感受有必要提一下set
这个命令,我以为很牛逼、很厉害、颇有用可是我历来没在脚本中使用过的命令。 set
命令用来改变内部脚本的变量或者选项。一种应用就是来改变options来影响脚本的行为。另外一种应用就是利用set `commond`
的输出来重置位置参数。
#!/bin/bash echo "Command-line argument #1 = $1" echo "Command-line argument #2 = $2" echo "Command-line argument #3 = $3" echo "--------------------------------------" echo "利用`uname -a`的输出来重置位置参数" set `uname -a` echo "Field #1 of 'uname -a' = $1" echo "Field #2 of 'uname -a' = $2" echo "Field #3 of 'uname -a' = $3" exit 0 output: [root@localhost study]# sh test_set.sh one two three Command-line argument #1 = one Command-line argument #2 = two Command-line argument #3 = three -------------------------------------- Sets the positional parameters to the output of the commond `uname -a` Field #1 of 'uname -a' = Linux Field #2 of 'uname -a' = localhost.localdomain Field #3 of 'uname -a' = 3.10.0-514.el7.x86_64
目前只支持一维数组!!!!!!!!!! 能够经过如下两种方式来为数组赋值:
#!/bin/bash my_array=(A B C "D") my_array[4]=F my_array[5]="G"
经过${my_array[idx]}
来读取数组中的值,也能够经过${my_array[*]}
或${my_array[@]}
来一次性读取数组中的全部元素。经过${#my_array[*]}
或${#my_array[@]}
来获取数组长度。
#!/bin/bash my_array=(A B C "D") my_array[4]=F my_array[5]="G123" echo "第一个元素:${my_array[0]}" echo "经过*来获取数组全部元素:${my_array[*]}" echo "经过@来获取数组全部元素:${my_array[@]}" echo "经过*来获取数组长度:${#my_array[*]}" echo "经过@来获取数组长度:${#my_array[@]}" output: [root@localhost study]# sh test_arr.sh 第一个元素:A 经过*来获取数组全部元素:A B C D F G123 经过@来获取数组全部元素:A B C D F G123 经过*来获取数组长度:6 经过@来获取数组长度:6
反引号括起来的字符串被bash解释为命令行执行,并以它的输出结果取代整个反引号部分。
#! /bin/sh str=`echo $PATH` echo ${str} output: [root@localhost study]# sh qut.sh /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
单引号扩起来的字符都视做普通字符,全部特殊字符都会失去原油的意义。
#! /bin/sh str='${PATH}' echo ${str} output: [root@localhost study]# sh qut.sh ${PATH}
由双引号括起来的字符,除$、\、’、和”这几个字符还是特殊字符并保留其特殊功能外,其他字符做为普通字符对待。
for循环的通常格式为
for var in [list] do commonds done
下面来看个例子:
#!/bin/bash # 若是后面的list不加引号,则默认以空格分割 for var in A B C D do echo "${var}" done echo;echo # 会将引号内的内容当作一个元素 for var in "A A" "B B B" "C C" do echo "${var}" done echo;echo # 上面两种写法有点相似java的for-each # 若是你用过C,那么你对下面这种写法更熟悉 for ((a=1, b=1; a <= 5 ; a++, b++)) do echo -n "$a-$b " done output: [root@localhost study]# sh for-loop.sh A B C D A A B B B C C 1-1 2-2 3-3 4-4 5-5
while循环的通常格式为
while [ condition ] do commands done
Example:
#!/bin/bash while (( a <= 5 )) do echo -n "$a " let "a+=1" done
break用来跳出整个循环,continue跳出本次循环。
首先来看个例子:
#! /bin/bash b=1+2 echo "${b}" output: [root@localhost study]# sh test.sh 1+2
默认状况下,bash中会把全部的赋值当作字符或字符串,因此看到的输出是1+2
而不是3
。 能够用如下三种方式来作算术运算。
#! /bin/bash # 一、使用`declare`来声明变量为整型。 declare -i b b=1+2 echo "declare:${b}" echo;echo # 二、let命令 let "c=1+1" echo "let:${c}" echo;echo # 三、利用双括号将表达式括起来 ((d=2+3)) echo "((表达式)):${d}" echo;echo # 四、利用单个方括号 echo "[表达式]:$[1+2]" output: [root@localhost study]# sh test.sh declare:3 let:2 ((表达式)):5 [表达式]:3
首先须要注意的是在Linux中“0”表示成功。test命令用来检查某个条件是否成立,它能够进行数值、字符和文件三个方面的测试。
test
这个命令Example:
[root@localhost ~]# test 1 -lt 2 [root@localhost ~]# echo $? 0 [root@localhost ~]# [ 1 -lt 2 ] [root@localhost ~]# echo $? 0 [root@localhost ~]# [[ 2 -eq 2 ]] [root@localhost ~]# echo $? 0 [root@localhost ~]# (( 2 == 2 )) [root@localhost ~]# echo $? 0
数值测试操做符:
Example:
#! /bin/sh a=4 b=5 if [ ${a} -ne ${b} ] then echo "${a} is not equal to ${b} fi echo if [ ${a} != ${b} ] then echo "${a} is not equal to ${b}." fi
: 大于,根据ASCII的排序
Example:
#! /bin/sh str1="" if [ -z ${str1} ] then echo "str1 is null" fi echo str1="abc" if [ -n ${str1} ] then echo "str1 is not null" fi echo str2="cdef" if [ "${str1}" != "${str2}" ] then echo "${str1} is not equal to ${str2}" fi
下面列举一些经常使用的文件操做:
Example:
#! /bin/sh filename="/study/bash/test_file.txt" if [ -e "${filename}" ] then echo "${filename} file exists." fi if [ -f "${filename}" ] then echo "${filename} is a regular file." fi if [ -s "${filename}" ] then echo "${filename} not a zero file." fi
if语句的通常格式:
if condition then commonds fi
if-else语句的格式
if condition1 then commonds elif condition2 then commonds else commonds fi
case语句的通常格式:
case 值 in 模式1) commonds ;; 模式2) commonds ;; esac
在case语句中能够用*
号来匹配任意值。
平时在一些简单的脚本里面基本用不到函数,可是在复杂的脚本中用来组织代码以及封装经常使用逻辑,在bash中仍是很好用的东西。 函数的定义格式以下:
[ function ] funname [()]{ action; [return ${result};] }
一、咱们能够function funname
方式来定义,也能够直接funname()
。 二、能够显示的加return来返回结果或者直接返回最后一条命令的执行结果。
Example:
#! /bin/sh fun(){ echo "it's a function" } fun
向函数传递参数的形式相似执行脚本的传参:fun_name arg1 arg2
Example:
#! /bin/sh fun(){ if [ ${1} -gt ${2} ] then echo "${1} bigger than ${2}." elif [ ${2} -gt ${1} ] then echo "${2} bigger than ${1}." else echo "${1} equal to ${2}." fi } fun 10 20
能够经过read
命令来读取标准输入的值。下面用一个例子来看下在bash中read
的经常使用方式:
#! /bin/sh # 将读取到的值赋值给var1这个变量,当咱们在行尾加\时,按回车会换到下一行继续输入 read var1 echo "var1 is ${var1}" echo "===============================" # 直接read,可使用$REPLY来获取读取到的值 read echo "read $REPLY" echo "===============================" # -r参数会按字符串解释\,而不会换行 read -r mul_line echo "mul_line:${mul_line}" echo "===============================" # 上面的方式都会在屏幕上打印输入值,在交互式的bash中是不友好的,咱们能够加-s选项来屏蔽输入的打印 read -s var2 echo "dont echo input:${var2}" echo "===============================" # 有时候可能不但愿一直等待用户输入,咱们能够用-t选项来这只超时时间 TIMELIMIT=5 read -t $TIMELIMIT var3 if [ -z ${var3}] then var3="time out" fi echo "timed read:${var3}"
下面是测试的输出
[root@localhost study]# sh read_test.sh read value to var2\ new line var1 is read value to var2new line =============================== read had no var read read had no var =============================== read with -r option\ mul_line:read with -r option\ =============================== dont echo input:do not echo input =============================== timed read:time out
在大多数编程语言中,字符串是很是重要、经常使用的数据类型。 一、字符串长度
${#string}
expr length $string
expr "$string" : '.*'
Example:
#! /bin/sh str1="fdhjksdf" echo "${#str1}" echo "`expr length ${str1}`" echo "`expr "${str1}" : '.*'`"
二、匹配子串长度
expr match "$string" '$substr_regxp'
expr "$string" : '$substr_regxp'
Example:
#!/bin/sh str1="str;str-str;strend" sub_regxp="[a-z]*;" echo "`expr match "${str1}" "${sub_regxp}"`" echo "`expr "${str1}" : "${sub_regxp}"`"
三、子串开始索引
expr index $string $substring
Example
#! /bin/sh str="abc;efg;higj" rgx_str=";.*;" echo "`expr index ${str} ${rgx_str}`"
四、字符串截取
${string:position}
截取从postition位置开始的字符串${string:position:length}
从position位置开始截取长度为length长的子串,postition的开始索引为0expr substr $string $position $length
从position位置开始截取长度为length长的子串,postition的开始索引为1expr match "$string" '$sub_regxp'
截取匹配正则的子串expr "$string" : '$sub_regxp'
截取匹配正则的子串
Example
#! /bin/sh str="abc;defg;hijk;lmnpq" echo "\${string:position}" echo "${str:4}" echo "===================================" echo "\${string:position:length}" echo "${str:4:4}" echo "===================================" echo "expr substr \$string \$position \$length" echo "`expr substr ${str} 5 4`" echo "===================================" sub_regxp=".*;\(.*\);" echo "expr match "\$string" '${sub_regxp}'" echo "`expr match "${str}" ${sub_regxp}`" echo "===================================" echo "expr "\$string" : '\$sub_regxp'" echo "`expr "${str}" : ${sub_regxp}`" output: [root@localhost study]# sh sub_str.sh ${string:position} defg;hijk;lmnpq =================================== ${string:position:length} defg =================================== expr substr $string $position $length defg =================================== expr match $string '.*;\(.*\);' hijk =================================== expr $string : '$sub_regxp' hijk
五、删除子串
${string#substring}
,从前面最小匹配删除${string##substring}
,从前面最长匹配删除${string%substring}
,从后面最小匹配删除${string%%substring}
,从后面最小匹配删除
Example
#! /bin/sh str=abcABC123ABCabc reg_str='a*C' echo ${str#$reg_str} # 123ABCabc echo ${str##$reg_str} # abc reg_str='b*c' echo ${str%$reg_str} # abcABC123ABCa echo ${str%%$reg_str} # a
六、子串替换
${string/substring/replacement}
从前面最小匹配替换${string//substring/replacement}
从前面最长匹配替换${string/#substring/replacement}
从后面最小匹配替换${string/%substring/replacement}
从后面最长匹配替换
在bash中提供了相关的参数供咱们进行脚本的调试追踪。
sh [-nvx] script.sh 参数: -n:不执行基本,只检查语法问题 -v:执行脚本前,先将脚本内容输出到屏幕上 -x:将使用到的内容显示到屏幕上