Linux shell脚本基础知识

shell脚本

介绍

shell是一个命令解释器,它的作用是解释执行用户输入的命令或者程序

  1. 交互式的方式进行执行
  2. 非交互式的方式进行执行,通过shell脚本来执行

shell脚本的类型

shell脚本语言是弱类型语言,无需定义变量类型即可使用,Linux下主要有两种类型的shell语言

  • Bourne shell   包括Bourne shell(sh)、Korn shell(ksh)、Bourne again shell
  • C shell     包括csh, tcsh两种类型

查看系统默认的shell

echo $SHELL

img

查看系统支持的shell类型

cat /etc/shells

shell脚本执行

shell脚本的执行通常可以按照以下几种方式进行:

  • bash filename / sh filename   这种方式适用于脚本文件本身没有执行权限的时候
  • /path/filename         这种方式适用于脚本文件本身具有执行权限,则可以直接执行
  • source filename / .filename    通过这种方式读入和加载shell脚本文件,然后依次执行shell脚本中的语句,有区别的是,这些语句是在当前父shell脚本的进程中执行。其他两种方法都会启用新的进程执行该脚本。

shell脚本编写规范

遵循以下shell脚本规范,在提高开发效率的同时,降低后期的维护成本

  • 一个规范的shell脚本应该在第一行指出应该由哪个解释器来执行shell脚本中的内容,指定的方式为 #! /bin/bash 或者 #! /bin/sh , sh为bash的软连接,因此两者没有区别
  • ‘#’后面的内容表示注释内容,注释内容不会被执行,也不会被解释器看到
  • shell脚本的开头加版本,版权等信息
  • shell脚本文件的扩展名为 .sh
  • 成对的符号应尽量一次性写出来,然后退格在符号内增加内容,以防止遗漏。这些成对的符号包括: {}、[]、‘’、“” 等
  • 中括号[]两端至少要有1个空格,因此,键入中括号时即留出空格[ ],然后在退格键入中间内容,并确保两端都至少由一个空格
  • 对于流程控制语句,应一次性将格式写完,再添加内容

变量

  1. 变量的赋值方法为: 先写变量名称,紧接着是= ,最后是值。中间无任何空格。 通过echo命令加上 $变量名,即可输出变量的值

  2. 交互式赋值方法,将外部输入读取到变量中:

    read -p "Please input option: " Opt
    echo The user input is: $Opt
    

  3. 赋值时使用引号

    • 双引号 : 允许通过$符号引用其他变量的值
    • 单引号 : 不允许使用$符号引用其变量的值
    • 反引号 : 提取命令执行后的输出结果
    • $() 与 ``含义相同
    #! /bin/bash
    
    A=10
    echo $A
    
    # 无引号
    B=$A+10
    echo $B 
    
    # 双引号
    C="$A+10"
    echo $C
    
    # 单引号
    D=‘$A+10’
    echo $D
    
    E=`ls`
    echo $E
    
    F=$(ls)
    echo ${F}    # 同$F
    

  4. 位置参数

    位置参数是一种在调用shell程序的命令中按照各自的位置决定的变量,是在shell脚本之后输入的参数。位置参数之间使用空格进行分割,shell取第一个位置参数的值赋给shell脚本中的$1,第二个位置参数的值赋给$2,以此类推。$0是一个特殊变量,其内容为当前shell脚本的文件名,所以$0不是一个位置参数。

    #! /bin/bash
    
    # $# 是一个预定义变量,表示位置参数的数量
    
    echo The para count is: $#
    
    echo The bash script name is: $0
    
    echo The first pos para is: $1
    
    echo The second pos para is: $2
    

  5. 预定义变量

    预定义变量是在shell脚本一开始就定义的变量,shell脚本预定义了若干个变量供用户使用,所有的预定义变量都由$和另一个字符构成。常见的shell预定义变量如下所是:

    预定义变量 含义
    $# 位置参数数量  
    $* 所有位置参数的内容
    $? 命令执行后的返回状态  0:正常  1:错误
    $$ 当前进程的进程号
    $! 后台运行的最后一个进程号
    $0 当前执行的进程名 (shell程序的文件名)
    $@ 传递给脚本或者函数的所有参数内容
    示例代码
    #! /bin/bash
    
    echo "Current file name: $0"
    
    echo "First Para is : $1"
    
    echo "Second Para is : $2"
    
    echo "Inputed Para1: $@"
    
    echo "Inputed Para2: $*"
    
    echo "Total Parameter number: $#"
    
    echo "Current process id is: $$"
    
    exit 1
    

    Note
    $* 和 $@ 都表示传递给函数或者脚本的所有参数,不被双引号包含时,都以"$1","$2","$3"...的形式输出所有参数。当被双引号包围时,"$*"会将所有的参数作为一个整体输出,以"$1 $2 $3 ... $n"的形式进行输出。"$@"会将各个参数分开,以"$1" "$2" "$3" ... "$n"的形式输出所有参数。

    示例代码
     #! /bin/bash
    
     # 无双引号
     echo "\$*="$*
    
     # 有双引号
     echo "\"\$*\"=""$*"
    
     # 无双引号
     echo "\$@="$@
    
     # 有双引号
     echo "\"\$@\"=""$@"
    
     # 循环输出
     echo "Print each param in \$@"
     for var in $@
     do 
         echo var is: $var
     done
    
     echo "Print each param in \"\$@\""
     for var in "$@"
     do 
         echo var is: $var
     done
    
     echo "Print each param in \$*"
     for var in $*
     do 
         echo var is: $var
     done
    
     echo "Print each param in \"\$*\""
     for var in "$*"
     do 
         echo var is: $var
     done
    
    

条件判断

测试表达式

  在shell的各种条件结构和流程控制结构中都要进行各种测试,然后根据测试结果的不同执行不同的分支,通常会与if等条件语句相结合,来完成测试判断,减少程序执行错误。

几种条件测试语法
  • test 测试表达式
  • [ 测试表达式 ]
  • [ [ 测试表达式 ] ]
  • ((测试表达式))
条件测试语法比较
test [ ] [ [ ] ] (())
边界是否需要空格 需要 需要 需要 不需要
逻辑操作符 !   -a   -o !   -a   -o !   &&   || !   &&   ||
整数比较操作 -eq -gt -lt -ge -le -eq -gt -lt -ge -le -eq -gt -lt -ge -le
或 = > >= < <=
-eq -gt -lt -ge -le
或 = > >= < <=
字符串比较操作 =   ==   != =   ==   != =   ==   != 不支持
文件操作 -d -f -e -s -r -x -w L
-nt -ot
-d -f -e -s -r -x -w L
-nt -ot
-d -f -e -s -r -x -w L
-nt -ot
不支持
是否支持通配符匹配 不支持 不支持 不支持 不支持
文件测试操作符
文件测试操作符 说明
-d 文件存在且类型为目录,返回真
-f 文件存在且类型为文件,返回真
-e 文件存在返回真
-s 文件存在且大小不为0,返回真
-r 文件存在且可读,返回真
-w 文件存在且可写返回真
-x 文件存在且可执行,返回真
-L 文件存在且为链接文件,返回真
f1 -nt f2 文件f1比文件f2新,返回真
f1 -ot f2 文件f1比文件f2旧,返回真
test 示例代码

#! /bin/bash

echo "Test the file myfile.txt.\n"

if test -e ./myfile.txt ; then              # 文件存在
    echo "File \"myfile.txt\" exist.\n"
    
    if test -d ./myfile.txt ; then          # 是否为目录
        echo "File \"myfile.txt\" is a directory.\n"
    else
        echo "File \"myfile.txt\" is file.\n"

        if test -r ./myfile.txt -a -w ./myfile.txt ; then       # 可读 且 可写
            echo "File \"myfile.txt\" can read and write.\n"
        elif test -x ./myfile.txt ; then                        # 可执行
            echo "File \"myfile.txt\" can execute.\n" 
        else
            echo "File \"myfile.txt\" can not both read and write and execute.\n"
        fi
    fi
else
    echo "File \"myfile.txt\" not exist.\n"
fi

输出:

[ ]示例代码

#! /bin/bash

echo "Test the file myfile.txt.\n"

if [ -e ./myfile.txt ] ; then              # 文件存在
    echo "File \"myfile.txt\" exist.\n"
    
    if [ -d ./myfile.txt ] ; then           # 是否为目录
        echo "File \"myfile.txt\" is a directory.\n"
    else
        echo "File \"myfile.txt\" is file.\n"

        if [ -r ./myfile.txt ] && [ -w ./myfile.txt ] ; then       # 可读 且 可写
            echo "File \"myfile.txt\" can read and write.\n"
        elif [ -x ./myfile.txt ] ; then                        # 可执行
            echo "File \"myfile.txt\" can execute.\n" 
        else
            echo "File \"myfile.txt\" can not both read and write and execute.\n"
        fi
    fi
else
    echo "File \"myfile.txt\" not exist.\n"
fi

字符串测试操作符
常用字符串测试操作符 说明
-n 若字符串长度不为0,返回真
-z 若字符串为空,返回真
str1 == str2 字符串相等
str1 != str2 字符串不相等

note : == 和 !=两端都要有空格


#! /bin/bash

str1="hello"
str2="world"

# test 判断
if test -z $str1 -o -z $str2 ; then
   echo "str1 or st2 is empty.\n"
else
   if test $str1 == $str2 ; then
      echo "str1 equals to str2.\n"
   else
      echo "str1 not equals to str2.\n"
   fi
fi

# [] 判断
if [ -z $str1 ] || [ -z $str2 ] ; then
    echo "str1 or st2 is empty.\n"
else
    if [ $str1 == $str2 ] ; then
        echo "str1 equals to str2.\n"
    else
        echo "str1 not equals to str2.\n"
    fi
fi

比较运算符 (整数比较)
表示方法1 表示方法2
-gt >
-ge >=
-lt <
-le <=
-eq ==

#! /bin/bash

var1=3
var2=4

if test 3 >= 4 ; then
   echo "var1 equals to var2.\n"
else
   echo "var1 not equals to var2"
fi

逻辑运算
表示方法1 表示方法2 说明
-a &&
-o ||
! !
case条件判断语句

  case条件语句相当于if多分支语句,但是其格式更加的工整规范,适合实现系统服务启动脚本应用

```bash

#! /bin/bash

read -n1 -p "Are you sure to start the routine(y or n) ?" choice

echo     # 换行

case $choice in
    [yY])                               # y or Y
    echo "Starting the routine."        # 会自动换行
    ;;
    "n" | "N")                          # n or N
    echo "Exiting the routine."
    ;;
    *)                                 # 相当于default
    echo "Unexpected input."
esac


read -n1 -p "Please input content(char): " content

echo 

case $content in
    [a-z])                               # 范围a-z
    echo "Lower case letter."
    ;;
    [A-Z])
    echo "Uper case letter."
    ;;
    [0-9])
    echo "number."
    ;;
    *)
    echo "Unknown content"
esac

```

循环

数组

  在shell脚本中,采用括号来定义数组,数组元素使用空格隔开


#! /bin/bash

# 数组定义
array=(1  2 3 4 5)

array1[0]=1
array1[1]=1
array1[2]=1
array1[3]=1

# 读取数组元素
ele=${array[0]}
echo -e "First element in array is $ele"    # 添加 -e可实现换行    \c表示不换行

# 获取数组长度
len=${#array[@]}      # 等效于 ${#array[*]}

# 获取特定元素的长度
eleLen=${#array[0]}

echo Array length is: $len

echo Element len is: $eleLen

# 数组遍历 (不带下标)
for var in ${array[@]} ; do
    echo Value: $var
done

array2=("Hello" "world" "test" "thus")
# 数组遍历 (带下标)
for((i=0; i<${#array2[@]};i++)); do
    echo Value is: ${array2[i]}

    if test $i -eq 1 ; then
        echo "Element length is ${#array2[i]}"
    fi
done

for语法格式
for 条件 
do
    命令
done

# 或者

for 条件 ; do
    命令
done
while循环

while 条件
do 
    命令
done
循环控制语句
命令 说明
break n n表示跳出循环的层数,如果省略,则表示跳出整个循环
continue n n表示退到第n层循环继续,如果省略,则表示跳过本次循环
exit n 推出当前shell程序,n为上一次执行的返回值,在下一个shell里面可以通过$?接收n的指

示例代码

#! /bin/bash

# 单层break
while true ; do
    read -n1 -p "Please input a number: " number

    echo

    case $number in
        [0-9])
        echo You input a number: $number
        ;;
        *)
        echo It is not a number.
        break                         # 也可修改为continue
        ;;                            # note 可写可不写
    esac
done

# 多层break
for var1 in 1 2 3 ; do
    for var2 in 7 8 9 ; do
        if [ $var1 -eq 3 -a $var2 -eq 9 ] ; then
            break 2          # 跳出两层循环
        else
            echo -e "Result: $var1 + $var2 \n"
        fi 
    done
done

函数

与其他编程语言一样,shell脚本也支持函数,函数必须线定义再使用

函数的定义格式如下所示:


func_name () {
    command1
    command2
    ...
    [return value]
}

函数的返回值为return语句返回的指,如果没有return语句,则会将最后一条命令执行的结果作为返回值。且shell函数的返回值只能是整数,一般用来表示执行成功与否,0表示成功,其他值表示失败。如果return了其他的数据类型,比如字符串,则会出现错误提示:"numeric argument required".

如果需要返回字符串,可以在函数外预先定义一个字符串变量,在函数中用来接收函数的执行结果,在函数执行完成之后,再去访问预先定义的变量,获取函数的执行结果

#! /bin/bash

# define function 
hello () {
    echo -e "Hellow everyone."
}

# invoke function
hello

带有返回值的函数

#! /bin/bash

# 定义函数
funcSum () {
    read -p "Please input one number: " num1
    read -p "Please input another number: " num2

    return $(($num1 + $num2))
}

# 调用函数
funcSum

# 获取函数调用返回值
ret=$?

echo Sum is: $ret

函数传参

在shell脚本调用函数的时候,可以向其传递参数,在函数内部可通过$n的指来获取参数的值,注意当n>=10的时候,需要通过${n}来获取参数的值.


#! /bin/bash

funcPara () {
    # 参数
    echo "Para 0 is: $0"      # 脚本文件的文件名
    echo "Para 1 is: $1"
    echo "Para 2 is: $2"
    echo "Para 3 is: $3"

    # 获取所有参数
    echo "All para are: $*"
}

funcPara 1 2 3 4

note

unset除了可以删除变量,也可以删除函数,删除函数的时候需要加上.f选项(unset具体使用见后续Linux命令):

$unset .f funcName

此外,如果需要直接从终端调用函数,可以将函数定义在主目录下的.profile文件中,每次登陆后,在命令提示父后面可直接调用函数

输入输出重定向

Unix命令默认从标准输入(stdin)设备获取输入,将结果输出到标准输(stdout)出设备。

输出重定向

命令的输出不仅可以是标准输出,还可以将其转移向文件,这被称为输出重定向,输出重定向的语法为

command > filename

输出重定向会覆盖文件原有的内容,如果希望文件内容不被覆盖,可按照如下方法将输出内容追加到文件末尾


command >> filename
command >> filename
...
输入重定向

类似于输出重定向,Unix命令也可以从文件获取输入,语法为

command < file

重定向之后,本来需要从键盘获取输入的命令会转移到文件读取内容,例如统计某文件的行数:

 wc -l < filename
补充内容

一般情况下,每个Linux命令运行,都会打开以下三个文件:

  • 标准输入文件(stdin)    stdin文件的文件描述符为0,Linux命令默认从stdin读取数据
  • 标准输出文件(stdout)    stdout文件的文件描述符为1,Linux命令默认向stdout输出数据
  • 标准错误文件(stderr)    stderr文件的文件描述符为2,Linux命令默认向stderr写入错误信息

如果希望将stderr重定义到文件,可采用如下的方法:

 command 2>file
 command 2>>file      # 追加

如果希望将标准输出和标准错误输出合并到同一个文件,可采用如下的方法


 # 文件描述符1和文件描述符2合并,重定向到文件输出
 command > file 2>&1
 command >> file 2>&1

 # 读如也可以采取合并

 # n <& m 将输入文件m和n进行合并
/dev/null文件

在执行某个命令的时候,如果希望不再屏幕上显示输出结果,可以将输出结果重定向到/dev/null,这是一个特殊的文件,写入这个文件的内容都会被丢弃,如果从该文件读取内容,将读取不到任何内容。可以将输出重定向到这个文件,起到禁止命令执行结果输出的效果。

 
 command>/dev/null 2>&1

xargs参数过滤

xargs (extended arguments)是给命令传递参数的一个过滤器,也是组合多个命令的一个工具,

posted @ 2022-12-29 17:08  Alpha205  阅读(249)  评论(0编辑  收藏  举报