Shell脚本学习

参考链接: https://blog.csdn.net/yuioplv/article/details/81006250

Shell 脚本介绍

#!/bin/bash

"#!"是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行。

shell脚本并不能作为正式的编程语言,因为它是在Linuxshell中运行的,所以称为shell脚本。

  • shell是一种脚本语言;
  • 可以使用逻辑判断、循环等语法;
  • 可以自定义函数;
  • shell是系统命令的集合;
  • shell脚本可以实现自动化运维,能大大增加我们的运维效率;

shell 变量

变量类型

运行 shell 时,会同时存在三种变量:

  • 局部变量
    局部变量在脚本或命令中定义,仅在当前 shell 实例中有效,其他 shell 启动的程序不能访问局部变量。
  • 环境变量
    所有的程序,包括 shell 启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行。必要的时候 shell 脚本也可以定义环境变量。
  • shell 变量
    shell 变量是由 shell 程序设置的特殊变量。shell 变量中有一部分是环境变量,有一部分是局部变量,这些变量保证了 shell 的正常运行

定义变量

Shell 支持以下三种定义变量的方式

variable=value
variable='value'
variable="value"

variable 是变量名,value 是赋给变量的值。如果 value 不包含任何空白符(例如空格、Tab缩进等),那么可以不使用引号;如果 value 包含了空白符,那么就必须使用引号包围起来。

使用单引号和使用双引号的区别:

$ vim test.sh

#!/bin/bash

value="Let's see this'll difference"
variable1='variable1:${value}'
variable2="variable2:${value}"
echo $variable1
echo $variable2
~
~

运行结果:

$ bash test.sh

variable1:${value}
variable2:Let's see this'll difference 

以单引号' '包围变量的值时,单引号里面是什么就输出什么,即使内容中有变量和命令(命令需要反引起来)也会把它们原样输出。这种方式比较适合定义显示纯字符串的情况,即不希望解析变量、命令等的场景。

以双引号" "包围变量的值时,输出时会先解析里面的变量和命令,而不是把双引号中的变量名和命令原样输出。这种方式比较适合字符串中附带有变量和命令并且想将其解析后再输出的变量定义。

如果变量的内容是数字,那么可以不加引号;如果真的需要原样输出就加单引号;其他没有特别要求的字符串等最好都加上双引号,定义变量时加双引号是最常见的使用场景。

Shell 变量的命名规范

  • 变量名由数字、字母、下划线组成;
  • 必须以字母或者下划线开头;
  • 不能使用 Shell 里的关键字(通过 help命令可以查看保留关键字)。

当前定义的所有环境变量可以通过set命令获取。

$ set
...
HISTFILE=/root/.bash_history
HISTFILESIZE=2000
HISTSIZE=1000
HOME=/root
HOSTNAME=kubuntu
HOSTTYPE=x86_64
IFS=$' \t\n'
LANG=zh_CN.UTF-8
LANGUAGE=zh_CN:zh
LESSCLOSE='/usr/bin/lesspipe %s %s'
LESSOPEN='| /usr/bin/lesspipe %s'
LINES=57
LOGNAME=root
...

变量名外面的花括号{ }是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界,比如下面这种情况:

$ skill="Java"
$ echo "I am good at ${skill}Script"
I am good at JavaScript

由于在 Shell 脚本中,$作为变量的前缀符,所以当需要在文本输出中显示$时,应使用转义。

#$15 被当成了代入到字符串中的"变量"
$ echo "The cost of the item is $15"
The cost of the item is
#使用 \ 转义后正常打印 $ 字符
$ echo "The cost of the item is \$15"
The cost of the item is $15

使用变量

使用一个定义过的变量,只要在变量名前面加美元符号$即可

  • 变量赋值:var=value(注意=号两边不能有空格)
  • 变量使用:$var

$ vim test.sh

#!/bin/bash

value="Let's see if this'll work" 
~
~
$ echo $value
Let's see if this'll work

$ echo ${value}
Let's see if this'll work

shell替换

命令替换

命令替换是指 Shell 可以先执行命令,将输出结果暂时保存,在适当的地方输出。(将命令的结果赋值给变量)
用反引号 ``(位于 Esc 键的下方)包围起来:

$ vim test.sh

#!/bin/bash

DATE=`date`
USERS=`who | wc -l`
UP=`date ; uptime`
echo "Date is $DATE"
echo "Logged in user are $USERS"
echo "Uptime is $UP" '>test.sh
~
~

运行结果:

$ bash test.sh

Date is 20201122日 星期二 15:51:41 CST
Logged in user are 1
Uptime is 20201122日 星期二 15:51:41 CST
 15:51:41 up 56 min,  1 user,  load average: 0.00, 0.00, 0.00

变量替换

变量替换可以根据变量的状态(是否为空、是否定义等)来改变它的值

可以使用的变量替换形式:

形式说明
${var}变量本来的值
${var:-word}如果变量 var 为空或已被删除(unset),那么返回 word,但不改变 var 的值
${var:=word}如果变量 var 为空或已被删除(unset),那么返回 word,并将 var 的值设置为 word。
${var:?message}如果变量 var 为空或已被删除(unset),那么将消息( message )送到标准错误输出,可以用来检测变量 var 是否可以被正常赋值。若此替换出现在 Shell 脚本中,那么脚本将停止运行。
${var:+word}如果变量 var 被定义,那么返回 word,但不改变 var 的值。

例:

$ vim test.sh

#!/bin/bash

echo ${var:-"Variable is not set"}
echo "1 - Value of var is ${var}"

echo ${var:="Variable is not set"}
echo "2 - Value of var is ${var}"

unset var
echo ${var:+"This is default value"}
echo "3 - Value of var is $var"

var="Prefix"
echo ${var:+"This is default value"}
echo "4 - Value of var is $var"

echo ${var:?"Print this message"}
echo "5 - Value of var is ${var}"

运行结果:

$ bash test.sh

Variable is not set
1 - Value of var is 
Variable is not set
2 - Value of var is Variable is not set

3 - Value of var is 
This is default value
4 - Value of var is Prefix
Prefix
5 - Value of var is Prefix

shell函数

shell函数定义

Shell 函数定义的语法格式如下:

function name() {
    statements
    [return value]
}

对各个部分的说明:

  • function是 Shell 中的关键字,专门用来定义函数;
  • name是函数名;
  • statements是函数要执行的代码,也就是一组语句;
  • return value表示函数的返回值,其中 return 是 Shell 关键字,专门用在函数中返回一个值;这一部分可以写也可以不写。

由{ }包围的部分称为函数体,调用一个函数,实际上就是执行函数体中的代码。

函数定义的简化写法
如果你嫌麻烦,函数定义时也可以不写 function 关键字:

name() {
    statements
    [return value]
}

如果写了 function 关键字,也可以省略函数名后面的小括号:

function name {
    statements
    [return value]
}

Shell特殊变量

变量含义
$0当前脚本的文件名。
$n(n≥1)传递给脚本或函数的参数。n 是一个数字,表示第几个参数。例如,第一个参数是 $1,第二个参数是 $2。
$#传递给脚本或函数的参数个数。
$*传递给脚本或函数的所有参数。
$@传递给脚本或函数的所有参数。当被双引号" "包含时,$@ 与 $* 稍有不同。
$?上个命令的退出状态,或函数的返回值。
$$当前 Shell 进程 ID。对于 Shell 脚本,就是这些脚本所在的进程 ID。

$*$@ 的区别
$*$@ 都表示传递给函数或脚本的所有参数,不被双引号(" ")包含时,都以"$1" "$2" … "$n" 的形式输出所有参数。
但是当它们被双引号(" ")包含时,"$*" 会将所有的参数作为一个整体,以"$1 $2 … $n"的形式输出所有参数;"$@" 会将各个参数分开,以"$1" "$2" … "$n" 的形式输出所有参数。

退出状态
$? 可以获取上一个命令的退出状态。所谓退出状态,就是上一个命令执行后的返回结果。
退出状态是一个数字,一般情况下,大部分命令执行成功会返回0,失败返回1
不过,也有一些命令返回其他值,表示不同类型的错误。

shell函数调用

给脚本文件传递参数

$ vim test.sh

#!/bin/bash
 
echo "Process ID: $$"
echo "File Name: $0"
echo "First Parameter : $1"
echo "Second Parameter : $2"
echo "All parameters 1: $@"
echo "All parameters 2: $*"
echo "Total: $#" 
~
~

运行结果为:
$ bash test.sh

$ bash test.sh
Process ID: 2355
File Name: test.sh
First Parameter : 
Second Parameter : 
All parameters 1: 
All parameters 2: 
Total: 0
给函数传递参数

编辑test.sh文件

#!/bin/bash
#定义函数
function func(){
    echo "Language: $1"
    echo "URL: $2"
    echo "First Parameter : $1"
    echo "Second Parameter : $2"
    echo "All parameters 1: $@"
    echo "All parameters 2: $*"
    echo "Total: $#"
}
#调用函数
func Java http://c.biancheng.net/java/

运行 test.sh,并附带参数:

Language: Java
URL: http://c.biancheng.net/java/
First Parameter : Java
Second Parameter : http://c.biancheng.net/java/
All parameters 1: Java http://c.biancheng.net/java/
All parameters 2: Java http://c.biancheng.net/java/
Total: 2

Shell字符串

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

单引号

str='this is a string'

单引号字符串的限制:

  • 单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的
  • 单引号字串中不能出现单引号(对单引号使用转义符后也不行)

双引号

your_name='qj'
str="Hello, I know your are \"$your_name\"! \n"
  • 双引号里可以有变量
  • 双引号里可以出现转义字符

字符串操作

拼接字符串
your_name="qj"
greeting="hello, "$your_name" !"
greeting_1="hello, ${your_name} !"

echo $greeting $greeting_1
hello, qj ! hello, qj !
获取字符串长度
string="abcd"
echo ${#string} #输出4
提取子字符串
string="alibaba is a great company"
echo ${string:1:4} #输出 liba
查找子字符串
string="alibaba is a great company"
echo `expr index "$string" y` #输出26

Shell 数组

数组分类

  • 普通数组:只能使用整数作为数组索引(元素的下标)
  • 关联数组:可以使用字符串为数组索引(元素的下标)

普通数组定义

  • 一次赋予一个值
数组名[索引下标]=值

例:

array_name[0]=value1
array_name[1]=value2
array_name[2]=value3
  • 一次赋予多个值
数组名(值123...

例:

array_name=(value1 value2 value3)

或者

array_name=(
value1
value2
value3
)

或者是命令

array1=(`` `cat /etc/passwd` ``)
array2=(`` `ls  /root` ``)

读取数组

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

${数组名[元素下标]}

例如:

echo ${array_name[0]}			获取数组里第一个元素
echo ${array_name[*]}			获取数组里所有元素
echo ${#array_name[*]}			获取数组里所有元素个数
echo ${!array_name[@]}			获取数组元素的索引下标
echo ${array_name[@]:1:2}		访问指定元素;1代表从下标为1的元素开始获取;2代表取后面几个元素

获取数组的长度

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

#取得数组元素的个数

length=${#array_name[@]}

#或者

length=${#array_name[*]}

#取得数组单个元素的长度

lengthn=${#array_name[n]}

Shell echo 命令

echoShell 的一个内部指令,用于在屏幕上打印出指定的字符串。

命令格式:

echo test

可以使用 echo 实现复杂的输出格式控制。

显示转义字符

echo "\"It is a test\""

输出结果:

"It is a test"
  • 双引号可以省略

显示变量

name="ok"
echo "$name It is a test"

输出结果:

OK It is a test
  • 双引号可以省略

如果变量与其它字符相连的话,需要使用大括号({ }):

mouth=8
echo "${mouth}-1-1927"

输出结果:

8-1-1927

显示换行

echo "OK!\n"
echo "It is a test"

输出:

OK!
It is a test

显示不换行

echo"OK!\c"
echo "It is a test"

输出:

OK!It si a test

显示结果重定向至文件

echo "It is a test" > myfile

原样输出字符串

若需要原样输出字符串(不进行转义),请使用单引号。例如:

echo '$name\"'

输出:

echo $name\"

显示命令执行结果

echo `date`

输出结果:#显示当前日期

1949101日 星期六 14:00:59 CST

Shell printf 命令

:格式化输出语句

printf 命令模仿 C 程序库(library)里的 printf() 程序。
printfPOSIX 标准所定义,因此使用 printf 的脚本比使用 echo 移植性好。
printf 使用引用文本或空格分隔的参数,外面可以在 printf 中使用格式化字符串,还可以制定字符串的宽度、左右对齐方式等。

printf 命令的语法:

printf  format-string  [arguments...]

默认 printf 不像 echo 那样会自动换行,必须显式添加换行符(\n)

printf "Hello, Shell\n"
Hello, Shell 

参数说明:

  • format-string: 为格式控制字符串。
  • arguments: 为参数列表。

例:

# format-string 为双引号
$ printf "%d %s\n" 1 "abc"
1 abc
# 单引号与双引号效果一样 
$ printf '%d %s\n' 1 "abc" 
1 abc
# 没有引号也可以输出
$ printf %s abcdef
abcdef
# 格式只指定了一个参数,但多出的参数仍然会按照该格式输出,format-string 被重用
$ printf %s abc def
abcdef
$ printf "%s\n" abc def
abc
def
$ printf "%s %s %s\n" a b c d e f g h i j
a b c
d e f
g h i
j
# 如果没有 arguments,那么 %s 用 NULL 代替,%d 用0代替
$ printf "%s and %d \n" 
and 0
# 如果以 %d 的格式来显示字符串,那么会有警告,提示无效的数字,此时默认置为0
$ printf "The first program always prints'%s,%d\n'" Hello Shell
-bash: printf: Shell: invalid number
The first program always prints 'Hello,0'
$

$ vim test.sh

printf "%-10s %-8s %-4s\n" 姓名 性别 体重kg  
printf "%-10s %-8s %-4.2f\n" 张三 男 66.1234 
printf "%-10s %-8s %-4.2f\n" 李四 男 48.6543 
printf "%-10s %-8s %-4.2f\n" 二丫 女 47.9876 

执行脚本,输出结果如下:
$ bash test.sh

姓名     性别   体重kg
张三     男      66.12
李四     男      48.65
二丫     女      47.99

%s %c %d %f都是格式替代符

%-10s 指一个宽度为10个字符(-表示左对齐,没有则表示右对齐),任何字符都会被显示在10个字符宽的字符内,如果不足则自动以空格填充,超过也会将内容全部显示出来。

%-4.2f 指格式化为小数,其中.2指保留2位小数。

Shell test 命令

test  < 表达式 > 

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

Shell 基本运算符

四则运算符号

算数运算:默认情况下,shell就只支持简单的整数运算
运算内容:加( + )减(--)乘( * )除( / )求余数( % )

表达式举例
$(( ))echo $(( 1 + 1 ))
$[ ]echo$[ 10 - 5 ]
exprexpr 10 / 5
letn=1;let n+1 等价于 let n=n+1

类C风格的数值比较
在(( ))中,=表示赋值 ==表示判断

判断参数含义
== 相等((1==2))
!= 不相等((1!=2))
>= 大于等于((1>=2))
<= 小于等于((1<=2))

字符串比较
双引号引起来,看作一个整体;=和==在[字符串]比较中都表示判断

判断参数含义
= 相等((1=2))
== 相等((1==2))
!= 不相等((1!=2))
!== 相等((1!==2))
>= 大于等于((1>=2))
<= 小于等于((1<=2))

整数判断

判断整数

判断参数含义
-eq相等
-ne不等
-gt大于
-lt小于
-ge大于等于
-le小于等于

多重条件判断

运算符说明
-a 和 &&逻辑与,两个条件都为真  表达式才为真。[1 -eq 1 -a 1 -ne 0] [1 -eq 1 ]&& [1 -ne 0]
-o 和 ||逻辑或,只要有一个条件为真 表达式就为真。 [1 -eq 1 -o 1 -ne 1]
!非运算,表达式为 true 则返回 false,否则返回 true。
;用于分割命令或表达式。

&& 前面的表达式为真,才会执行后面的代码
|| 前面的表达式为假,才会执行后面的代码

字符串判断

运算符说明
-z判断是否为空字符串,字符串长度为0则成立。
-n判断是否为非空字符串,字符串长度为0则成立。
string1 = string2判断字符串是否相等。
string1 != string2判断字符串是否相不等。

文件判断

判断文件类型

判断参数含义
-e file检测文件是否存在(任何类型),如果是,则返回 true。
-f file检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。
-d file检测文件是否是目录,如果是,则返回 true。
-L file检测文件是否存在并且是一个符号链接。
-b file检测文件是否是块设备文件,如果是,则返回 true。
-S file判断某文件是否是一个套接子文件。
-c file检测文件是否是字符设备文件,如果是,则返回 true。
-p file检测文件是否是有名管道,如果是,则返回 true。
-s file检测文件是否为空(文件大小是否大于0),不为空返回 true。

判断文件权限

判断参数含义
-r file检测文件当前用户对其是否可读
-w file检测文件当前用户对其是否可写
-x file检测文件当前用户是否可执行
-u file检测文件是否设置了 SUID 位,高级权限冒险位。
-g file检测文件是否设置了 SGID 位,高级权限强制位。
-k file检测文件是否设置了t位,高级权限粘滞位。

Shell if else 语句

if 语句通过关系运算符判断表达式的真假来决定要执行的分支。Shell 有三种 if ... else 语句:

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

if … else 语句

if ... else 语句的语法:

    单分支
        if  判断条件;then
            条件为真的分支代码
        fi

如果 expression 返回 truethen 后边的语句将会被执行;如果返回 false,不会执行任何语句。

fi 来结尾闭合 if

注意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

运行结果:

a is not equal to b

if … else … fi 语句

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

    双分支
        if  判断条件; then
            条件为真的分支代码
        else
            条件为假的分支代码
        fi

如果 expression 返回 true,那么 then 后边的语句将会被执行;否则,执行 else 后边的语句。

例:

#!/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

执行结果:

a is not equal to b

if … elif … fi 语句

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

    多分支
        if  判断条件 1 ; then
            条件为真的分支代码
        elif  判断条件 2 ; then
            条件为真的分支代码
        elif  判断条件 3 ; then
            条件为真的分支代码
        else
            以上条件都为假的分支代码
        fi
        逐条件进行判断,第一次遇为“真”条件时,执行其分支,而后结束整个if 语句
        

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

运行结果:

a is less than b

Shell case esac 语句

case ... esac 与其他语言中的 switch ... case 语句类似,是一种多分枝选择结构。
case 语句匹配一个值或一个模式,如果匹配成功,执行相匹配的命令。

case 语句格式如下:

casein
模式1)
    command1
    command2
    command3
    ;;
模式2)
    command1
    command2
    command3
    ;;
*)
    command1
    command2
    command3
    ;;
esac

case 工作方式:取值后面必须为关键字 in,每一模式必须以右括号结束。取值可以为变量或常数。匹配发现取值符合某一模式后,其间所有命令开始执行直至 ;;;; 与其他语言中的 break 类似,意思是跳到整个 case 语句的最后。

取值将检测匹配的每一个模式。一旦模式匹配,则执行完匹配模式相应命令后不再继续其他模式。如果无一匹配模式,使用星号 * 捕获该值,再执行后面的命令。

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

echo 'Input a number between 1 to 4'
echo 'Your number is:\c'
read aNum
case $aNum 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

输入不同内容,会输出不同的结果。
例:

Input a number between 1 to 4
Your number is:3
You select 3

例2:

#!/bin/bash

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

运行结果:

$ bash test.sh
test.sh: usage: [ -f file ] | [ -d directory ]
$ bash test.sh -f index.htm
File name is index.htm
$ bash test.sh -d unix
Dir name is unix
$

Shell for 循环

与其他编程语言类似,Shell 支持 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

运行结果:

The value is: 1
The value is: 2
The value is: 3
The value is: 4
The value is: 5

顺序输出字符串中的字符:

for str in 'This is a string'
do
    echo $str
done

运行结果:

This is a string

显示主目录下以 .bash 开头的文件:

#!/bin/bash

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

运行结果:

/root/.bash_history
/root/.bash_logout
/root/.bash_profile
/root/.bashrc

Shell while 循环

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

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

命令执行完毕,控制返回循环顶部,从头开始直至测试条件为假。

while 循环
测试:如果 COUNTER 小于5,那么返回 trueCOUNTER0开始,每次循环处理时,COUNTER1。运行上述脚本,返回数字1到5,然后终止。

COUNTER=0
while [ $COUNTER -lt 5 ]
do
    COUNTER='expr $COUNTER+1'
    echo $COUNTER
done

运行脚本,输出:

1
2
3
4
5

while 循环可用于读取键盘信息。

例:
输入信息被设置为变量 FILM,结束循环。

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

运行脚本,输出结果:

type <CTRL-D> to terminate
enter your most liked film: Sound of Music
Yeah! great film the Sound of Music

Shell until 循环

until 循环执行一系列命令直至条件为 true 时停止。until 循环与 while 循环在处理方式上刚好相反。一般 while 循环优于 until 循环,但在某些时候,也只是极少数情况下,until 循环更加有用。

until 循环格式为:

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

command 一般为条件表达式,如果返回值为 false,则继续执行循环体内的语句,否则跳出循环。

例:使用 until 命令输出0 ~ 9的数字:

#!/bin/bash

a=0

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

运行结果:

0
1
2
3
4
5
6
7
8
9

shell跳出循环

Shell break 和 continue 命令

在循环过程中,当需要在未达到循环结束条件时强制跳出循环,Shell可以使用 breakcontinue 来跳出循环。

break 命令

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

例:
Shell脚本进入死循环直至用户输入数字大于5。强制跳出循环,要使用 break 命令返回到 shell 提示符下。

#!/bin/bash
while :
do
    echo -n "Input a number between 1 to 5: "
    read aNum
    case $aNum in
        1|2|3|4|5) echo "Your number is $aNum!"
        ;;
        *) echo "You do not select a number between 1 to 5, game is over!"
            break
        ;;
    esac
done

在嵌套循环中,break 命令后面可以跟一个整数,表示跳出第几层循环。 break n 表示跳出第 n 层循环。

嵌套循环,例:如果 var1 等于2,并且 var2 等于0,跳出循环。

#!/bin/bash

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 表示直接跳出外层循环。运行结果:

1 0
1 5

continue 命令

continue 命令与 break 命令类似,continue 不会跳出所有循环,仅跳出当前循环。

例:

#!/bin/bash
while :
do
    echo -n "Input a number between 1 to 5: "
    read aNum
    case $aNum in
        1|2|3|4|5) echo "Your number is $aNum!"
        ;;
        *) echo "You do not select a number between 1 to 5!"
            continue
            echo "Game is over!"
        ;;
    esac
done

当输入大于5的数字时,该循环不会结束,语句 echo "Game is over!"不会被执行。

continue 后面可以跟一个数字,表示跳出第几层循环。 continue n 表示跳出第 n 层循环。

例2:

#!/bin/bash

NUMS="1 2 3 4 5 6 7"

for NUM in $NUMS
do
   Q=`expr $NUM % 2`
   if [ $Q -eq 0 ]
   then
      echo "Number is an even number!!"
      continue
   fi
   echo "Found odd number"
done

运行结果:

Found odd number
Number is an even number!!
Found odd number
Number is an even number!!
Found odd number
Number is an even number!!
Found odd number

Shell 输入输出重定向

Linux 中,标准输入设备指的是键盘,标准输出设备指的是显示器。
Linux 中一切皆文件,包括标准输入设备(键盘)和标准输出设备(显示器)在内的所有计算机硬件都是文件。

输入与输出的文件描述符

文件描述符文件名类型硬件
0stdin标准输入文件键盘
1stdout标准输出文件显示器
2stderr标准错误输出文件显示器

输出重定向

命令的输出不仅可以是显示器,也可以输出为文件,称为输出重定向。

Bash 支持的输出重定向符号

类 型符 号作 用
标准输出重定向command >file以覆盖的方式,把 command 的正确输出结果输出到 file 文件中。
command >>file以追加的方式,把 command 的正确输出结果输出到 file 文件中。
标准错误输出重定向command 2>file以覆盖的方式,把 command 的错误信息输出到 file 文件中。
command 2>>file以追加的方式,把 command 的错误信息输出到 file 文件中。
正确输出和错误信息同时保存command >file 2>&1以覆盖的方式,把正确输出和错误信息同时保存到同一个文件(file)中。
command >>file 2>&1/td> 以追加的方式,把正确输出和错误信息同时保存到同一个文件(file)中。
command >file1 2>file2以覆盖的方式,把正确的输出结果输出到 file1 文件中,把错误信息输出到 file2 文件中。
command >>file1 2>>file2以追加的方式,把正确的输出结果输出到 file1 文件中,把错误信息输出到 file2 文件中。
command >file 2>file【不推荐】这两种写法会导致 file 被打开两次,引起资源竞争,所以 stdout 和 stderr 会互相覆盖。
command >>file 2>>file

在输出重定向中,>代表的是覆盖,>>代表的是追加。

输出重定向命令语法:

$ command > file

输出到显示器的内容会被重定向到文件。

例:命令输出在不会显示器上显示。

$ ls -a >testfile

查看 testfile 文件内容:

$ cat testfile
.bash_history
.bashrc
.cache
.local
.profile
.ssh
.vim
$

输出重定向会覆盖文件内容
例:

$ echo line 1 > testfile
$ cat users
line 1
$

可以使用 >> 追加到文件末尾,不覆盖文件内容。
例:

$ echo line 2 >> testfile
$ cat testfile
line 1
line 2
$

输入重定向

输入重定向就是改变输入的方向,不再使用键盘作为命令输入的来源,而是使用文件作为命令的输入。

Bash 支持的输出重定向符号

符号说明
command <file将 file 文件中的内容作为 command 的输入。
command <<END从标准输入(键盘)中读取数据,直到遇见分界符 END 才停止(分界符可以是任意的字符串,用户自己定义)。
command file2将 file1 作为 command 的输入,并将 command 的处理结果输出到 file2。

输入重定向完整写法为fd<file, fd 表示文件描述符,如不指定,则默认为 0,表示标准输入文件。

【示例】统计文档行数。

Linux wc 命令可以用来对文本进行统计,包括单词个数、行数、字节数,用法如下:

wc  [选项]  [文件名]

-c选项统计字节数,-w选项统计单词数,-l选项统计行数。

$ echo '12345
> abcde '>tests
$ cat tests
12345
abcde 
$
$ wc -l tests
2 tests
$

或输入重定向到 tests 文件:

$ wc -l < tests
2
$

wc -l tests会输出文件名;wc -l < tests不会输出文件名,仅从标准输入读取内容。

重定向命令列表

命令说明
command > file将输出重定向到 file。
command < file将输入重定向到 file。
command >> file将输出以追加的方式重定向到 file。
n > file将文件描述符为 n 的文件重定向到 file。
n >> file将文件描述符为 n 的文件以追加的方式重定向到 file。
n >& m将输出文件 m 和 n 合并。
n <& m将输入文件 m 和 n 合并。
<< tag将开始标记 tag 和结束标记 tag 之间的内容作为输入。

/dev/null 文件

将输出重定向到 /dev/null,只执行命令,不在屏幕上显示输出结果:

$ command > /dev/null

/dev/null 是一个特殊的文件,写入的内容都会被丢弃。将命令的输出重定向/dev/null ,内容会"禁止输出"。

屏蔽 stdout 和 stderr:

$ command > /dev/null 2>&1file

Shell 文件包含

Shell 可以包含外部脚本,将外部脚本的内容合并到当前脚本。

可以使用:

. filename

source filename

一般常用点号(.),点号(.)和文件名中间有空格。

创建两个脚本,subscript.sh作为被调用脚本 。
例:

url="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"

主文件 main.sh,内容:

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

执行脚本:

$ bash main.sh
http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4
$

sed文本编辑器

sed常见语法格式有二,一命令行模式,二脚本模式。

语法格式

sed	[options] '处理动作' 文件名

常用选项

选项说明
- e进行多项(多次)编辑
- n取消默认输出
- r使用扩展正则表达式
- i原地编辑(修改源文件)
- f指定sed脚本的文件名

常见处理动作

选项说明
'p'打印
'i'在指定指定行之前插入内容
'a'在指定指定行之后插入内容
'c'替换指定行所有内容
'd'删除指定行

对文件进行增、删、改、查操作

语法:sed 选项 ’定位+命令‘ 需要处理的文件
打印文件内容
[root@server ~] # sed '' a.txt   对文件什么都不做
[root@server ~] # sed - n 'p' a.txt 	打印每一行,取消默认输出
[root@server ~] # sed - n '1p' a.txt 	打印第一行
[root@server ~] # sed - n '2p' a.txt 		打印第二行
[root@server ~] # sed - n '1,5p' a.txt  打印1到5行
[root@server ~] # sed - n '$p' a.txt 		打印最后一行
增加文件内容
[root@server ~] # sed '$a99999' a.txt   文件最后一行下增加内容
[root@server ~] # sed 'a99999' a.txt   文件每行下增加内容
[root@server ~] # sed '5a99999' a.txt   文件第5行下增加内容
[root@server ~] # sed '$i99999' a.txt   文件最后一行上一行增加内容
[root@server ~] # sed 'i99999' a.txt   文件每行上一行增加内容
[root@server ~] # sed '6i99999' a.txt   文件第6行上一行增加内容
[root@server ~] # sed '/^uucp/i99999' a.txt  以ccup开头的上一行增加内容
修改文件内容
[root@server ~] # sed '5chello' a.txt   替换第5行内容
[root@server ~] # sed 'chello' a.txt   替换文件所有内容
[root@server ~] # sed '1,5chello' a.txt   替换文件1到5行内容,为hello
[root@server ~] # sed '/^uucp/c99999'  a.txt   替换以uucp开头的行
删除文件内容
[root@server ~] # sed '1d' a.txt   删除文件第1行
[root@server ~] # sed 1,5d' a.txt   删除文件1到5行
[root@server ~] # sed '$d' a.txt   删除文件最后一行

对文件进行搜索替换操作

语法: sed 选项 's/搜索内容/替换内容/动作' 需要处理的文件

s表示search搜索;/表示分隔符,可以自定义;p打印和g全局替换

[root@server ~] # sed -n 's/root/ROOT/p' a.txt   
[root@server ~] # sed -n '1,5s/^/#/p' a.txt   注释掉文件1到5行内容

sed结合正则使用

sed 选项 'sed命令 或者 正则表达式 或者 地址定位' 文件名

1.定址用于决定对哪些进行编辑。地址形式可以是数字、正则表达式、或者二者结合。
2.如没有指定地址,sed将处理输入文件的所有行。

正则说明备注
/key/查询包含关键字的行sed -n '/root/p' 1.txt
/key1/,/key2/匹配包含两个关键字之间的行sed -n '/^adm/,/^mysql/p' 1.txt
/key/,x从匹配关键字的行开始到文件第x行之间的行(包含关键字所在行)sed -n '/^ftp/,7p'
x,/key/从第x行开始到与关键字的匹配行之间的行
x,y!不包含x到y行
/key/!不包含关键字的行sed -n '/bash$/!p' 1.txt

脚本编写

#!/bin/sed -f
1,5d
s /root/hello/g
3666
5777
a
p

使用

sed -f   脚本 file
./sed.sh  file 

awk

  • 一种编程语言,主要用于在linux/unix下对文本和数据进项处理,是linux/unix下的一个工具。要处理的数据可以来自标准输入,一个或多个文件,或其它命令输出。
  • awk处理文本和数据的方式是:逐行扫描文件,默认从第一行到最后一行,寻找匹配的特定模式的行,并在这些行上进行你想要的操作。
  • awk用于处理文件和数据
  • 可以用来统计数据
  • 支持条件判断,支持for和while循环

awk命令行模式使用

语法结构:

awk 选项 '命令部分' 文件名

常用选项

  • -F 定义字段分割符号,默认分隔符为空格
  • -V 定义变量并赋值

命令说明

  • 正则表达式,地址定位
'/root/{awk语句}'			等同sed中:'/root/p'
'NR==1,NR==5 {awk语句}'		等同sed中:'/1,5/p'
'/^root/,/^ftp/{awk 语句你}'		等同sed中:'/^root/,/^ftp/p'
  • {awk语句1;awk语句2;…}
'{print $0;print $1}'		等同于sed:'p'
'NR==5{print $0}'			等同于sed:'5p'
  • BEGIN…END…
'BEGIN {awk语句} ; {处理中} ;END {awk语句}'
'BEGIN {awk语句} ; {处理中}'
'{处理中} ;END {awk语句}'

脚本使用

#!/bin/awk -f 
BEGIN {FS=':'}
NR==1,NR==3{print $1''\t''$NF}

执行

awk 选项 -f awk的脚本文件 要处理的文件
awk -f awk.sh filename
或
./awk.sh filename

awk相关变量

变量变量说明备注
$0当前处理行的所有记录
$1,$2,$3,…$n文件中每行以间隔符号分割的不同字段awk -F: '{print $1,$3}'
NF当前记录的字段数(列数)awk -F: '{print NF}'
$NF最后一列$(NF-1)表示倒数第二列
FNR/NR行号
FS定义间隔符‘BEGIN{FS=":"};{print $1,$3}’
OFS定义输出字段分割符,默认空格‘BEGIN{OFS="\t"};{print $1,$3}’
RS输入记录分割符,默认换行‘BEGIN{RS="\t"};{print $0}’
ORS输出记录分割符,默认换行‘BEGIN{ORS="\n\n"};{print $1,$3}’
FILENAME当前输入的文件名

grep命令:

命令语法:

	grep [OPTIONS] PATTERN [FILE...]
    grep [OPTIONS] [-e PATTERN | -f FILE] [FILE...]
- i:忽略大小写对文件内容进行匹配。
- v:反向匹配,根据用户输入的信息取反。
- o: 输入匹配的是什么,输出的就是什么,而不是显示匹配字串的一整行。
- E:grep的扩展,或者使用egrep命令。
- - color: 匹配到的字符显示颜色。

基本正则表达式:grep

grep -E 或者 egrep 的基本命令都是差不多一致的。

fgrep: 不支持使用正则表达式,如果输入*,直接匹配文本字面意思,就只匹配文本内带*的行。

基本正则表达式元字符:
字符匹配:
.: 匹配任意单个字符,包括标点符号,特殊字符。
[ ]: 匹配指定范围内的任意单个字符。
[^]: 反向匹配任意单个字符。

针对[ ]又有几个扩展的元字符

[0-9]:表示匹配所有数字
[a-z]:表示匹配所有小写字母
[A-Z]:表示匹配所有大写字母
次数匹配:

用于实现指定前面字符所能够出现的次数。

*:匹配任意长度,它前面的字符长度可以出现任意次(可以出现0次)。
?: 匹配前面字符可出现0次或1次,前面的字符可有可无
{m}:匹配m次,它前面的字符要出现m
{m,n}:匹配指定字符至少出现m次,至多n次,仍然匹配前面的字符。
{0,n}:匹配前面的字符至少出现0次,至多出现n次。
.*:匹配前面的任意长度任意字符
位置锚定:
^: 行首锚定,写在匹配字符的左侧。
$:行尾锚定,写在匹配字符的右侧。
^$: 空白行。
<: 词首,出现于单词左侧 \b。
>: 词尾,出现于单词右侧 \b。
分组:

分组是因为被匹配的内容在文件中出现多次,而不需要多次输入同样的信息去匹配。

():在括号内,可以出现任何正确的正则表达式匹配。
() \1:引用第一个左括号以及与之对应的右括号所包括的所有内容。
\2:引用第二个左括号以及与之对应的右括号所包括的所有内容,可以有\n次。
字符集合:

数字:[:dihit:]

小写字母:[:lower:]

大写字母:[:upper:]

标点符号:[:punct:]

空白字符:[:space:]

所有字母:[:alpha:]

所有数字:[:alnum:]

*)在引用字符合集的时候需要如[[:space:]]这样使用。

扩展正则表达式: egrep

*)元字符匹配与grep基本正则表达式匹配一致,包括位置锚定,字符合集。

次数匹配:
*:星号在扩展正则表达式和基本正则表达式匹配一至,并且在基本正则表达式中不用加\进行转义。
?:匹配前面的字符可以出现任意次,包括0次。
+:匹配前面的字符至少出现1次。
{m}: 匹配前面的字符至少出现m次。
{m,n}:匹配前面的字符至少出现m次,至多n次。
分组:

()\1: :引用第一个左括号以及与之对应的右括号所包括的所有内容。

或者:

| : 用于表示匹配a或A,例如:(cat|CAT).

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值