Shell(1):基础

学习自:shell学习教程(超详细完整)_shell教程

万字最全Linux Shell详细教程!

0、常用

规则

1)运算符和变量之间需要有空格
2)if、中括号、表达式间均需要空格
3)开头:#!/bin/bash,标注你的shell类型

4)路径写绝对路径,不然很容易混淆

5)与写在sh脚本中的bash语句相比,如果将指令直接写在命令行中,那么行与行之间用分号区别(例如for循环那一句都要用分号);

6)如果用到了 管道 |,那么需要把前半句话括号包括(相当于管道的数据来源是个整体),不然可能导致预期之外的输出;

算数运算:

整数

  • val = $[$a 运算符 $b](个人觉得最好用)
  • val =` expr  $a 运算符 $b`
  • val = $(expr $a 运算符 $b)
val=$[2 + 2]
val=$(expr 2 + 2)
val=`expr 2 + 2`

自增/减:let

复制代码
n=1

let n++
echo $n
2

let n--
echo $n
1
复制代码

小数:bc

echo " 2 * 0.5 " | bc
1.0

#设置精度(小数点后的位数)
echo " scale=2; 1.74 / 3 "| bc
.58
echo " scale=3; 1.74 / 3 "| bc
.580

 

在语句中传入表达式运算

  • ` 表达式 `
  • $(表达式)

例如把某个文件作为for循环的循环体:

for i in `cat test.txt`
for i in $(cat test.txt)
    do
        xxx
    done

数组

复制代码
A=(A B C D)

#输出全部
echo ${A[@]}
echo ${A[*]}

#输出长度
echo ${#A[@]}
echo ${#A[*]}

#输出单个元素,下标从0开始
echo ${A[1]} #B

#for循环
for i in ${A[@]}
do
    echo $i
done

复制代码

 

for循环

复制代码
#循环输出当前目录下所有文件的行数、单词数、字符数
for i in $(ls)
    do
        wc $i
    done

  5795  72282 447054 redis_6379.log
  38  485 3849 sentinel.log

#配合{}实现自定义循环内容
#这里构造了两个前半截相同,最后一节为11和12的IP地址进行循环输出
for i in 192.168.1.1{1,2}
do
  echo $i
done

192.168.1.11
192.168.1.12
复制代码

while死循环(注意while后的冒号)

while :
do
    循环体
    #每60s执行一次
    sleep 60
done

if

复制代码
#判断某个指令的执行结果是否为空
if [ `grep abc test.txt`]
then
    echo "OK"
fi
#如果grep结果中包含空格,则可以写为
if [ `grep abc test.txt | awk '{print $1}'` ]
then
    echo "OK"
fi
复制代码

 

  • wc:统计
wc [选项] 文件名
选项:
    -l:只统计行数
    -w:只统计单词数
    -m:只统计字符数
  -L:最长行的长度
  • 将一个文件内容清空
true > 1.txt

 变量运算

val =$[2 + 2]
val =$[$val1+$val2]

 重启nginx

if [ 条件 ]
then
    /usr/local/nginx/sbin/nginx -s reload 2> /dev/null
fi

将某个需要执行的指令用 2> /dev/null即可,见/dev/null

2)一些特殊字符

Shell 中的特殊字符 | 菜鸟教程

符号

说明

用法

; 多条命令顺序执行,命令间不互相干扰 命令1 ; 命令2
|

①正则表达式中表示

②用于命令间,表示用前边命令的输出作为后边命令的输入

①ab(c|d)

②命令1 | 命令2

 &

①把命令放在后台执行

②标准输出和标准错误输出,具体见输入输出重定向一节

①java -jar xxx.jar &

 &&  前边指令执行成功才执行后边指令

1)如果用在if条件中,可以当做或、与使用

if [  $a -eq 00 ] || [ $a -eq 12 ]

2)单独使用时,可以实现if的逻辑(||代表if [ 公式 ] false,&&代表if [公式] true)

复制代码
#输入一个数字
#1)如果比当前最小值小,就把当前最小值更改为该数字;
#2)如果比当前最大值大,就把当前最大值更改为该数字。
MIN=0
MAX=100
read -p "输入一个数字:" INT

[$MIN -lt $INT] || MIN=$INT
[$MAX -gt $INT] || MAX=$INT
复制代码

 

|| 

 前边指令执行失败才执行后边指令

①注释

$#:命令行参数个数

${#变量}:变量(字符串)长度

${#数组[@]}:数组长度

 
$

变量:

①$a:变量的值

命令行:

$0:当前脚本程序名称

$1-$n:第n个命令行参数

$*:所有命令行参数,整体输出

$@:所有命令行参数,分别输出

$#:命令行所有参数的个数

进程

$?:最后一次执行状态

$$:当前进程的进程号pid

$!:后台运行的最后一个进程的进程号

awk

$0:某一行的全部字段数据

$1-$n:某一行的第n个字段数据

 
~

字符串是否包含某个子串

awk '$2 ~ /Sc/'

第二字段中是否包含"Sc"字符

{}

正则:表示范围

循环:指定循环范围

匿名函数

命令:{}中的命令,是在当前shell执行

192.168.1.1{1,2}代表192.168.1.11和192.168.1.12 
:=

${var:="defaultvalue"} 当变量var为空(未定义时),为它赋一个默认值

event=${event:="on"}

如果event存在,就用存在的值

如果event不存在,就用"on"当它的值

 

#* 匹配最左侧的符号  

 

裁剪字符

学习自:shell中 #*, ##*, %*, %%*的含义及用法_shell #*-CSDN博客

用法:变量名|裁剪指令|匹配符号

这里的分隔符|,只是为了方便区分,实际上三者之间并无任何分隔符。

1)#*、##*

说明

#*:最小匹配,并移除匹配位置左边的所有字符

##*:最大匹配,并移除匹配位置左边的所有字符

例子

1
2
3
4
5
6
7
8
VAR="abc/opt/software/hadoop/a.txt"
VAR=${VAR#*/}#最短匹配,匹配第一个斜杠/,即abc/opt,此时abc/都会被删除
echo $VAR<br><br>opt/……/a.txt
 
VAR="abc/opt/software/hadoop/a.txt"
VAR=${VAR##*/} #最长匹配,匹配最后一个斜杠/,即……doop/a.txt,此时……doop/都会被删除
echo $VAR<br>
a.txt

2)%*、%%*

说明

%*:最小匹配,从右起匹配第一个字符并移除该字符右侧所有字符

%%*:最大匹配,从右往左匹配最后一个字符并移除该字符右侧所有字符

例子

1
2
3
4
5
6
7
8
9
10
11
12
VAR="/opt/software/hadoop/a.txt"
VAR=${VAR%*/} #从右往左找到第一个斜杠,并删除它及它左边所有字符
echo $VAR
 
/opt/……/hadoop
 
 
VAR="opt/software/hadoop/a.txt"
VAR=${VAR%%*/} #从右往左找到最后一个斜杠,并删除它及它左边所有字符
echo $VAR
 
opt

  

$

变量

作用

$n

n为数字

$0:当前脚本程序名称

$1-9:第1-9个参数

${10}:10以上的参数需要用大括号包含

$* 命令行中的所有参数,以一个整体输出
$@ 命令行中的所有参数,不同参数用@加以区分
$# 命令行中所有参数的个数

预定义变量

作用

$?

最后一次执行的命令的返回状态:

0:正确执行

非0:不正确

$$ 当前进程的进程号PID
$! 后台运行的最后一个进程的进程号

 

1、什么是Shell?

Shell是用C编写的程序,它是用户使用Linux的桥梁,Shell既是一种命令语言,也是一种程序设计语言

Shell为提供了一个界面,用户通过这个界面访问操作系统内核的服务。

2、Shell分类

在Linux中输入指令:

cat /etc/shells #查看全部SHELL
echo $SHELL #默认SHELL

Linux中默认的Shell是/bash/bash,流行的有ash、bash、ksh、csh、zsh

 

3、Shell编写规范

注意Shell脚本与Shell指令的区别:

Shell指令:其实就是Linux指令。

Shell脚本:按照一定语法规则编写的Shell文件。

shell脚本文件执行

首先编写一个Shell脚本

[... ~]$vim test.sh
#!/bin/bash
echo "hello world"

两种执行方式:

①给文件增加执行权限

[root@localhost ~]$chmod u+x test.sh
[root@localhost ~]$ ./test.sh #绝对路径或者相对路径执行

②通过bash调用脚本

bash test.sh
或者
bash "test.sh"

1)文件头

#!/bin/bash [告知系统该脚本所使用的的shell解释器]
shell指令

2)命名规范

以.sh结尾.sh是Linux下bash shell的默认后缀

3)Bash常用快捷键(在Linux命令行模式下使用)

快捷键

命令

CTRL+A

将光标移动至命令行开头

CTRL+E 将光标移动至命令行结尾
CTRL+C 强制终止当前命令
CTRL+L 清屏,相当于clear
CTRL+U 删除/剪切光标之前的所有字符
CTRL+K 删除/剪切光标之后的所有字符
CTRL+Y 粘贴
CTRL+R

在历史命令中搜索,按下CTRL+R就会出现搜索页面。

在其中输入搜索内容就可以从历史命令中搜索。

CTRL+D 退出终端
CTRL+Z 暂停,并放入后台
CTRL+S

暂停输出

CTRL+Q 恢复输出

4)输入输出重定向

①标准输入输出

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

②输入重定向

  • 输入重定向:不使用标准输入端口(也就是①中所说的键盘)输入文件,而是使用指定的文件作为标准输入设备
  • 修改:使用"<"符来修改标准输入设备
类型符号(语法)功能
标准输入 命令<文件1 命令把文件1的内容作为标准输入设备
标识符限定输入 命令<<标识符 命令把标准输入中读入内容,直到遇到“标识符”分解符为止
输入输出重定向(同时使用) 命令< 文件1 >文件2 命令把文件1的内容作为标准输入,把文件2作为标准输出。

③输出重定向

  • 输出重定向:把输出信息写入到文件中,而非控制台(显示屏)如果没有重定向,那么默认的输出设备是控制台

类型

指令

效果

标准输出重定向 命令 > 文件 覆盖,把命令的正确输出内容输出到指定的文件
命令 >> 文件 追加,把命令的正确输出内容输出到指定的文件
标准错误输出重定向 错误命令2 > 文件 覆盖,把命令的错误输出内容输出到指定的文件
错误命令2 >> 文件 追加,把命令的错误输出内容输出到指定的文件
正确输出和错误输出同时保存 命令 > 文件 2>&1 覆盖,把正确输出和错误输出都保存到同一个文件
命令 >> 文件 2>&1 追加,把正确输出和错误输出都保存到同一个文件
命令 &> 文件 覆盖,把正确输出和错误输出都保存到同一个文件
命令 &>> 文件 追加,把正确输出和错误输出都保存到同一个文件
命令 >> 文件1 2>>文件2 追加,正确输出追加到文件1中,错误输出追加到文件2
  • /dev/null:如果要执行某个命令,但不要在屏幕上显示结果,可以将输出重定向到/dev/null

用法

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 之间的内容作为输入。

例子

echo "hello world" > test7.sh

5)指令执行流程

标志

用法

效果

; 命令1;命令2 顺序执行
&& 命令1&&命令2 只有当命令1正确执行,才会执行命令2
|| 命令1||命令2 只有当命令1不正确执行,才会执行命令2

 

6)全局规则

set +/-[选项]

其中 -代表设定某选项+代表取消该选项,单set可以查看所有变量

选项有:

  • u:调用未声明变量是否报错(默认无反应);
  • x:命令执行前,先把命令输出一次(即输出命令和命令结果)
[root@localhost ~]$ set -u #未定义变量报错
[root@localhost ~]$ echo $file
-bash: file: unbound variable

[root@localhost ~]$ set -x #指令重现
[root@localhost ~]$ ls
+ls --color=auto
test.sh

四、shell变量

1)何为shell变量

在一个脚本周期内,其值可以发生改变的量就是变量。

shell变量在其定义之前就使用,比如在100行用赋值号创建了某个变量,但是你可以在第10行就用echo把这个变量输出出来(这一点和Java、C、Python语言都大不相同)。

另外,如果使用了一个错误的或者未定义的变量,那么实际sh执行过程中不会报错,但是由于该变量不存在,所以会把该$变量自动替换为空(空字符串)

2)变量命名

  • 赋值号左右两侧不能有空格变量的值如果有空格,那么该值要用单引号双引号包括。例如test=“hello world!”
  • 环境变量名大写;
  • 如果要进行占位赋值,可以用"$变量名"、"${变量名}"、${变量名}、$变量名
     带双引号输出的作用:安全,如果输出的内容中包含空格,使用双引号会正确识别空格并完整输出。
    test=123
    test="$test"456
    test=${test}456
    
    #以上两种方式分开执行的话
    #结果都是123456 

示例

复制代码
name=sc

#①单引号
echo '$name'
$name

#②双引号
echo "${name}"
sc

echo "$name"
sc

#③其他一些输出
echo ${name}
echo $name
sc
复制代码

在输出单个变量时,加不加双引号效果一样,这点跟Python一样灵活,相当于$就是通知了一个占位符。

echo

  • 用法:类似python的print,可以把内容输出到屏幕上

以下代码的输出结果相同

echo "${name}"
echo "$name"
echo ${name}
echo $name
  • 关于单双引号

    • 双引号①识别变量,实现②转义功能(类似于转义斜杠“\”
    • 单引号不能识别变量,也不能转义,只会原样输出
    • 如果反引号要执行某段指令,那么其中的变量$i必须用双引号包括,才能够正确解析出来,例如:
      echo `grep "$i" /tmp/test.txt`

      当i=/data/123.txt时,上文会执行指令grep /data/123.txt /tmp/test.txt并将结果输出,

      如果用单引号:

      echo `grep '$i' /tmp/test.txt`

      则是执行指令grep '/data/123.txt' /tmp/test.txt,会把这个单引号也引入grep中进行执行。

      所以反引号中的变量,要用双引号包括

       

如果只是用于显示无疑义的字符串,那么在用echo输出时,加不加引号的效果是一样的:

echo "hello world"
echo hello world
#效果相同
  • 转义

echo "\"hello world\""

"hello world"#两个""被转义并被正常输出
  • 换行

echo -e "hello\n" #-e开启转义
hello

#多出来一个空行
  • 不换行

echo -e "abc! \c"  #\c 不换行
abc! #不换行 接下来的命令将继续在"abc!"这一行输入

特殊符号

符号

效果

单引号 '' 单引号内的所有字符都视为普通字符
双引号 ""

双引号内的部分字符视为普通字符,除了$、反引号`、\

$:调用变量的值(类似C语言中的&)

`:引用命令

\:转义

反引号 ``

反引号内代表系统命令(可以视为执行引号内的指令返回的内容),而不是普通变量。比如`date`就代表系统时间

但不常用,通常为了避免眼花,常用$()代替:

echo `date`
echo $(date)
$()

1、效果同反引号,用来引用系统命令(常用) 

2、如果要在shell脚本中使用Linux指令,需要用$()包括

() 用于一串命令执行,()中的命令会在子shell中运行 
{}

1、用于一串命令执行,{}中的命令会在当前shell中执行;

2、利用${变量}或$变量实现变量占位输出

[ ]

1、用于变量测试 

2、返回true或false的运算

# 注释 
$  获取变量值,用法${变量}或$变量
\ 转义字符,跟在其后的字符将变为普通字符 

 

3)变量的类别

  • 用户自定义变量:我们自定义的变量,最常见;
  • 环境变量:和系统环境相关的变量;分为用户自定义环境变量系统自带环境变量
  • 位置参数变量:用于向脚本中传递参数或数据变量名作用都是固定的;
  • 预定义变量:Bash中定义好的变量变量名作用都是固定的;

①用户自定义变量

  • 声明:变量名=变量值

  • 调用:echo $name或echo ${name}

  • 变量删除:unset 变量名
  • 规则设置:set +/-[选项]

其中 -代表设定某选项+代表取消该选项,单set可以查看所有变量

选项有:

    • u:调用未声明变量是否报错(默认无反应);
    • x:命令执行前,先把命令输出一次
[root@localhost ~]$ set -u #未定义变量报错
[root@localhost ~]$ echo $file
-bash: file: unbound variable

[root@localhost ~]$ set -x #指令重现
[root@localhost ~]$ ls
+ls --color=auto
test.sh

只读变量

  • 声明:

    变量名=变量值
    readonly 变量 #不写变量名会默认上一个
  • 作用:接下来这个变量就无法修改了(再赋值)

②环境变量

  • 声明: export 变量名=变量值
  • 查询:env [变量名](set可以查看所有变量,env只能查看环境变量,不加变量名可以查看所有环境变量)
  • 删除:unset 变量名
[root@localhost ~]$ env
HOSTNAME=localhost.localdomain      #主机名
SHELL=/bin/bash                     #当前的shell
TERM=linux                          #终端环境
HISTSIZE=1000                       #历史命令条数
SSH_CLIENT=192.168.4.1594824 22     #当前操作环境是用ssh连接的,这里记录客户端ip
SSH_TTY=/dev/pts/1                  #ssh连接的终端时pts/1
USER=root                           #当前登录的用户
  •  用法:$变量名

和常规变量的用法一样,只是无需声明,可以直接在shell脚本中用$环境变量输出。

echo $NGINX_VERSION
1.16.1

 

③位置参数/命令行参数

变量名、用法、作用都是固定的

变量

作用

$n

n为数字

$0:当前脚本程序名称

$1-9:第1-9个参数

${10}:10以上的参数需要用大括号包含

$* 命令行中的所有参数,以一个整体输出
$@ 命令行中的所有参数,不同参数用@加以区分
$# 命令行中所有参数的个数

命令行参数如何设置和传入呢?

在用bash xxx.sh调用bash文件时,通过罗列传入:

bash Test.sh p1 p2

这样,$0就代表Test.sh,$1代表第一个参数,即p1,$2代表第二个参数,即p2。

$*与$@的区别

二者都是输出命令行所有参数,但是$*将这些参数作为一个整体输出,而$@分别输出这些参数。

复制代码
[root@localhost sh]$ vi parameter2.sh
#!/bin/bash
for i in"$*"
#$*中的所有参数是一个整体,所以这个for循环只会循环一次
    do
        echo "The parameters is: $i"
    done
x=1

for y in"$@"
#$@中的每个参数都是独立的,所以“$@”中有几个参数,就会循环几次
    do
        echo "The parameter$x is: $y"
        #输出变量y的值
        x=$(( $x +1 ))
        #然变量x每次循环都加1,为了输出时看的更清楚
    done
复制代码

④预定义变量

名称固定,作用固定

预定义变量

作用

$?

最后一次执行的命令的返回状态:

0:正确执行

非0:不正确

$$ 当前进程的进程号PID
$! 后台运行的最后一个进程的进程号

用法

$?

复制代码
[root@localhost sh]$ ls
count.sh hello.sh parameter2.sh parameter.sh
#ls命令正确执行
[root@localhost sh]$ echo $?
0 #预定义变量“$?”的值是0,证明上一个命令执行正确
[root@localhost sh]$ ls install.log
ls:无法访问install.log:没有那个文件或目录
#当前目录中没有install.log文件,所以ls命令报错了
[root@localhost sh]$ echo $?
2
#变量“$?”返回一个非0的值,证明上一个命令没有正确执行
#至于错误的返回值到底是多少,是在编写ls命令时定义好的,如果碰到文件不存在就返回数值2
复制代码

$$与$!

复制代码
[root@localhost sh]$ vi variable.sh
#!/bin/bash
echo "The current process is $$"
#输出当前进程的PID.
#这个PID就是variable.sh这个脚本执行时,生成的进程的PID
find /root -name hello.sh &
#使用find命令在root目录下查找hello.sh文件
#符号&的意思是把命令放入后台执行,工作管理我们在系统管理章节会详细介绍
echo "The last one Daemon process is $!"
#输出这个后台执行命令的进程的PID,也就是输出find命令的PID号
复制代码

5)从键盘输入:read

用法:read [选项名 选项值] [变量名]

  • 选项:

    • -a:后跟一个变量,该变量会被视为一个数组,然后为其赋值,默认以空格为分隔符;
    • -p:提示信息:在等待输入时,给出提示信息;
    • -t:秒数:最多等待指定时间;
    • -n:数字:输入指定字符数量后执行;
    • -s:隐藏输入数据(就像Linux登录时输入密码那样)
    • -d:开启标志符,当输入该字符后结束。
    • -e:开启命令补全功能
  • 变量名

    • 变量名自定义,如果没有则默认为REPLY
    • 如果只有单个变量,则把整行输入赋予该变量;
    • 如果有一个以上的变量,则输入行会进行分割,一个个赋予,最后一个变量会获得剩余全部字符

例子:

复制代码
vi read.sh
#!/bin/bash

read -t 30 -p "Please input your name: " name
#①提示“Please ……”
#②等待30 秒
#③存入变量name
echo "Name is $name"

read -s -t 30 -p "Please enter your age: " age
#与上一个相比,隐藏输入
echo "Age is $age"

read -n 1 -t 30 -p "Please select your gender[M/F]:" gender
#与上一个相比,加入了选项 -n 1,代表只接收一个输入字符就会执行(都不用输入回车)
echo "Sex is $gender"
复制代码

 

五、运算符

1)算术运算符

bash不支持使用+-*/直接进行运算,相关操作要依赖指令awk、expr实现,其中expr最常用

例子

用一个算术表达式给变量赋值:

val=$(expr 2 + 2)
或 
val=`expr 2 + 2`

val=$[2 + 2]

需要注意的是数字和运算符之间需要空格,2 + 2而非2+2

运算符

乘的符号为*,有的版本的shell为\*

运算

运算符

说明

+

用法:$(expr $a 运算符 $b)`expr $a 运算符 $b`

注意运算符数字间的空格

val=$(expr 2 + 2)
或 
val=`expr 2 + 2`

val =$[2 + 2]#个人认为最方便

 

-
*
/
取余 %
赋值 =
相等 ==

1、条件判断要放在[ ]之内

if [ $a == $b ]
then
    echo 'a等于b'
else
    echo 'a不等于b'
fi

2、返回true或false

不等 !=

2)关系运算符

关系运算符只支持数字纯数字字符串,不支持其他字符串

用法:

[$a 运算符 $b]

运算符之前还有-

运算符:

运算

运算符

说明

== -eq

真:true

假:false

!= -ne
> -gt
< -lt
-ge
-le

可以通过$?查询最后一次运算结果:

  • 0:true
  • 非0:false

例子

判断当前输入的用户是否存在。如果存在则提示“用户存在”,否则提示“用户不存在”。

注:如果要在shell脚本使用linux命令,可以使用$()或者反引号包裹命令;

复制代码
[root@localhost ~]$ vim demo.sh 
#!/bin/bash
#接受用户的输入
read -p '请输入需要查询的用户名:' username

#获取指定用户名在passwd文件中出现的次数
count=$(cat /etc/passwd | grep $username | wc -l)

#判断出现的次数,如果次数=0则用户不存在,反之存在
if [  $count == 0 ]
then 
        echo '用户不存在'
    else 
        echo '用户存在'
fi
复制代码

 3)逻辑运算符(或与非)

运算

运算符

说明

用法

注意[ ]与左右表达式之间的空格

-o

o代表or

[表达式1 -o 表达式2]

1 2其中有一个为true才返回true

[ $a -lt 20 -o $b -gt 100 ]

-a

a代表and

[表达式1 -a 表达式2]

1 2都为true才返回true

[ $a -lt 20 -a $b -gt 100 ]

!  [! 逻辑运算] [ ! false ]返回true 

4)字符串运算符

运算符

说明

例子

注意①[ ]左右表达式之间的空格

②表达式 与 运算符之间的空格

= 两个字符串是否相等,是则返回true [ $a = $b ]
!= 两个字符串是否相等,否则返回true [ $a != $b ]
-z 字符串长是否为0,是则返回true [ -z $a ]
-n 字符串长是否为0,否则返回true [ -n $a ] 

str

(无运算符

字符串是否为空,不为空则返回true [ $a ]

5)文件测试运算符

用于检测Unix/Linux文件的各种属性

运算符

用法都是:test 运算符 file

说明

都是检测是否是某种文件

-b 块设备文件
-c 字符设备文件
-d 目录
-f 普通文件(既非目录,亦非设备)
-g SGID
-k 粘着位
-p 有名管道
-u SUID
-r 可读
-w 可写
-x 可执行
-s 空(文件大小是否大于0)
-e 存在

用法

if test -e ./test1.sh
then
    echo '文件已存在!'
else
    echo '文件不存在!'
fi

 

六、流程语句

下文所有的条件表达式中的运算符,在第五节运算符中有说明

1)if条件

用法

复制代码

if
[ 条件 ] then 表达式1 else 表达式2 fiif [ 条件1 ] then 表达式1 elif[ 条件2 ] then 表达式2 ... else 所有条件都不成立时,执行该表达式 fi
复制代码

注意:

1、if和中括号中括号和内部条件之间均有不能省略的空格!!

if [ 表达式 ];then 表达式省略空格会导致

  • 错误的替换/bad subsitution

  • 未预期的符号 `then' 附近有语法错误

2、then之后的表达式不能省略,即不能写一个空if语句,不然会报错

  • 未预期的符号 `fi' 附近有语法错误

例子

①统计根分区使用率

复制代码
[root@localhost ~]$ vi sh/if1.sh
#!/bin/bash

#统计根分区使用率
rate=$(df -h | grep "/dev/sda2" | awk '{print $5}’| cut -d "%"-f1)
#把根分区使用率作为变量值赋予变量rate
if [ $rate -ge 80 ]
#判断rate的值如果大于等于80,则执行then程序
    then
        echo "Warning!/dev/sda3 is fu11!!"
    #打印警告信息。在实际工作中,也可以向管理员发送邮件。
fi
复制代码

②创建目录

复制代码
[root@localhost ~]$ vi sh/add_dir.sh
#!/bin/bash
#创建目录,判断是否存在,存在就结束,反之创建
echo "当前脚本名称为$0"
DIR="/media/cdrom"
if [ ! -e $DIR ]
then
    mkdir -p $DIR
fi
echo "$DIR 创建成功"
复制代码

③备份mysql数据库

复制代码
[root@localhost ~]$ vi sh/bakmysql.sh
#!/bin/bash
#备份mysql数据库。

ntpdate asia.pool.ntp.org &>/dev/null
#同步系统时间
date=$(date +%y%m%d)
#把当前系统时间按照“年月日”格式赋子变量date
size=$(du -sh/var/lib/mysql)
#统计mysql数据库的大小,并把大小赋予size变量

if [ -d /tmp/dbbak ]
#判断备份目录是否存在,是否为目录
    then
    #如果判断为真,执行以下脚本
    echo "Date : $date!" > /tmp/dbbak/dbinfo.txt
    #把当前日期写入临时文件
    echo "Data size : $size" >> /tmp/dbbak/dbinfo.txt
    #把数据库大小写入临时文件
    cd/tmp/dbbak
    
    #进入备份目录
    tar -zcf mysql-lib-$date.tar.gz /var/lib/mysql dbinfo.txt &> /dev/null
    #打包压缩数据库与临时文件,把所有输出丢入垃圾箱(不想看到任何输出)
    rm -rf /tmp/dbbak/dbinfo.txt
    #删除临时文件
else
    mkdir /tmp/dbbak
    #如果判断为假,则建立备份目录
    echo "Date : $date!" > /tmp/dbbak/dbinfo.txt
    echo "Data size : $size" >> /tmp/dbbak/dbinfo.txt
    #把日期和数据库大小保存如临时文件
    cd /tmp/dbbak
    tar -zcf mysql-lib-$date.tar. gz dbinfo.txt /var/lib/mysql &> /dev/null
    #压缩备份数据库与临时文件
    rm -rf/tmp/dbbak/dbinfo.txt
    #删除临时文件
fi
复制代码

④判断apache是否启动,如果没有启动则自动启动

复制代码
[root@localhost ~]$ vi sh/autostart.sh
#!/bin/bash
#判断apache是否启动,如果没有启动则自动启动

port=$(nmap -sT 192.168.4.210 | grep tcp | grep http | awk '{print $2}’)
#使用nmap命令扫描服务器,并截取 apache服务的状态,赋予变量port
#只要状态是open,就证明正常启动
if [ "$port" == "open"]
#如果变量port的值是“open”
    then
    echo "$(date) httpd is ok!” >> /tmp/autostart-acc.log
    #则证明apache 正常启动,在正常日志中写入一句话即可
else
    /etc/rc.d/init.d/httpd start &>/dev/null
    #否则证明apache没有启动,自动启动apache
    echo "$(date) restart httpd !!" >> /tmp/autostart-err.log
    #并在错误日志中记录自动启动apche 的时间
fi
复制代码

⑤判断用户输入的是什么文件

复制代码
[root@localhost ~]$ vi sh/if-elif.sh
#!/bin/bash
#判断用户输入的是什么文件

read -p "Please input a filename: " file
#接收键盘的输入,并赋予变量file
if [ -z "$file” ]
#判断file变量是否为空
    then
        echo "Error, please input a filename"
        #如果为空,执行程序1,也就是输出报错信息
        exit 1
        #退出程序,并返回值为Ⅰ(把返回值赋予变量$P)
elif [ ! -e "$file” ]
        #判断file的值是否存在
        then
        echo "Your input is not a file!"
        #如1果不存在,则执行程序2
        exit 2
        #退出程序,把并定义返回值为2
elif [ -f "$file” ]
        #判断file的值是否为普通文件
        then
        echo "$file is a regulare file!”
        #如果是普通文件,则执行程序3
elif [ -d "$file” ]
        #到断file的值是否为目录文件
        then
        echo "$file is a directory!"
        #如果是目录文件,网执行程序4
else
    echo "$file is an other file!”
    #如果以上判断都不是,则执行程序5
fi
复制代码

2)case条件(类似C中的switch case)

用法

复制代码
case $变量名 in
    值1|值2|值3)
    程序1;;
    值n)
    程序2;;
    #其他分支...
    *)
    若果以上分支都没命中,则执行该程序(相当于default);;
esac
复制代码

注意事项:

  • 分支程序末尾的两个分号;;
  • in后边判断时,只有右半括号),没有左半括号;
  • 以case开始,以esac结束
  • 多个并列的值用分隔。

3)for循环

用法

①直接把每次循环时的值都给出了(类似python中的for i in ...)

复制代码
for 变量 in 值1 值2 值3 ...
    do
        程序
    done

for 变量 in { 起始值1..终值n }
  do
    程序
  done
复制代码

注意:

  • 值与值间用空格分隔;
  • 以上多个值可以用一个List或者文件具有多个值的对象代替;

例子

  • 打印一些值
    for time in morning noon afternoon evening
        do
            echo "This time is $time!"
        done
  • 打印从1到100
    for i in {1..100}
        do
            echo $i
        done
  • 批量解压缩脚本
    for i in $(cat ls.log) `
    #或者这样写for i in `cat ls.log`
    #读取ls.log文件的内容,文件中有多少个值,就会循环多少次,每次循环把文件名赋予变量i
        do
            tar -zxf $i &>/dev/nulldone
    rm -rf /lamp/ls.log

②自增式循环(类似C中for(i=1;i<=100;i++))

for (( 初始值;循环控制条件;变化 ))
    do
        程序
    done

例子

  • 从1加到100
    s=0
    for (( i=1;i<=100;i=i+1 ))
    do
        s=$(( $s+$i ))
        done
    echo "The sum of 1+2+...+100 is : $s"

注意

  • for后边跟两个括号(())
  • 值、括号、运算符之间两两空格(这一项还不确定,因为我发下现没空格也能运行)

③用for循环读取文件中的值

学习自:bash如何使用for循环按行读入文本文件 - 简书

空格为分隔符

for i in $`cat t.txt` 或 for i in $(cat t.txt)
    do
        echo $i
    done

换行符为空格符

IFS=$'\n'
for i in $''`cat t.txt`
    do
        echo $i
    done

 

4)while循环

用法

while [ 条件1 ]
    do
        程序
    done

例子

  • 从1加到100
    复制代码
    [root@localhost ~]$ vi sh/addnum.sh
    #!/bin/bash
    #从1加到100
    
    i=1
    s=0
    #给变量i和变量s赋值
    
    while [ $i -le 100 ]
    #如果变量i的值小于等于100,则执行循环
        do
            s=$(( $s+$i ))
            i=$(( $i+1 ))
        done
    echo "The sum is: $s"
    复制代码

     

5)until循环

与while的区别在于until是在条件不成立时才进行循环

用法

until [ 条件1 ]
    do
        程序
    done

例子

  • 从1加到100
    复制代码
    [root@localhost ~]$ vi sh/until.sh
    #!/bin/bash
    #从1加到100
    
    i=1
    s=0
    #t给变量i和变量s赋值
    
    until [ $i -gt 100 ]
    #循环直到变量i的值大于100,就停止循环
        do
            s=$(( $s+$i ))
            i=$(( $i+1 ))
        done
    echo "The sum is: $s"
    复制代码

6)流程控制(跳出循环):break与continue,

用途和其他语言的中的相同,break——跳出循环,continue——继续下次循环

复制代码
[root@localhost ~]$ vi sh/break.sh
#!/bin/bash
#演示continue

for (( i=1;i<=10;i=i+1 ))
#循环十次
    do
        if ["$i" -eq 4 ]
        #如果变量i的值等于4
            then
            continue
            #退出换成continue
        fi
    echo $i
    #输出变量i的值
    done
复制代码
复制代码
[root@localhost ~]$ vi sh/continue.sh
#!/bin/bash
#演示continue

for (( i=1;i<=10;i=i+1 ))
#循环十次
    do
        if ["$i" -eq 4 ]
        #如果变量i的值等于4
            then
            continue
            #退出换成continue
        fi
    echo $i
    #输出变量i的值
    done
复制代码

7)函数

用法

#定义
function 函数名 () {
    程序
}

#调用
    函数名 $参数1 $参数2

例子

接收用户输入的数字,然后从1加到这个数字

复制代码
 1 [root@localhost ~]$ vi sh/function.sh
 2 #!/bin/bash
 3 #接收用户输入的数字,然后从1加到这个数字
 4 
 5 function sum () {
 6     #定义函数sum
 7     s=0
 8     for (( i=0; i<=$num;i=i+1 ))
 9         #循环直到i大于$1为止。$1是函数sum 的第一个参数
10         #在函数中也可以使用位置参数变量,不过这里的$1指的是函数的第一个参数
11         do
12             s=$(( $i+$s ))
13         done
14     echo "The sum of 1+2+3...+$1 is :$s"
15     #输出1加到$1的和
16 }
17 
18 read -p "Please input a number: " -t 30 num
19 #接收用户输入的数字,并把值赋予变量num
20 y=$(echo $num | sed 's/[0-9]//g')
21 #把变量num的值替换为空,并赋予变量y
22 
23 if [ -z "$y"]
24 #判断变量y是否为空,以确定变量num中是否为数字
25     then
26         sum $num
27         #调用sum函数,并把变量num的值作为第一个参数传递给sum函数
28 else
29         echo "Error!! Please input a number!"
30         #如果变量num 的值不是数字,则输出报错信息
31 fi
复制代码

注意

  • 8行的$num,这个num是外部已经命名的变量num,参数是没有名字的,只以$1、$2……加以分辨;
  • 14行的$1代表的才是传入参数,$数字的具体用法参照预定义变量一节

七、字符串

1)正则表达式

元字符

说明

例子

\ 转义符,将特殊字符转为一般字符 a\.b 只能匹配 a.b
^ 匹配开头 ^tux 匹配以tux开头的行
$ 匹配结尾 tux$ 匹配以tux结尾的行
. 除换行符\n外的任意单个字符 ab.匹配任意ab+单字符
[ ] 匹配[ ]中的任意单字符
coo[kl]匹配cook和cool
[^] 匹配除了[^ ]列出的单字符 123[^45]匹配123x,x是除了4和5外的其他任意字符
[-] 匹配某个范围内的字符(必须递增) [0-9]可以匹配0-9的任意单个数字
? 匹配之前的项0或1次 colou?r可以匹配0或1次u,即color或colour
+ 匹配之前的项1或多次 sa-6+可以匹配多个6,即sa-6、sa-666
* 匹配之前的项人一次 co*l可以匹配任意个o,即col、cool、coool
() 匹配某个子串 ma(trix)?可以匹配matrix和ma
{n} 匹配之前的项n次 [0-9]{3}可以匹配任意三位数
{n,} 至少匹配之前的项n次 [0-9]{2,}匹配一个两位或以上的数
+n,m} 最少匹配n次,最多匹配m次 [0-9]{2,5}匹配一个2至5位数
| ab(c|d)匹配c或d,即abc和abd均可

2)字符截取、替换

①cut——列提取

用法

cut [选项] 文件名

选项

选项

说明

-f

列号;提取第几列

多列用逗号,分隔,如cut -f 2,3

-d 分隔符;以指定分隔符分割列
-n 取消分割多字节字符
-c

字符范围();不依赖分隔符来区分列,而是通过字符范围(从第几个字符开始分割)进行字符提取:

n-:从第n个字符到行尾;

n-m:从第n到第m个字符;

-m:从第一个到第m个字符;

--complement 补全被选择的字符或字段
--out-delimiter 输出内容为字段分隔符

说明:

  • 默认分隔符为制表符,即TAB

例子

  • 创建文件

[root@localhost ~]$ vi student.txt
id    name    gender    mark
1    liming    m        86
2    sc        m        67
3    tg        n        90
  • 分割

    复制代码
    [root@localhost ~]$ vi student.txt
    id    name    gender    mark
    1    liming    m        86
    2    sc        m        67
    3    tg        n        90
    
    
    [root@localhost ~]$ cut -f 2 student.txt
    #提取第二列内容
    
    [root@localhost ~]$ cut -f 2,3 student.txt
    #提取第2和3列的内容
    [root@localhost
    ~]$ cut -c 8- student.txt #每行从第八个字符开始提取到行尾,好像很乱啊,那是因为每行的字符个数不相等啊
    8-:提取到行尾
    8:提取第8个
    -8:从行首提取到第8个 [root@localhost
    ~]$ cut -d ":" -f 1,3 /etc/passwd #以“:”作为分隔符,提取/etc/passwd_文件的第一列和第三列
    复制代码

     

十、字符处理命令

1)sort排序命令

  • 用法

    sort [选项] 文件名

     

  • 选项

    选项

    说明

    -f 忽略大小写
    -b 忽略每行前边的空白部分
    -n 数值型排序,默认用字符串型排序
    -r 反向排序
    -u 删除重复行。同uniq命令
    -t 指定分隔符,默认TAB
    -k n[,m] 按照指定的字段范围排序。从第n字段开始,m字段结束(默认到行尾)
  • 例子
    默认是用每行开头第一个字符来进行排序的:
    [root@localhost~]$ sort /etc/passwd
    #排序用户信息文件

    反向排序用-r选项:

    [root@localhost~]$ sort -r/etc/passwd
    #反向排序

    如果要指定排序的字段,需要用"-t"选项指定分割符,并使用"-k"选项指定字段号。假设我们要用UID字段排序/etc/passwd文件

    [root@localhost~]$ sort -t ":" -k 3,3 /etc/passwd
    #指定分隔符是“:”,用第三字段开头,第三字段结尾排序,就是只用第三字段排序

    如果写为-k 3,代表从第三字段到行尾排序

2)uniq取消重复行

  • 用法

    [root@localhost~]$ uniq [选项] 文件名
    选项:
        -i:忽略大小写

     

3)wc统计命令

wc [选项] 文件名
选项:
    -l:只统计行数
    -w:只统计单词数
    -m:只统计字符数

 

 十一、一些函数

1、eval

学习自:shell 中的 eval_shell eval-CSDN博客

用法:eval 指令字符串(不加用引号)

说明:执行eval后的指令字符串,相当于我们直接在控制台输入指令字符串并按回车,

通常配合$变量来实现灵活、批量对指令中的某个字段的替换

例子

1)指令字符串中只有一个指令关键字,相当于直接执行后边的指令

1
2
echo $NAME
eval echo $NAME

此时二者是等价的。

2)指令字符串中有两个指令关键字,相当于先执行外层指令,再执行外层指令执行完毕后输出的指令

myfile="cat test.txt"

eval echo $myfile
#先执行echo $myfile,输出cat test.txt
#再执行eval cat test.txt,输出test.txt的内容

3)允许嵌套变量,此时在要嵌套的变量之前加/$

#输出最后一个参数
echo "Last argument is $(eval echo \$$#)"

 

posted @   ShineLe  阅读(71)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
历史上的今天:
2022-07-17 Springboot:@Service注解作用 以及 当有多个Impl时如何知道注入的是哪个Impl
点击右上角即可分享
微信分享提示