shell基础


原文链接: shell基础

基本内容

  • 元字符
  • 变量
  • 注释

Shell script: 获取第10+个参数

$10被解释成了$1+0。

解决方法很简单,第10个参数加花括号即可:
    #!/bin/bash
    echo $1 $2 $3 $4 $6 $7 $8 $9 ${10}

元字符

具有特定功能的保留字
IFS(交换字段分隔符):由 三者之一组成(我们常用space),用来拆分command line的每一个词(word)用的,因为shell command line是按词来处理的。
CR(回车键) : 由产生,用来结束command line。
= : 设定变量。
$ : 做变量或运算替换(请不要与 shell prompt 搞混了)。
> : 重定向 stdout(标准输出standard out)。
< : 重定向 stdin(标准输入standard in)。
|: 管道命令。
& : 重定向 file descriptor (文件描述符),或将命令置于后台执行。
( ): 將其內的命令置于 nested subshell (嵌套的子shell)执行,或用于运算或命令替换。
{ }: 將其內的命令置于 non-named function(未命名函数) 中执行,或用在变量替换的界定范围。
; : 在前一个命令结束时,而忽略其返回值,继续执行下一個命令。
&& : 在前一個命令结束时,若返回值为 true,继续执行下一個命令。
|| : 在前一個命令结束时,若返回值为 false,继续执行下一個命令。
!: 执行 history 列表中的命令
; : 分隔同一行的2条shell语句。

主要内容:

  • 转义符
  • 命令替换
  • 变量替换

转义符

如果表达式中包含特殊字符,Shell 将会进行替换,转义字符也是一种替换。
下面的转义字符都可以用在 echo 中:

转义字符含义
\反斜杠
\a警报,响铃
\b退格(删除键)
\f换页(FF),将当前位置移到下页开头
\n换行
\r回车
\t水平制表符(tab键)
\v垂直制表符
╭─sam@sam  ~
╰─$ echo -E "Value of a is \n nnnnnnnnnnn "
Value of a is \n nnnnnnnnnnn

╭─sam@sam  ~
╰─$ echo  "Value of a is \n nnnnnnnnnnn "
Value of a is
nnnnnnnnnnn

╭─sam@sam  ~
╰─$ echo -e "Value of a is \n nnnnnnnnnnn "
Value of a is
nnnnnnnnnnn

可以使用 echo 命令的 -E 选项禁止转义, -e 选项是转义;默认是不转义的;
在没有 -E 的情况下,可承认并可以内置替换以下序列:

    \NNN  字符的ASCII代码为NNN(八进制)
    \\    反斜线
    \a    报警符(BEL)
    \b    退格符
    \c    禁止尾随的换行符
    \f    换页符
    \n    换行符
    \r    回车符
    \t    水平制表符
    \v    纵向制表符

(三) 运算符

主要内容

Bash 支持很多运算符,包括

  • 算数运算符
  • 关系运算符
  • 布尔运算符
  • 字符串运算符
  • 文件测试运算符

算数运算符

原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk 和 expr,expr 最常用。

先来看一个使用算术运算符的例子:

#!/bin/sh
a=10
b=20

val=`expr $a + $b`
echo "a + b : $val"

val=`expr $a - $b`
echo "a - b : $val"

val=`expr $a \* $b`
echo "a * b : $val"

val=`expr $b / $a`
echo "b / a : $val"

val=`expr $b % $a`
echo "b % a : $val"

运行结果:

  a + b : 30
  a - b : -10
  a * b : 200
  b / a : 2
  b % a : 0

注意

  • 表达式和运算符之间要有空格,例如 2+2 是不对的,必须写成 2 + 2,这与我们熟悉的大多数编程语言不一样。
  • 完整的表达式要被 ** ` ** 包含,注意这个字符是反引号
  • 乘号( * )前边必须加反斜杠()才能实现乘法运算;


算术运算符列表

运算符说明举例
+加法expr $a + $b 结果为 30。
-减法expr $a - $b 结果为 10。
*乘法expr $a \* $b 结果为 200。
/除法expr $b / $a 结果为 2。
%取余expr $b % $a 结果为 0。
=赋值a=$b 将把变量 b 的值赋给 a。
==相等。用于比较两个数字,相同则返回 true。[ $a == $b ] 返回 false。
!=不相等。用于比较两个数字,不相同则返回 true。[ $a != $b ] 返回 true。

关系运算符

运算符说明举例
-eq检测两个数是否相等,相等返回 true。[ $a -eq $b ] 返回 true。
-ne检测两个数是否相等,不相等返回 true。[ $a -ne $b ] 返回 true。
-gt检测左边的数是否大于右边的,如果是,则返回 true。[ $a -gt $b ] 返回 false。
-lt检测左边的数是否小于右边的,如果是,则返回 true。[ $a -lt $b ] 返回 true。
-ge检测左边的数是否大等于右边的,如果是,则返回 true。[ $a -ge $b ] 返回 false。
-le检测左边的数是否小于等于右边的,如果是,则返回 true。[ $a -le $b ] 返回 true。

布尔运算符

运算符说明举例
!非运算,表达式为 true 则返回 false,否则返回 true。[ ! false ] 返回 true。
-o或运算,有一个表达式为 true 则返回 true。[ $a -lt 20 -o $b -gt 100 ] 返回 true。
-a与运算,两个表达式都为 true 才返回 true。[ $a -lt 20 -a $b -gt 100 ] 返回 false。

字符串运算符

运算符说明举例
=检测两个字符串是否相等,相等返回 true。[ $a = $b ] 返回 false。
!=检测两个字符串是否相等,不相等返回 true。[ $a != $b ] 返回 true。
-z检测字符串长度是否为0,为0返回 true。[ -z $a ] 返回 false。
-n检测字符串长度是否为0,不为0返回 true。[ -z $a ] 返回 true。
str检测字符串是否为空,不为空返回 true。[ $a ] 返回 true。

文件测试运算符

文件测试运算符用于检测 Unix 文件的各种属性。

操作符说明举例
-b file检测文件是否是块设备文件,如果是,则返回 true。[ -b $file ] 返回 false。
-c file检测文件是否是字符设备文件,如果是,则返回 true。[ -c $file ] 返回 false。
-d file检测文件是否是目录,如果是,则返回 true。[ -d $file ] 返回 false。
-f file检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。[ -f $file ] 返回 true。
-g file检测文件是否设置了 SGID 位,如果是,则返回 true。[ -g $file ] 返回 false。
-k file检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。[ -k $file ] 返回 false。
-p file检测文件是否是具名管道,如果是,则返回 true。[ -p $file ] 返回 false。
-u file检测文件是否设置了 SUID 位,如果是,则返回 true。[ -u $file ] 返回 false。
-r file检测文件是否可读,如果是,则返回 true。[ -r $file ] 返回 true。
-w file检测文件是否可写,如果是,则返回 true。[ -w $file ] 返回 true。
-x file检测文件是否可执行,如果是,则返回 true。[ -x $file ] 返回 true。
-s file检测文件是否为空(文件大小是否大于0),不为空返回 true。[ -s $file ] 返回 true。
-e file检测文件(包括目录)是否存在,如果是,则返回 true。[ -e $file ] 返回 true。

例子:

if [ -r $file ]
then
   echo "File has read access"
else
   echo "File does not have read access"
fi

if [ -w $file ]
then
   echo "File has write permission"
else
   echo "File does not have write permission"
fi

if [ -x $file ]
then
   echo "File has execute permission"
else
   echo "File does not have execute permission"
fi

if [ -f $file ]
then
   echo "File is an ordinary file"
else
   echo "This is sepcial file"
fi

if [ -d $file ]
then
   echo "File is a directory"
else
   echo "This is not a directory"
fi

if [ -s $file ]
then
   echo "File size is zero"
else
   echo "File size is not zero"
fi

if [ -e $file ]
then
   echo "File exists"
else
   echo "File does not exist"
fi

(四) 字符串

主要内容

  • 字符串拼接
  • 字符串长度
  • 字符串截取
  • 字符串查找


字符串是shell编程中最常用最有用的数据类型,字符串可以用单引号,也可以用双引号,也可以不用引号。

单引号和双引号的区别 :

  • 单引号之间的内容成为纯文本,取消所有特殊符号的意义;
  • 双引号仅特殊符号 $ \ ` 这三个的含义保留。



字符串拼接

name="sam"
greeting="hello, "$name" !"
greeting_1="hello, ${name} !"

字符串长度

string="abcd"
echo ${#string} # 4

字符串截取

string="Hello world, shell"
echo ${string:0:4} #输出Hell
echo ${string:1:4} #输出ello
echo ${string:1:1} #输出e

字符串长度

$(awk 'BEGIN {print length("'$str'")}')
$(expr length $str)
$(echo "$str" | wc -c)  # 结果多1

判断字符串为空

[ "$str" = "" ]
[ -z "$str" ]

字符串截取

str="^ababa$"
echo ${str#*b}   # aba$
echo ${str##*b}  # a$
echo ${str%b*}   # ^aba
echo ${str%%b*}  # ^a

1、第一种方法:
${varible##*string} 从左->右截取最后一个string后的字符串
${varible#*string}从左->右截取第一个string后的字符串
${varible%%string*}从右<-左截取最后一个string后的字符串
${varible%string*}从右<-左截取第一个string后的字符串
“*”只是一个通配符可以不要

# %  右向左移除 非贪婪匹配
# %% 右向左移除 贪婪匹配
# #  左向右移除 非贪婪匹配
# ## 左向右移除 贪婪匹配

URL="www.txazo.com"
echo ${URL%.*}   # www.txazo
echo ${URL%%.*}  # www
echo ${URL#*.}   # txazo.com
echo ${URL##*.} # com

${string:index:length} , index 从 0 开始。

字符串查找

string="alibaba is a great company"
echo `expr index "$string" is` #3 , i或s最早出现的地方

待找个更好的

#(五) 数组
主要内容
bash支持一维数组(不支持多维数组),并且没有限定数组的大小。数组元素的下标由0开始编号。获取数组中的元素要利用下标,下标可以是整数或算术表达式,其值应大于或等于0。

  • 数组定义
  • 数组读取
  • 数组长度


数组定义

在Shell中,用括号来表示数组,数组元素用“空格”符号分割开。定义数组的一般形式为:

  array_name=(value1 ... valuen)

例如:

array_name=(value0 value1 value2 value3)

#或者
array_name=(
value0
value1
value2
value3
)

#或者
array_name[0]=value0
array_name[1]=value1
array_name[2]=value2
#可以不使用连续的下标,而且下标的范围没有限制。

数组读取

读取数组元素值的一般格式是:

  ${array_name[index]}

例如:

valuen=${array_name[2]}

使用 @ 或 * 可以获取数组中的所有元素,例如:

${array_name[*]}
${array_name[@]}

数组长度

获取数组长度的方法与获取字符串长度的方法相同, #

例如:

# 取得数组元素的个数
length=${#array_name[@]}
# 或者
length=${#array_name[*]}
# 取得数组单个元素的长度
lengthn=${#array_name[n]}

(六) 条件判断

主要内容

  • if ... else 语句
  • case ... esac 语句
    *

if ... else 语句

Shell 有三种 if ... else 语句:

if ... fi 语句;
if ... else ... fi 语句;
if ... elif ... else ... fi 语句。

if ... fi

if ... fi 语句的语法:

if [ expression ]
then
  Statement(s) to be executed if expression is true
fi

注意:expression 和方括号([ ])之间必须有空格,否则会有语法错误。

例如:

#!/bin/sh
a=10
b=20
if [ $a == $b ]
then
    echo "a is equal to b"
fi
if [ $a != $b ]
then
    echo "a is not equal to b"
fi

if ... else ... fi 语句

if ... else ... fi 语句的语法:

if [ expression ]
then
  Statement(s) to be executed if expression is true
else
  Statement(s) to be executed if expression is not true
fi

例如:

#!/bin/sh
a=10
b=20

if [ $a == $b ]
then
    echo "a is equal to b"
else
    echo "a is not equal to b"
fi

if ... elif ... fi 语句

if ... elif ... fi 语句可以对多个条件进行判断,语法为:

if [ expression 1 ]
then
  Statement(s) to be executed if expression 1 is true

elif [ expression 2 ]
then
  Statement(s) to be executed if expression 2 is true

elif [ expression 3 ]
then
  Statement(s) to be executed if expression 3 is true

else
  Statement(s) to be executed if no expression is true
fi

哪一个 expression 的值为 true,就执行哪个 expression 后面的语句;如果都为 false,那么不执行任何语句。

例如:

#!/bin/sh
a=10
b=20

if [ $a == $b ]
then
    echo "a is equal to b"

elif [ $a -gt $b ]
then
    echo "a is greater than b"

elif [ $a -lt $b ]
then
    echo "a is less than b"

else
    echo "None of the condition met"
fi

test

if ... else 语句也经常与 test 命令结合使用, test 命令用于检查某个条件是否成立,与方括号([ ])类似。

if test $[a] -eq $[b]
then
    echo 'The two numbers are equal!'
else
    echo 'The two numbers are not equal!'
fi

case ... esac 语句

case ... esac 是一种多分枝选择结构。case 语句匹配一个值或一个模式,如果匹配成功,执行相匹配的命令。
case语句格式如下:

case 值 in
模式1)
command1
command2
command3
;;

模式2)
command1
command2
command3
;;

* )
command1
command2
command3
;;
esac

取值后面必须为关键字 in,每一模式必须以右括号结束。取值可以为变量或常数。匹配发现取值符合某一模式后,其间所有命令开始执行直至 ;;。
取值将检测匹配的每一个模式。一旦模式匹配,则执行完匹配模式相应命令后不再继续其他模式。如果无一匹配模式,使用星号 * 捕获该值,再执行后面的命令。

例如:

echo 'Input a number between 1 to 4'
echo 'Your number is:\c'
read num
case $num in
    1)  echo 'You select 1'
    ;;
    2)  echo 'You select 2'
    ;;
    3)  echo 'You select 3'
    ;;
    4)  echo 'You select 4'
    ;;
    *)  echo 'You do not select a number between 1 to 4'
    ;;
esac
option="${1}"

case ${option} in

   -f) FILE="${2}"
      echo "File name is $FILE"
      ;;
   -d) DIR="${2}"
      echo "Dir name is $DIR"
      ;;
   *)
      echo "`basename ${0}`:usage: [-f file] | [-d directory]"
      exit 1 # Command to come out of the program with status 1
      ;;
esac

扩展 (())

条件测试使用 [] 时候,必须保证运算符与算数之间有空格。四则运算也只能借助:expr命令完成。 双括号 (()) 结构语句,可以扩展shell中算数及赋值运算。

使用方法:

语法:

((表达式1,表达式2…))

特点:

1、在双括号结构中,所有表达式可以像c语言一样,如:a++,b--等。 a=a+1

2、在双括号结构中,所有变量可以不加入:“$”符号前缀。

3、双括号可以进行逻辑运算,四则运算

4、双括号结构 扩展了for,while,if条件测试运算

5、支持多个表达式运算,各个表达式之间用逗号“,”分开

(七) 循环

主要内容

  • for循环
  • while循环
  • until循环
  • 跳出循环



for循环

for循环一般格式为:

for 变量 in 列表
do
command1
command2
...
commandN
done

列表是一组值(数字、字符串等)组成的序列,每个值通过空格分隔。每循环一次,就将列表中的下一个值赋给变量。
in 列表是可选的,如果不用它,for 循环使用命令行的位置参数。

  • 例一
    顺序输出当前列表中的数字:

for loop in 1 2 3 4 5
do
    echo "The value is: $loop"
done
  • 例二
    顺序输出字符串中的字符:

for str in 'This is a string'
do
    echo $str
done
  • 例三
    显示主目录下以 .bash 开头的文件:

#!/bin/bash
for FILE in $HOME/.bash*
do
    echo $FILE
done

while循环

while循环用于不断执行一系列命令,也用于从输入文件中读取数据;命令通常为测试条件,条件为真时继续循环。其格式为:

while command
do
  Statement(s) to be executed if command is true
done
  • 例一
    COUNTER计数

COUNTER=0
while [ $COUNTER -lt 5 ]
do
    COUNTER='expr $COUNTER+1'
    echo $COUNTER
done
  • 例二
    读取键盘信息,按结束循环。

echo 'type <CTRL-D> to terminate'
echo -n 'enter your most liked film: '
while read FILM
do
    echo "Yeah! great film the $FILM"
done

until循环

until 循环执行一系列命令直至条件为 true 时停止。until 循环与 while 循环在处理方式上刚好相反。
command 一般为条件表达式,如果返回值为 false,则继续执行循环体内的语句,否则跳出循环。
until 循环格式为:

until command
do
  Statement(s) to be executed until command is true
done

  • 输出 0 ~ 9

a=0
until [ ! $a -lt 10 ]
do
   echo $a
   a=`expr $a + 1`
done

跳出循环

在循环过程中,有时候需要在未达到循环结束条件时强制跳出循环,Shell也使用 break 和 continue 来跳出循环。

break

break命令允许跳出本层所有循环(终止执行后面的所有循环)

break n 表示跳出第 n 层循环。

例如:

    for var1 in 1 2 3
    do
       for var2 in 0 5
       do
          if [ $var1 -eq 2 -a $var2 -eq 0 ]
          then
             break 2
          else
             echo "$var1 $var2"
          fi
       done
    done

如上,break 2 表示直接跳出外层循环。

continue

continue命令与break命令类似,只有一点差别,它不会跳出所有循环,仅仅跳出当前循环。
continue n 表示跳出第 n 层循环。

(八) 函数, 文件包含, 转码工具

主要内容

  • 函数
  • 文件包含
  • 转码工具


函数

函数可以将一个复杂功能划分成若干模块,让程序结构更加清晰,代码重复利用率更高。函数必须先定义后使用。

函数定义

定义格式如下:

function function_name () {
list of commands
[ return value ]
}

function_name () {
list of commands
[ return value ]
}

function 可以省略,清晰起见,还是建议加上。

函数返回值

  • 函数可以显式增加return语句;如果不加,会将最后一条命令运行结果作为返回值。
  • Shell 函数返回值只能是整数,一般用来表示函数执行成功与否,0表示成功,其他值表示失败。如果 return 其他数据,比如一个字符串,往往会得到错误提示:“numeric argument required”。
  • 如果一定要让函数返回字符串,那么可以先定义一个变量,用来接收函数的计算结果,脚本在需要的时候访问这个变量来获得函数返回值。

例:

#!/bin/bash
funWithReturn(){
    echo "The function is to get the sum of two numbers..."
    echo -n "Input first number: "
    read aNum
    echo -n "Input another number: "
    read anotherNum
    echo "The two numbers are $aNum and $anotherNum !"
    return $(($aNum+$anotherNum))
}
funWithReturn
# Capture value returnd by last command
ret=$?
echo "The sum of two numbers is $ret !"

函数删除

删除函数也可以使用 unset 命令,不过要加上 .f 选项,如下所示:

$unset .f function_name

函数调用

调用只需要给出函数名,不需要加括号。

# Define your function here
Hello () {
   echo "Url is http://giveme5.top"
}
# Invoke your function
Hello

函数参数

调用函数时可以向其传递参数。在函数体内部,通过 $n 的形式来获取参数的值,例如,$1表示第一个参数,$2表示第二个参数...
注意,当n>=10时,需要使用 ${n} 来获取参数。 获取第十个参数需要 ${10} , $10 不能获取第十个参数。

例如:

#!/bin/bash
funWithParam(){
    echo "The value of the first parameter is $1 !"
    echo "The value of the second parameter is $2 !"
    echo "The value of the tenth parameter is $10 !"
    echo "The value of the tenth parameter is ${10} !"
    echo "The value of the eleventh parameter is ${11} !"
    echo "The amount of the parameters is $# !"  # 参数个数
    echo "The string of the parameters is $* !"  # 传递给函数的所有参数
}
funWithParam 1 2 3 4 5 6 7 8 9 34 73

输出:

he value of the first parameter is 1 !
The value of the second parameter is 2 !
The value of the tenth parameter is 10 !
The value of the tenth parameter is 34 !
The value of the eleventh parameter is 73 !
The amount of the parameters is 12 !
The string of the parameters is 1 2 3 4 5 6 7 8 9 34 73 !"

另外:
几个特殊变量用来处理参数(更多特殊变量):

特殊变量说明
$#传递给函数的参数个数。
$*显示所有传递给函数的参数。
$@与$*相同,但是略有区别,请查看更多特殊变量
$?函数的返回值。



shift

shift: 参数左移指令, 每执行一次,参数序列顺次左移一个位置,$#的值减1,用于分别处理每个参数,移出去的参数,不再可用。

例:
加法计算器,通过 shift 指令使参数左移,求出所有参数的和

#!/bin/bash
if [ $# -le 0 ]
then
	echo "err!:Not enough parameters"
exit  124
fi
sum=0
while [ $# -gt 0 ]
do
sum=`expr $sum + $1`

shift #参数左移

done
echo $sum

文件包含

文件包含即将外部脚本的内容合并到当前脚本。

可以使用:

. filename

source filename

两种方式的效果相同,简单起见,一般使用点号(.),但是注意点号(.)和文件名中间有一空格。

例如:
创建两个脚本,一个是被调用脚本 subscript.sh,内容如下:

    url="http://see.xidian.edu.cn/cpp/view/2738.html"

一个是主文件 main.sh,内容如下:

    #!/bin/bash
    . ./subscript.sh
    echo $url

注意:被包含脚本不需要有执行权限。

dos2unix

由于编码问题,在windows中开发的脚本导入到Linux系统后执行报错,主要是因为在windows开发保存时没注意编码(UTF8)和换行符(LF)。
在Linux中可以用工具dos2unix解决。

  • 安装
    [root@localhost test]#rpm -ivh /mnt/Packages/dos2unix-6.0.3-4.el7.x86_64.rpm

  • 使用
    [root@localhost ]# dos2unix test.sh
    dos2unix: converting file test.sh to Unix format ...

(九) 色彩

主要内容

  • 色彩

色彩

shell脚本中echo显示内容带颜色显示,echo显示带颜色,需要使用参数-e开启转义

  • 格式

echo -e "\033[字背景颜色;文字颜色 m字符串\033[0m"

\033[ 是固定语法; 0m 是清除所有格式

例:

其中42的位置代表底色,31的位置代表的是字的颜色

注:

1、字背景颜色和文字颜色之间是英文的";"
2、文字颜色后面有个m
3、字符串前后可以没有空格,如果有的话,输出也是同样有空格
  • 颜色定义

可以自己来尝试找出不同颜色搭配

  • 最后面控制选项说明

    \33[0m 关闭所有属性
    \33[1m 设置高亮度
    \33[4m 下划线
    \33[5m 闪烁
    \33[7m 反显
    \33[8m 消隐
    \33[30m — \33[37m 设置前景色
    \33[40m — \33[47m 设置背景色
    \33[nA 光标上移n行
    \33[nB 光标下移n行
    \33[nC 光标右移n行
    \33[nD 光标左移n行
    \33[y;xH设置光标位置
    \33[2J 清屏
    \33[K 清除从光标到行尾的内容
    \33[s 保存光标位置
    \33[u 恢复光标位置
    \33[?25l 隐藏光标
    \33[?25h 显示光标

`