Shell学习

一、第一个Shell脚本

#!/bin/bash
echo"Hello World"

#! 是一种约定的标记,告诉系统使用什么解释器执行;

执行Shell脚本主要有两种方式:

#第一种:
#使用chmod命令,给脚本添加可执行权限
chmod +x ./hello.sh

#执行脚本
./hello.sh
#第二种:作为解释器的参数运行脚本呢
/bin/bash hello.sh

#或者
/bin/sh hello.sh

ps:使用第二种方式就不需要在脚本的第一行标记使用的解释器了

以 # 开头的行就是注释,会被解释器忽略。

Shell中还可以使用多行注释,格式如下:

:<<EOF
注释内容...
注释内容...
注释内容...
EOF

EOF 也可以使用其他符号代替,例如:

:<<'
注释内容...
注释内容...
注释内容...
'

:<<!
注释内容...
注释内容...
注释内容...
!

二、Shell变量

2.1 变量的命名

变量名=值 
  1. 赋值“=”号两边不能有空格;
  2. 命名只能使用字母、数字、下划线且不能以数字开头;
  3. 不能使用标点符号;
  4. 不能使用bash中的关键字。(关键字可参考:http://blog.chinaunix.net/uid-25880122-id-2941630.html

2.2 变量的使用

除了显式的标量赋值,还可以通过语句进行赋值,例如:

for file in `ls /etc`
或
for file in $(ls /etc)

循环遍历 /etc目录下的文件名。

ps:在shell中,"$" 和"`"(反引号,引用系统命令,包含的命令会被优先执行)的作用类似。

your_name="zhangsan"
echo $your_name
echo ${your_name}

使用"$"可以使用已经定义好的变量。花括号可用可不用,但为了代码的规范性,需要使用。即可以限定范围,代码也更具可读性。

myUrl="https://www.google.com"
readonly myUrl

使用关键字 readonly 定义只读变量,则变量的值不能够改变。

myUrl="https://www.runoob.com"
unset myUrl

使用关键字 unset 取消变量或函数的定义。

2.3 变量的类型

shell 中有四种类型的变量:用户自定义变量(局部变量)、环境变量、位置参数变量和预定义变量。

  • 用户自定义变量:在脚本或命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量;
  • 环境变量:所有的程序,包括shell启动的程序,都能访问环境变量。当一个 shell 脚本程序开始执行时,一些变量会根据环境设置中的值进行初始化,这些变量通常用大写字母做名字,以便与用户自定义变量做区分,被称为环境变量。环境变量可以在当前 shell 和这个 shell 的所有子 shell 中生效。如果把环境变量写入相应的配置文件(如 /etc/profile ),那么这个环境变量就会在所有的 shell 中生效。系统自带的环境变量的名字不可更改,但是值可以按需更改。用户也可以使用 export 命令在 shell 中自己创建环境变量。
  • 位置参数变量主要用来向脚本中传递参数或数据,变量名不能自定义,变量作用也是固定的。主要有以下几种位置参数变量:
    • $1、$2、$3:脚本程序的参数,分别代表程序的第1个参数、第2个参数、... 程序第10个以上的参数需要用大括号包含,如 ${10};
    • $*:代表命令行中的所有参数。在一个变量中将所有参数列出,各参数之间用环境变量 IFS 中的第一个字符分隔开;
    • $@:和 $* 一样,也包含了命令行中的所有参数,使用时加引号,并在引号中返回每个参数,参数也是分开显示的;
    • 区别:$* 将所有的参数视为一个整体,而 $@ 将所有的参数分别视为单独的个体。一般来说,采用 $@ 来访问脚本程序的参数会比较好,不必担心 IFS 所设置的分隔符为空而导致各参数连在一起分不清楚。
  • 预定义变量是在 bash 中已经定义好了的变量,变量名不能自定义,变量作用也是固定的。实际上,位置参数变量就是预定义变量的一种。
    • $?:保存最后一次执行的命令的返回状态。如果 $? 的值为 0 ,则表明上一个命令成功执行;如果值非 0 ,则表明上一个命令没有成功执行;
    • $!:用于保存后运行的最后一个进程的 PID 号。

三、算数运算

shell 的算术运算符与 C 语言里的差不多,优先级与顺序也相同。但是,由于 shell 中所有变量都是被看做字符串来存储的,因此,要处理算术表达式,还需要使用一些特殊手段将数值型字符串转换成相应的数值。

3.1 使用expr命令对表达式进行求值

a=2
b=3
c=`expr $a + $b`

注意:在 expr 命令所支持的操作符中,“ | 、 & 、< 、<= 、> 、 >= 、 *  ” 这几个需要用 \ 符进行转义再使用。此外,表达式的各字符之间需要用空格隔开

a=5;b=6;c=0
  echo $(expr $a \| $c)       # 输出 5
  echo $(expr $b \& $c)       # 输出 0
  echo $(expr $a \& $b)       # 输出 5
  echo $(expr $a \<= $b)      # 输出 1
  echo $(expr $a \* $b)       # 输出 30
  echo $(expr $a = 2)         # 输出 1

3.2 使用$((...))的方式进行表达式求值(等价于 $[] )

expr 虽然功能强大,但是上面已经提到,在进行一些运算的时候,需要使用 \ 符来进行转义,这对于阅读代码的人来说并不友好。另一方面,expr 命令执行起来其实很慢,因为它需要调用一个新的 shell 来处理 expr 命令。更新更好的一种做法是使用 $((...)) 扩展的方式。只需要将准备求值的表达式放在 $((...)) 的括号中即可进行简单的算术求值。所有支持 $(( ... )) 的shell,都可以让用户在提供变量名称时,无须前置 $ 符

a=5;b=6
 
 echo $(($a + $b))      # 输出 11 。在变量名前加上 $,这在shell中一般是取变量值的意思
 echo $((a + b))            # 输出 11 。可见,变量前不加 $ 也是可以的,为了简便,后面的代码就不加 $ 了 
 echo $((a | b))            # 输出 7 。这里的 | 是按位或操作符
 echo $((a || b))           # 输出 1 。这里的 || 是逻辑或操作符
 echo $((a & b))            # 输出 4 。这里的 & 是按位与操作符
 echo $((a && b))           # 输出 1 。这里的 && 是逻辑与操作符
 echo $((a * b))            # 输出 30
 echo $((a == b))           # 输出 0

五、Shell字符串

your_name="runoob"
# 使用双引号拼接
greeting="hello, "$your_name" !"
greeting_1="hello, ${your_name} !"
echo $greeting  $greeting_1
# 使用单引号拼接
greeting_2='hello, '$your_name' !'
greeting_3='hello, ${your_name} !'
echo $greeting_2  $greeting_3

输出:
hello, runoob ! hello, runoob !
hello, runoob ! hello, ${your_name} !

单引号:单引号中的任何字符都会原样输出,因此字符串中的变量是无效的;

双引号:双引号内可以出现变量也可以出现转义符。

str="abcd"
echo ${str} # 输出 abcd

# 使用 "#"输出字符串长度
echo ${#str} #输出 4

#提取子字符串
echo ${str:1:3} #输出 bcd 

#ps:字符串的索引值从0开始
#查找字符的位置
string="runoob is a great site"
echo `expr index "$string" io`  # 输出 4

#ps:i 和o先查到那个就输出那个的位置,索引是从1开始

六、Shell数组

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

6.1 定义数组

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

# 数组名=(值1 值2 ... 值n)
#例如:
array=(value0 value1 value2 value3)

还可以单独定义数组的各个分量:

array_name[0]=value0
array_name[1]=value1
array_name[n]=valuen
#ps:下标的范围没有限定

6.2 读取数组

#格式:
# ${数组名[下标]}
valuen=${array_name[n]}

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

echo ${array_name[@]}

6.3 获取数组长度

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

七、Shell中的流程控制

7.1 test命令

Shell中的 test 命令用于检查某个条件是否成立,它可以进行数值、字符和文件三个方面的测试。

num1=100
num2=100
if test $[num1] -eq $[num2]
then
    echo '两个数相等!'
else
    echo '两个数不相等!'
fi

输出:两个数相等!

数值符号说明:

  • -eq :等于则为真
  • -ne :不等于则为真
  • -gt :大于则为真
  • -ge: 大于等于则为真
  • -lt :小于则为真
  • -le :小于等于则为真

字符串中运算符说明:

  • = :等于则为真
  • != :不相等则为真
  • -z  字符串名:字符串的长度为零则为真
  • -n 字符串名:字符串的长度不为零则为真
if test -e ./bash
then
    echo '文件已存在!'
else
    echo '文件不存在!'
fi

文件运算中的符号说明:

  • -e 文件名 :如果文件存在则为真
  • -r 文件名 :如果文件存在且可读则为真
  • -w 文件名 :如果文件存在且可写则为真
  • -x 文件名 :如果文件存在且可执行则为真
  • -s 文件名 :如果文件存在且至少有一个字符则为真
  • -d 文件名 :如果文件存在且为目录则为真
  • -f 文件名 :如果文件存在且为普通文件则为真
  • -c 文件名 :如果文件存在且为字符型特殊文件则为真
  • -b 文件名 :如果文件存在且为块特殊文件则为真

7.2 Shell中的if语句

格式:

if condition
then
    command1 
    command2
    ...
    commandN
else
    command
fi

if else-if else 语法格式:

if condition1
then
    command1
elif condition2 
then 
    command2
else
    commandN
fi

示例:判断两个变量是否相等?

a=10
b=20
if [ $a == $b ]
then
   echo "a 等于 b"
elif [ $a -gt $b ]
then
   echo "a 大于 b"
elif [ $a -lt $b ]
then
   echo "a 小于 b"
else
   echo "没有符合的条件"
fi

在实际应用中,if else 语句经常与 test 命令结合使用,如下所示:

num1=$[2*3]
num2=$[1+5]
if test $[num1] -eq $[num2]
then
    echo '两个数字相等!'
else
    echo '两个数字不相等!'
fi

7.3 for循环

for循环一般格式为:

for var in item1 item2 ... itemN
do
    command1
    command2
    ...
    commandN
done

#也可以写成一行
for var in item1 item2 ... itemN; do command1; command2… done;

ps:in 列表可以包含替换、字符串和文件名。

7.4 while语句

while 循环用于不断执行一系列命令,也用于从输入文件中读取数据。其语法格式为:

while condition
do
    command
done

示例代码:

#!/bin/bash
int=1
while(( $int<=5 ))
do
    echo $int
    let "int++"
done

ps:以上实例使用了 Bash let 命令,它用于执行一个或多个表达式,变量计算中不需要加上 $ 来表示变量。

7.5 死循环

while :
do
    command
done

#或者
while true
do
    command
done

#或者
for (( ; ; ))

7.6 until循环

until 循环执行一系列命令直至条件为 true 时停止。

until 循环与 while 循环在处理方式上刚好相反。

一般 while 循环优于 until 循环,但在某些时候—也只是极少数情况下,until 循环更加有用。

until 语法格式:

until condition
do
    command
done

示例:

a=0
#直到a不小于10为止
until [ ! $a -lt 10 ]
do
   echo $a
   a=`expr $a + 1`
done

7.7 多选语句 case...esac

case ... esac 为多选择语句,与其他语言中的 switch ... case 语句类似,是一种多分支选择结构,每个 case 分支用右圆括号开始,用两个分号 ;; 表示 break,即执行结束,跳出整个 case ... esac 语句,esac(就是 case 反过来)作为结束标记。

可以用 case 语句匹配一个值与一个模式,如果匹配成功,执行相匹配的命令。

case ... esac 语法格式如下:

case 值 in
模式1)
    command1
    command2
    ...
    commandN
    ;;
模式2)
    command1
    command2
    ...
    commandN
    ;;
esac

case 工作方式如上所示,取值后面必须为单词 in,每一模式必须以右括号结束。取值可以为变量或常数,匹配发现取值符合某一模式后,其间所有命令开始执行直至 ;;。

取值将检测匹配的每一个模式。一旦模式匹配,则执行完匹配模式相应命令后不再继续其他模式。如果无一匹配模式,可以使用星号 * 作为默认选项捕获该值(类似于default选项),再执行后面的命令。

下面的脚本提示输入 1 到 4,与每一种模式进行匹配:

echo '输入 1 到 4 之间的数字:'
echo '你输入的数字为:'
read aNum
case $aNum in
    1)  echo '你选择了 1'
    ;;
    2)  echo '你选择了 2'
    ;;
    3)  echo '你选择了 3'
    ;;
    4)  echo '你选择了 4'
    ;;
    *)  echo '你没有输入 1 到 4 之间的数字'
    ;;
esac

ps:在Shell的循环中也可以使用break(跳出所有循环)和 continue(跳过本次循环)的语句。

八、Shell中的函数

格式:

[ function ] funname [()]

{
    action;

    [return int;]
}

说明;

  • 可以带function fun() 定义,也可以直接fun() 定义,不带任何参数。
  • 参数返回,可以显示加:return 返回,如果不加,将以最后一条命令运行结果,作为返回值。
demoFun(){
    echo "这是我的第一个 shell 函数!"
}
echo "-----函数开始执行-----"
demoFun
echo "-----函数执行完毕-----"

 

 





 

 

上一篇:06-SHELL脚本编程进阶 20220222 (十)


下一篇:linux--shell脚本记录进程内存变化top | VmRSS | VmSize(内存泄漏)