美文网首页
shell编程学习笔记(一)

shell编程学习笔记(一)

作者: dev_winner | 来源:发表于2020-10-22 11:14 被阅读0次
    • 编译型语言:程序在执行之前需要一个专门编译的过程,把程序编译成为机器语言文件,运行时不需要重新翻译,直接使用编译的结果即可。程序执行效率高,依赖编译器,跨平台性相对差些。例如:CC++语言等。
    • 解释型语言:程序不需要编译,其在运行时由解释器翻译成机器语言,每执行一次都要翻译一次,因此效率比较低。例如shellpython脚本语言等。
    • 编译型语言比解释型语言执行速度快,但是不如解释型语言跨平台性好。若是做底层开发或大型应用程序,则一般使用编译型语言;若是一些服务器脚本及一些辅助接口,对速度要求不高,对各平台的兼容性有要求,则一般用解释型语言
    • shell介于内核与用户之间,负责命令的解释。shell就是人机交互的一个桥梁。
    • shell的种类:
    [root@localhost ~]# cat /etc/shells
    /bin/sh #bash的一个快捷方式
    /bin/bash #bash是大多数Linux默认的shell,包含的功能几乎可以覆盖shell所有的功能
    /sbin/nologin #表示非交互,无法登录操作系统,所谓“无法登录”指的仅是这个用户无法使用bash或其他shell来登陆系统而已,并不是说这个账号就无法使用系统资源。
    /bin/tcsh #具有c语言风格的一种shell,具有许多特性,但也有一些缺陷
    /bin/csh #csh的增强版,完全兼容csh
    [root@localhost ~]# ll /bin/sh
    lrwxrwxrwx. 1 root root 4 11月 16 2019 /bin/sh -> bash
    [root@localhost ~]# ll /bin/bash
    -rwxr-xr-x. 1 root root 964600 8月   8 2019 /bin/bash
    [root@localhost ~]# ps
       PID TTY          TIME CMD
     22430 pts/2    00:00:00 bash #当前终端所提交的任何指令都由bash解析器解析并执行
     23200 pts/2    00:00:00 ps
    
    • shell脚本的定义:将需要执行的命令保存到文本中,按照顺序执行。它是解释型的,意味着不需要编译。
    • 何时用到shell脚本?将重复化复杂化的工作命令写成脚本,以后仅仅需要执行该脚本就能完成这些工作。
    • shell脚本能干啥?①自动化软件部署(LAMP/LNMP/Tomcat/...);②自动化管理(系统初始化脚本、批量更改主机密码,推送公钥等);③自动化分析处理(统计网站访问量);④自动化备份(数据库备份、日志转储);⑤自动化监控脚本
    • shell脚本第一行的魔法字符#!指定解释器(必写)。例如:#!/bin/bash表示该命令以下内容使用bash解释器来解析。注意:若直接将解析器路径写死在脚本里,在某些系统可能存在找不到解析器的兼容性问题,此时则可换作:#!/bin/env 解析器,例如:#!/bin/env bash
    [root@localhost myshell]# vim first_shell.sh
    [root@localhost myshell]# chmod +x first_shell.sh(对该脚本添加执行权限)
    [root@localhost myshell]# ./first_shell.sh (脚本标准的执行方式:先添加执行权限,再用相对路径或绝对路径运行该文件)
    hello world!
    [root@localhost myshell]# /root/myshell/first_shell.sh 
    hello world!
    [root@localhost myshell]# bash -x first_shell.sh (-x:查看该脚本逐行执行的命令和对应的结果)
    + echo 'hello world!'
    hello world!
    [root@localhost myshell]# bash -n first_shell.sh (-n:查看该脚本是否有语法问题)
    [root@localhost myshell]# 
    [root@localhost myshell]# chmod -x first_shell.sh (若脚本没有执行权限,则可以使用非标准的执行方式,但一般不推荐使用)
    [root@localhost myshell]# source first_shell.sh 
    hello world!
    [root@localhost myshell]# date +'%F %T'
    2020-10-22 13:44:58
    
    • 变量的使用:变量名=变量值。默认情况下,shell中的变量是没有类型的,变量被赋予任何值。
    [root@localhost myshell]# A=hello
    [root@localhost myshell]# echo $A
    hello
    [root@localhost myshell]# echo ${A}
    hello
    [root@localhost myshell]# unset A (取消变量)
    [root@localhost myshell]# echo $A(显示空行)
    
    [root@localhost myshell]# 
    
    • 变量的定义规则:①变量名区分大小写;②变量名不能有特殊符号;③ 变量名不能以数字开头;④等号两边不能有任何空格;⑤变量名尽量做到见名知意。注意:对于有空格的字符串给变量赋值时,要用引号引起来。
    [root@localhost myshell]# *A=hello
    bash: *A=hello: 未找到命令...
    [root@localhost myshell]# ?A=hello
    bash: ?A=hello: 未找到命令...
    [root@localhost myshell]# @A=hello
    bash: @A=hello: 未找到命令...
    [root@localhost myshell]# A=hello world
    bash: world: 未找到命令...
    [root@localhost myshell]# A="hello world"
    [root@localhost myshell]# A='hello world'
    [root@localhost myshell]# echo ${A:2:3}(从第3个字符开始连续截取3个字符)
    llo
    [root@localhost myshell]# 1A=hello
    bash: 1A=hello: 未找到命令...
    [root@localhost myshell]# _A=hello
    [root@localhost myshell]# echo $_A
    hello
    [root@localhost myshell]# echo ${_A}
    hello
    [root@localhost myshell]# A= 123
    bash: 123: 未找到命令...
    [root@localhost myshell]# A = 123
    bash: A: 未找到命令...
    [root@localhost myshell]# A =123
    bash: A: 未找到命令...
    
    • $变量名${变量名}的区别?相同点:都可以调用变量;不同点:${变量名}可以只截取变量的一部分,而$变量名不可以。
    [root@localhost myshell]# hostname
    localhost.localdomain 
    [root@localhost myshell]# A=`hostname`
    [root@localhost myshell]# echo $A
    localhost.localdomain
    [root@localhost myshell]# cat /etc/redhat-release (系统版本)
    CentOS Linux release 7.7.1908 (Core)
    [root@localhost myshell]# uname -r(内核版本)
    3.10.0-1062.4.3.el7.x86_64
    [root@localhost myshell]# B=$(uname -r)
    [root@localhost myshell]# echo $B
    3.10.0-1062.4.3.el7.x86_64
    
    • 交互式定义变量:read [选项] 变量名
    • 常见选项:
    -p:定义提示用户的信息
    -n:限制变量值的长度
    -s:不显示用户输入的内容
    -t:定义超时时间,默认单位为秒(限制用户输入变量值的超时时间)
    [root@localhost myshell]# read name
    zhangsan
    [root@localhost myshell]# echo $name
    zhangsan
    [root@localhost myshell]# read -p "Input your name:" name
    Input your name: zhangsan 
    [root@localhost myshell]# echo $name
    zhangsan
    [root@localhost myshell]# read -sp "Input your password:" password(不显示输入的密码)
    Input your password:[root@localhost myshell]# echo $password
    123
    [root@localhost myshell]# read -n 5 -p "Input your name:" name(限制变量值的长度)
    Input your name:12345[root@localhost myshell]# read -t 3 -p "name: " name(设置输入超时时间,只有在超时时间内按下回车键才能有效赋值)
    name: [root@localhost myshell]# read -t 3 -p "name: " name
    name: ddcdsdddddddd[root@localhost myshell]# echo $name(变量值显示为空)
    
    [root@localhost myshell]# read -p "请输入ip地址:" IP < ip.txt 
    [root@localhost myshell]# echo $IP
    10.1.1.1
    
    • 定义有类型的变量:declare [选项] 变量名=变量值
    • 常用选项:
    -i:将变量看成整数(declare -i A=233)
    -r:定义只读变量(declare -r B=hello)
    -a:定义普通数组;查看普通数组
    -A:定义关联数组;查看关联数组
    -x:将临时变量导出为环境变量(declare -x AAA=123 等价于 export AAA=123)
    ---------------------------------------------------------------------------------------------
    [root@localhost myshell]# A=hello
    [root@localhost myshell]# echo $A
    hello
    [root@localhost myshell]# declare -i A=123(声明并定义变量A为整数类型)
    [root@localhost myshell]# echo $A
    123
    [root@localhost myshell]# A=hello(已声明了变量A的数据类型,则应将其赋值为整数)
    [root@localhost myshell]# echo $A
    0
    [root@localhost myshell]# A=333
    [root@localhost myshell]# echo $A
    333
    [root@localhost myshell]# declare -r B=hello
    [root@localhost myshell]# echo $B
    hello
    [root@localhost myshell]# B=8888
    -bash: B: 只读变量
    [root@localhost myshell]# unset B(无法删除只能读,变量B的生命周期在当前终端内,键入命令:exit退出当前终端程序即可)
    -bash: unset: B: 无法反设定: 只读 variable
    [root@localhost myshell]# AAA=zhangsan
    [root@localhost myshell]# env | grep AAA
    [root@localhost myshell]# export AAA(导出AAA为环境变量)
    [root@localhost myshell]# env | grep AAA
    AAA=zhangsan
    [root@localhost myshell]# declare -x BBB=lisi(默认导出BBB为环境变量)
    [root@localhost myshell]# env | grep BBB
    BBB=lisi
    
    • 本地变量:当前用户自定义的变量。只在当前进程中有效,其他进程及当前进程的子进程无效。
    [root@localhost myshell]# ps
       PID TTY          TIME CMD
     22430 pts/2    00:00:01 bash
     30287 pts/2    00:00:00 ps
    [root@localhost myshell]# C=hello
    [root@localhost myshell]# echo $C
    hello
    [root@localhost myshell]# su - zwj(切换到其他用户)
    [zwj@localhost ~]$ ps
       PID TTY          TIME CMD
     30297 pts/2    00:00:00 bash
     30338 pts/2    00:00:00 ps
    [zwj@localhost ~]$ echo $C(变量C在其他进程无效)
    
    [zwj@localhost ~]$ exit(退出当前进程)
    登出
    [root@localhost myshell]# ps
       PID TTY          TIME CMD
     22430 pts/2    00:00:01 bash
     30347 pts/2    00:00:00 ps
    [root@localhost myshell]# /bin/bash(再开一个子进程 bash)
    [root@localhost myshell]# ps
       PID TTY          TIME CMD
     22430 pts/2    00:00:01 bash
     30348 pts/2    00:00:00 bash
     30376 pts/2    00:00:00 ps
    [root@localhost myshell]# ps auxf | grep bash(查看父子进程的关系)
    root        869  0.0  0.0 115440  1012 ?        S    10月21   0:04 /bin/bash /usr/sbin/ksmtuned
    root      22430  0.0  0.1 116492  3256 pts/2    Ss   09:22   0:01      \_ -bash(父进程)
    root      30348  0.0  0.1 116352  2924 pts/2    S    20:57   0:00          \_ /bin/bash(子进程)
    root      30410  0.0  0.0 112728   968 pts/2    S+   21:00   0:00              \_ grep --color=auto bash
    root       3183  0.0  0.1 116564  2988 pts/0    Ss+  10月21   0:00  \_ bash
    [root@localhost myshell]# echo $C(变量C在子进程无效)
    
    [root@localhost myshell]# 
    
    
    • 环境变量:当前进程有效,且能够被子进程调用。
    • env:查看当前用户的环境变量(env | grep 环境变量名
    • set:查询当前用户的所有变量(临时变量与环境变量)(set | grep 变量名
    • 设置环境变量的用法:①export 变量名=变量值;②将临时变量export一下,即export 临时变量;③使用declare语法,即declare -x 变量名=变量值
    [root@localhost ~]# export DDD=lisi(设置环境变量)
    [root@localhost ~]# ps
       PID TTY          TIME CMD
     33225 pts/1    00:00:00 bash
     34261 pts/1    00:00:00 ps
    [root@localhost ~]# /bin/bash(开启一个子进程 bash)
    [root@localhost ~]# ps
       PID TTY          TIME CMD
     33225 pts/1    00:00:00 bash
     34262 pts/1    00:00:00 bash
     34290 pts/1    00:00:00 ps
    [root@localhost ~]# env | grep DDD(子进程可以调用父进程的环境变量 DDD)
    DDD=lisi
    [root@localhost ~]# exit
    exit
    [root@localhost ~]# ps
       PID TTY          TIME CMD
     33225 pts/1    00:00:00 bash
     34293 pts/1    00:00:00 ps
    [root@localhost ~]# su - zwj(切换到其他用户)
    上一次登录:四 10月 22 20:56:56 CST 2020pts/2 上
    [zwj@localhost ~]$ env | grep DDD(环境变量 DDD 在其他进程无效)
    [zwj@localhost ~]$ exit
    登出
    [root@localhost ~]# ps
       PID TTY          TIME CMD
     33225 pts/1    00:00:00 bash
     34343 pts/1    00:00:00 ps
    
    • 全局变量:所有的用户和程序都能调用且继承,新建的用户也默认能调用。
    文件名 说明
    $HOME/.bashrc 当前用户的bash信息,用户登录时读取
    $HOME/.bash_profile 当前用户的环境变量,用户登录时读取
    $HOME/.bash_logout 当前用户退出当前shell时最后读取
    $HOME/.bash_history 当前用户使用过的历史命令
    /etc/bashrc 全局的bash信息,所有用户都生效
    /etc/profile 全局环境变量信息,系统和所有用户都生效
    • 注意:以上文件修改后都需要重新source让其生效或者退出重新登录。若全局和局部发生冲突,一般以局部为准。
    • 用户登录系统读取相关文件的顺序:
    1、/etc/profile
    2、$HOME/.bash_profile
    3、$HOME/.bashrc
    4、/etc/bashrc
    5、$HOME/.bash_logout
    
    • 系统变量(内置bash中的变量):shell本身已固定好了它的名字和作用。
    $?:上一条命令执行后返回的状态,状态值为0表示执行正常,非0表示执行异常或错误。
    $0:当前执行的程序名或脚本名。
    $#:执行脚本命令时后面传递参数的个数。
    $*:执行脚本命令时后面传递的所有参数,参数当成一个整体输出,每一个变量参数之间以空格隔开。
    $@:执行脚本命令时后面传递的所有参数,参数是独立的,也是全部输出。
    $1~$9:执行脚本命令时后面的位置参数,$1表示第1个位置参数,依此类推。
    ${10}~${n}:扩展位置参数,第10个位置以后变量获取,其下标必须用{}大括号括起来(2位数字以上加上花括号)。
    $$:当前所在进程的进程号,如echo $$。
    $!:当前终端后台运行的最后一个进程号。 
    !$:调用最后一条命令历史中的参数
    ---------------------------------------------------------------------------------------
    [root@localhost ~]# jobs(查看后台运行的进程)
    [root@localhost ~]# sleep 500 &(开启一个后台进程)
    [1] 35728
    [root@localhost ~]# jobs
    [1]+  运行中               sleep 500 &
    [root@localhost ~]# sleep 600
    ^Z
    [2]+  已停止               sleep 600
    [root@localhost ~]# jobs
    [1]-  运行中               sleep 500 &
    [2]+  已停止               sleep 600
    [root@localhost ~]# ps -ef | grep sleep
    root      35728  33225  0 10:31 pts/1    00:00:00 sleep 500
    root      35736    869  0 10:31 ?        00:00:00 sleep 60
    root      35737  33225  0 10:31 pts/1    00:00:00 sleep 600
    root      35739  33225  0 10:32 pts/1    00:00:00 grep --color=auto sleep
    [root@localhost ~]# echo $!(当前终端后台运行的最后一个进程号)
    35728
    [root@localhost myshell]# ls -l
    总用量 0
    -rw-r--r--. 1 root root 0 10月 23 10:38 testFun.sh
    [root@localhost myshell]# echo !$(调用最后一条命令历史(ls -l)中的参数(-l))
    echo -l
    -l
    
    • 简单的四则运算:
    表达式 举例
    $(()) echo $((1+1))
    $[] echo $[10-5]
    expr expr 10 / 5(注意符号左右各加一个空格)
    let n=1;let n+=1 等价于 let n=n+1
    [root@localhost myshell]# expr 10 \* 5(星号是特殊字符,必须加反斜杠转义一下)
    50
    [root@localhost myshell]# echo $[10%8]
    2
    [root@localhost myshell]# expr 10 % 5
    0
    [root@localhost myshell]# expr 10/5(符号左右各加一个空格)
    10/5
    [root@localhost myshell]# n=1;let n=n+1;echo $n
    2
    [root@localhost myshell]# let n+=2
    [root@localhost myshell]# echo $n
    4
    [root@localhost myshell]# let n=n**3(4^3=64)
    [root@localhost myshell]# echo $n
    64
    [root@localhost myshell]# echo $[1+1.5](shell不支持小数运算)
    -bash: 1+1.5: 语法错误: 无效的算术运算符 (错误符号是 ".5")
    [root@localhost myshell]# echo 1+1.5 | bc(小数计算必须交给bc程序去处理)
    2.5
    [root@localhost myshell]# bc
    bc 1.06.95
    Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
    This is free software with ABSOLUTELY NO WARRANTY.
    For details type `warranty'. 
    1+4.5
    5.5
    2^7
    128
    2**7(求次幂不支持**符号,但支持^符号)
    (standard_in) 2: syntax error
    quit
    [root@localhost myshell]# 
    
    • 条件判断语法结构:①test 条件表达式;②[ 条件表达式 ];③[[ 条件表达式 ]](支持正则)。注意:后两种语法的条件表达式两边必须有空格!
    • 文件类型的比较运算符:
    -e:判断文件是否存在(任何类型文件)
    -f:判断文件是否存在且为一个普通文件
    -d:判断文件是否存在且为一个目录
    -L:判断文件是否存在且为一个软链接文件
    -b:判断文件是否存在且为一个块设备文件
    -S:判断文件是否存在且为一个套接字文件
    -c:判断文件是否存在且为一个字符设备文件
    -p:判断文件是否存在且为一个命名管道文件
    -s:判断文件是否存在且为一个非空文文件(有内容)
    ------------------------------------------------------------------------------------------
    [root@localhost myshell]# ll
    总用量 4
    -rw-r--r--. 1 root root 0 10月 23 17:23 file1
    -rw-r--r--. 1 root root 6 10月 23 17:24 file2
    -rw-r--r--. 1 root root 0 10月 23 10:38 testFun.sh
    [root@localhost myshell]# test -e ./file1;echo $?(判断文件是否存在)
    0
    [root@localhost myshell]# test -e ./test1;echo $?
    1
    [root@localhost myshell]# ll
    总用量 4
    drwxr-xr-x. 2 root root 6 10月 23 18:02 dir1
    -rw-r--r--. 1 root root 0 10月 23 17:23 file1
    -rw-r--r--. 1 root root 6 10月 23 17:24 file2
    -rw-r--r--. 1 root root 0 10月 23 10:38 testFun.sh
    [root@localhost myshell]# [ -d ./dir1 ];echo $?(判读目录是否存在)
    0
    [root@localhost myshell]# test -d ./dir1;echo $?
    0
    [root@localhost myshell]# ln -s file1 test1(设置文件file1的软链接文件为test1)
    [root@localhost myshell]# ll
    总用量 4
    drwxr-xr-x. 2 root root 6 10月 23 18:02 dir1
    -rw-r--r--. 1 root root 0 10月 23 17:23 file1
    -rw-r--r--. 1 root root 6 10月 23 17:24 file2
    lrwxrwxrwx. 1 root root 5 10月 23 18:06 test1 -> file1
    -rw-r--r--. 1 root root 0 10月 23 10:38 testFun.sh
    [root@localhost myshell]# [ -L ./test1 ];echo $?(判断是否为软链接文件)
    0
    [root@localhost myshell]# [[ -f ./file2 ]];echo $?(判断是否为普通文件)
    0
    [root@localhost myshell]# [[ -f ./test1 ]];echo $?
    0
    [root@localhost myshell]# [ ! -d ./dir1 ];echo $?(判断目录是否不存在)
    1
    [root@localhost myshell]# [ ! -d ./dir2 ];echo $?
    0
    
    • 判断文件权限的运算符:
    -r:当前用户对其是否可读
    -w:当前用户对其是否可写
    -x:当前用户对其是否可执行
    -u:是否有suid,高级权限冒险位
    -g:是否sgid,高级权限强制位
    -k:是否有t位,高级权限粘滞位(只有文件的创建者和管理员,控制删除)
    ------------------------------------------------------------------------------
    [root@localhost myshell]# ll
    总用量 4
    drwxr-xr-x. 2 root root 6 10月 23 18:02 dir1
    -rw-r--r--. 1 root root 0 10月 23 17:23 file1
    -rw-r--r--. 1 root root 6 10月 23 17:24 file2
    lrwxrwxrwx. 1 root root 5 10月 23 18:06 test1 -> file1
    -rw-r--r--. 1 root root 0 10月 23 10:38 testFun.sh
    [root@localhost myshell]# test -r testFun.sh;echo $?(判断root用户对该文件是否可读)
    0
    [root@localhost myshell]# [ -w ./testFun.sh ];echo $?(判断root用户对该文件是否可写)
    0
    
    • 文件新旧的比较运算符(新旧指文件的修改时间):
    file1 -nt file2:比较file1是否比file2新
    file1 -ot file2:比较file1是否比file2旧
    file1 -ef file2:比较是否为同一个文件或用于判断硬链接,即是否指向同一个inode号(通过 ll -i 查看)
    --------------------------------------------------------------------------------------
    [root@localhost myshell]# ll -i(文件目录的详细解释)
    总用量 12
    45158288 -rw-r--r--. 1 root root 2 10月 23 22:05 ?(第一列为inode索引节点编号)
     6936436 drwxr-xr-x. 2 root root 6 10月 23 18:02 dir1
    37437214 -rw-r--r--. 1 root root 6 10月 23 21:26 file1
    37437215 -rw-r--r--. 1 root root 6 10月 23 17:24 file2
    45158280 lrwxrwxrwx. 1 root root 5 10月 23 18:06 test1 -> file1
    37437213 -rw-r--r--. 1 root root 0 10月 23 10:38 testFun.sh
    [root@localhost myshell]# ll
    总用量 4
    drwxr-xr-x. 2 root root 6 10月 23 18:02 dir1
    -rw-r--r--. 1 root root 0 10月 23 17:23 file1
    -rw-r--r--. 1 root root 6 10月 23 17:24 file2
    lrwxrwxrwx. 1 root root 5 10月 23 18:06 test1 -> file1
    [root@localhost myshell]# test file1 -nt file2;echo $?
    1
    [root@localhost myshell]# test file1 -ot file2;echo $?
    0
    [root@localhost myshell]# cat file1
    hello
    [root@localhost myshell]# cat file2
    hello
    [root@localhost myshell]# [ file1 -ef file2 ];echo $?
    1
    
    • 判断整数大小的运算符:
    -eq:相等
    -ne:不等
    -gt:大于
    -lt:小于
    -ge:大于等于
    -le:小于等于
    
    • 判断字符串的运算符,注意:双引号引起来,将其看成一个整体;===运算符在表达式[ 字符串 ]中都表示判断两个字符串是否相等。
    -z:判断是否为空字符串,字符串长度为0则成立
    -n:判断是否为非空字符串,字符串长度不为0则成立
    string1 = string2:判断字符串是否相等
    string1 != string2:判断字符串是否不等
    -------------------------------------------------------------
    [root@localhost myshell]# test -z "hello world";echo $?(结果为假)
    1
    [root@localhost myshell]# test -n "hello world";echo $?(结果为真)
    0
    [root@localhost myshell]# test -z "";echo $?(结果为真)
    0
    [root@localhost myshell]# test "hello " = "world";echo $?(结果为假)
    1
    [root@localhost myshell]# test "hello " != "world";echo $?(结果为真)
    0
    [root@localhost myshell]# [ 1 -eq 2 ];echo $?(结果为假)
    1
    [root@localhost myshell]# [ 1 -le 2 ];echo $?(结果为真)
    0
    [root@localhost myshell]# A="hello";B=world;[ "$A" = "$B" ];echo $?
    1
    [root@localhost myshell]# A="hello";B=world;[ "$A" != "$B" ];echo $?
    0
    [root@localhost myshell]# A="hello";B=world;[ "$A" == "$B" ];echo $?
    1
    
    • 多重条件判断的运算符:
    -a 和 &&:逻辑与(注意:&& 运算符不能写在[]运算符括号里)
    -o 和 ||:逻辑或
    -------------------------------------------------------------------
    [root@localhost myshell]# [ 1 -eq 1 -a 1 -le 0 ];echo $?
    1
    [root@localhost myshell]# [ 1 -eq 1 ] && [ 1 -ne 0 ];echo $?
    0
    [root@localhost myshell]# [ 1 -eq 1 -o 1 -ne 0 ];echo $?
    0 
    [root@localhost myshell]# id -u(打印当前用户的uid)
    0
    [root@localhost myshell]# [ $(id -u) -eq 0 ] && echo "admin"
    admin
    [root@localhost myshell]# [ 1 -eq 2 ] && echo AAA || echo BBB && echo CCC 
    BBB
    CCC
    
    • ;:用于分割命令或表达式。
    • 类C风格的数值比较,注意:在(( ))中,=表示赋值,==表示判断。
    [root@localhost myshell]# ((1==1));echo $?
    0
    [root@localhost myshell]# ((A1=10));echo $?
    0
    [root@localhost myshell]# echo $A1
    10
    [root@localhost myshell]# unset A1
    [root@localhost myshell]# ((A1==10));echo $?
    1
    
    • [][[]]的区别:
    [root@localhost myshell]# A=
    [root@localhost myshell]# echo $A
    
    [root@localhost myshell]# test "$A" = "hello";echo $?
    1
    [root@localhost myshell]# [ $A = hello ];echo $?(只有一个中括号时,字符串的比较必须加引号)
    -bash: [: =: 期待一元表达式
    2
    [root@localhost myshell]# 
    [root@localhost myshell]# [ "$A" = "hello" ];echo $?
    1
    [root@localhost myshell]# [[ $A = hello ]];echo $?(不加引号则改为2个中括号才能进行比较)
    1
    
    • if结构:
    if [ condition ];then
      command
      command
    fi
    --------------------------------
    if test condition;then
      command
      command
    fi
    --------------------------------
    if [[ condition ]];then
      command
      command
    fi
    --------------------------------
    等价于:[ condition ] && command
    
    • if-else结构:
    if [ condition ];then
       command1
    else 
       command2
    fi
    --------------------------------
    等价于:[ condition ] && command1 || command2
    
    • if-elif-else结构:
    if [ condition ];then
      command1
    elif [ condition ];then
      command2
    else 
      command3
    fi 
    
    • 判断两台主机是否互通:编写脚本文件(ping_remote.sh)
    #!/bin/env bash
    # 该脚本用于哦按段当前主机是否和远程指定的主机互通
    
    # 交互式定义变量,让用户自己决定ping哪个主机
    read -p "请输入你要ping的主机IP:" ip
    # 使用ping命令判断两台主机是否互通,参数-c:控制发送次数为1次,扔掉输出结果:&>/dev/null 
    ping -c1 $ip &>/dev/null 
    
    if [ $? -eq 0 ];then
        echo "当前主机和远程主机:$ip是互通的"
    else 
        echo "当前主机和远程主机:$ip是不通的"
    fi
    --------------------------------------------------------------------------------
    [root@localhost myshell]# vim ping_remote.sh
    [root@localhost myshell]# chmod +x ping_remote.sh
    [root@localhost myshell]# ll
    总用量 16
    drwxr-xr-x. 2 root root   6 10月 23 18:02 dir1
    -rw-r--r--. 1 root root   6 10月 23 21:26 file1
    -rw-r--r--. 1 root root   6 10月 23 17:24 file2
    -rwxr-xr-x. 1 root root 435 10月 24 09:24 ping_remote.sh
    lrwxrwxrwx. 1 root root   5 10月 23 18:06 test1 -> file1
    -rw-r--r--. 1 root root   0 10月 23 10:38 testFun.sh
    [root@localhost myshell]# ./ping_remote.sh 
    请输入你要ping的主机IP:10.1.1.1
    当前主机和远程主机:10.1.1.1是互通的
    [root@localhost myshell]# ./ping_remote.sh 
    请输入你要ping的主机IP:10.1.1.100
    当前主机和远程主机:10.1.1.100是不通的
    
    • 判断某个程序的进程是否存在:编写脚本文件(pid.sh),使用pgrep指令,其返回某个程序的进程号,如:pgrep httpd
    #!/bin/env bash
    # 判断一个程序(httpd)的进程是否存在
    pgrep httpd &>/dev/null
    test $? -eq 0 && echo "当前httpd进程存在" || echo "当前httpd进程不存在"
    
    • 判断一个服务是否正常:①wget -P 存放文件的路径 下载网址,如:wget -P /shell http://www.baidu.com;②curl 访问网址,如:curl baidu.com。编写脚本(web_server_check.sh)
    #!/bin/env bash
    # 判断门户网站是否能够正常提供服务
    
    # 定义变量
    web_server=www.baidu.com
    # 访问网站
    wget -P /root/myshell $web_server &>/dev/null
    [ $? -eq 0 ] && echo "当前网站服务是ok" && rm -f /root/myshell/index.* || echo "当前网站服务不ok,请立刻处理"
    --------------------------------------------------------------------------------------------
    [root@localhost myshell]# wget http://baidu.com
    --2020-10-24 10:34:11--  http://baidu.com/
    正在解析主机 baidu.com (baidu.com)... 220.181.38.148, 39.156.69.79
    正在连接 baidu.com (baidu.com)|220.181.38.148|:80... 已连接。
    已发出 HTTP 请求,正在等待回应... 200 OK
    长度:81 [text/html]
    正在保存至: “index.html”
    
    100%[==================================================================================================================================================>] 81          --.-K/s 用时 0s      
    
    2020-10-24 10:34:11 (7.41 MB/s) - 已保存 “index.html” [81/81])
    [root@localhost myshell]# curl http://www.baidu.com
    
    • 判断某个用户是否存在,编写脚本文件(check_user.sh)
    #!/bin/env bash
    read -p "请输入一个用户名:" user_name
    id $user_name &>/dev/null
    [ $? -eq 0 ] && echo "该用户存在!" || echo "该用户不存在!"
    --------------------------------------------------------------------------------
    [root@localhost myshell]# id zwj
    uid=1002(zwj) gid=1002(wudang) 组=1002(wudang)
    [root@localhost myshell]# chmod +x check_user.sh 
    [root@localhost myshell]# ls
    check_user.sh  dir1  file1  file2  pid.sh  ping_remote.sh  test1  testFun.sh  web_server_check.sh
    [root@localhost myshell]# ll
    总用量 24
    -rwxr-xr-x. 1 root root 165 10月 24 10:57 check_user.sh
    drwxr-xr-x. 2 root root   6 10月 23 18:02 dir1
    -rw-r--r--. 1 root root   6 10月 23 21:26 file1
    -rw-r--r--. 1 root root   6 10月 23 17:24 file2
    -rwxr-xr-x. 1 root root 175 10月 24 10:22 pid.sh
    -rwxr-xr-x. 1 root root 435 10月 24 09:24 ping_remote.sh
    lrwxrwxrwx. 1 root root   5 10月 23 18:06 test1 -> file1
    -rw-r--r--. 1 root root   0 10月 23 10:38 testFun.sh
    -rwxr-xr-x. 1 root root 299 10月 24 10:41 web_server_check.sh
    [root@localhost myshell]# ./check_user.sh 
    请输入一个用户名:zwj
    该用户存在!
    [root@localhost myshell]# ./check_user.sh 
    请输入一个用户名:zhangsan
    该用户不存在!
    
    • 带列表for循环:
    for variable in {list}
      do 
        command
        command
      done
    --------------------------------------
    for variable in a b c
      do 
        command
        command
      done
    
    • 打印0~4中所有的偶数:编写脚本(for1.sh)
    #!/bin/env bash
    for i in {0..4..2}
      do
        echo $i
      done
    ------------------------------------------------------------------------------------
    [root@localhost myshell]# vim for1.sh
    [root@localhost myshell]# chmod +x for1.sh 
    [root@localhost myshell]# ./for1.sh 
    0
    2
    4
    [root@localhost myshell]# for i in {0..4..2};do echo $i;done(步长是2)
    0
    2
    4
    [root@localhost myshell]# seq 3(给出1~3的序列)
    1
    2
    3
    [root@localhost myshell]# for i in $(seq 3);do echo $i;done
    1
    2
    3
    [root@localhost myshell]# for i in {3..1};do echo $i;done(逆序打印3到1)
    3
    2
    1
    [root@localhost myshell]# seq 3 -1 1(seq [OPTION]... FIRST INCREMENT LAST)
    3
    2
    1
    [root@localhost myshell]# for i in {3..1..-1};do echo $i;done
    3
    2
    1
    
    • 不带列表for循环(循环次数由执行脚本命令时用户传递的参数决定):
    for variable
      do
        command
        command
      done
    -----------------------------------------------------------
    #!/bin/env bash(编写脚本文件:for2.sh)
    for i
      do
        echo "hello"
      done
    -----------------------------------------------------------
    [root@localhost myshell]# vim for2.sh
    [root@localhost myshell]# chmod +x for2.sh 
    [root@localhost myshell]# ./for2.sh a b c
    hello
    hello
    hello
    [root@localhost myshell]# bash -x for2.sh a
    + for i in '"$@"'(shell会自动补全参数)
    + echo hello
    hello
    
    • 类C风格的for循环:
    for((expr1;expr2;expr3))
      do
        command
        command
      done
    -----------------------------------
    for((i=1;i<=5;i++))
      do
        echo $i
      done
    ----------------------------------------------------------------
    [root@localhost myshell]# for((i=1;i<=2;i++));do echo $i;done
    1
    2
    [root@localhost myshell]# sum=0;for i in {1..100..2};do let sum=$sum+$i;done;echo "1-100的奇数和是:$sum"
    1-100的奇数和是:2500
    
    • 判断一个正整数是否为质数:
    #!/bin/env bash
    # 定义变量来保存用户输入的数字
    read -p "请输入一个正整数:" num
    [ $num -eq 1 ] && echo "$num不是质数" && exit
    for i in `seq 2 $[$num-1]`
      do
        [ $[$num%$i] -eq 0 ] && echo "$num不是质数" && exit
      done
    echo "$num是质数" && exit
    ----------------------------------------------------------------------
    [root@localhost myshell]# vim isPrime.sh
    [root@localhost myshell]# chmod +x isPrime.sh 
    [root@localhost myshell]# ./isPrime.sh 
    请输入一个正整数:3
    3是质数
    
    • 批量创建5个用户并指定每个用户的用户组为class,同时设置每个用户的登录密码,编写脚本文件(add_users.sh):
    #!/bin/env bash
    # 判断 class 组是否存在
    grep -w ^class /etc/group &> /dev/null
    [ $? -ne 0 ] && groupadd class
    # 循环创建用户
    for((i=1;i<=5;i++))
      do
        useradd -G class u$i
        echo 123 | passwd --stdin u$i
      done
    ------------------------------------------------------------------------------
    [root@localhost myshell]# vim add_users.sh 
    [root@localhost myshell]# chmod +x add_users.sh 
    [root@localhost myshell]# ./add_users.sh 
    更改用户 u1 的密码 。
    passwd:所有的身份验证令牌已经成功更新。
    更改用户 u2 的密码 。
    passwd:所有的身份验证令牌已经成功更新。
    更改用户 u3 的密码 。
    passwd:所有的身份验证令牌已经成功更新。
    更改用户 u4 的密码 。
    passwd:所有的身份验证令牌已经成功更新。
    更改用户 u5 的密码 。
    passwd:所有的身份验证令牌已经成功更新。
    [root@localhost myshell]# for i in {1..5};do echo `id u$i`;done
    uid=1004(u1) gid=1005(u1) 组=1005(u1),1004(class)
    uid=1005(u2) gid=1006(u2) 组=1006(u2),1004(class)
    uid=1006(u3) gid=1007(u3) 组=1007(u3),1004(class)
    uid=1007(u4) gid=1008(u4) 组=1008(u4),1004(class)
    uid=1008(u5) gid=1009(u5) 组=1009(u5),1004(class)
    [root@localhost myshell]$ su - u2
    密码:
    [u2@localhost ~]$ 
    
    • 批量创建5个用户stu1~stu5,要求这个几个用户的家目录都在/rhome,编写脚本文件(create_users.sh)
    #/bin/env bash
    # 判断 /rhome 是否存在
    # 先判断根目录下是否有rhome文件,有则替换它的文件名为 rhome.bak
    [ -f /rhome ] && mv /rhome /rhome.bak
    # 否则判断是否存在 rhome 这个目录,没有则在根目录下创建 rhome 这个目录
    test ! -d /rhome && mkdir /rhome
    # 循环创建5个用户,并指定各自的家目录和设置各自的登录密码
    for((i=1;i<=5;i++))
      do
        useradd -d /rhome/stu$i stu$i
        echo 123 | passwd --stdin stu$i
      done
    ------------------------------------------------------------------------------
    [root@localhost myshell]# chmod +x create_users.sh 
    [root@localhost myshell]# ./create_users.sh 
    useradd:用户“stu1”已存在
    更改用户 stu1 的密码 。
    passwd:所有的身份验证令牌已经成功更新。
    useradd:用户“stu2”已存在
    更改用户 stu2 的密码 。
    passwd:所有的身份验证令牌已经成功更新。
    useradd:用户“stu3”已存在
    更改用户 stu3 的密码 。
    passwd:所有的身份验证令牌已经成功更新。
    useradd:用户“stu4”已存在
    更改用户 stu4 的密码 。
    passwd:所有的身份验证令牌已经成功更新。
    useradd:用户“stu5”已存在
    更改用户 stu5 的密码 。
    passwd:所有的身份验证令牌已经成功更新。
    [root@localhost myshell]# ll /rhome/
    总用量 0
    drwx------. 3 stu1 stu1 78 10月 24 17:17 stu1
    drwx------. 3 stu2 stu2 78 10月 24 17:17 stu2
    drwx------. 3 stu3 stu3 78 10月 24 17:17 stu3
    drwx------. 3 stu4 stu4 78 10月 24 17:17 stu4
    drwx------. 3 stu5 stu5 78 10月 24 17:17 stu5
    
    • 用脚本检查局域网内某些主机是否存活,编写脚本文件(ip_check.sh)
    • shell脚本并发:并发执行{程序}&表示将程序放在后台并行执行,若需要等待程序执行完毕再进行下面的内容,则需要加wait
    #!/bin/env bash
    # 定义变量
    ip=10.1.1
    # 循环去ping主机的IP
    for((i=1;i<=10;i++))
      do
      {
        ping -c1 $ip.$i &> /dev/null
        if [ $? -eq 0 ];then
        echo "$ip.$i is ok" >> /tmp/ip_up.txt
        else 
        echo "$ip.$i is down" >> /tmp/ip_down.txt
        fi
      }& #后台并发执行
      done
      wait(等待并发执行完成)
    echo "局域网内IP网络检测完毕..." 
    ---------------------------------------------------------------------------------
    [root@localhost myshell]# chmod +x ip_check.sh
    [root@localhost myshell]# time ./ip_check.sh (time命令统计脚本执行的时间)
    局域网内IP网络检测完毕...
    
    real    0m10.105s
    user    0m0.016s
    sys 0m0.100s
    [root@localhost tmp]# ll
    总用量 4
    -rw-r--r--. 1 root root 171 10月 24 20:41 ip_down.txt
    [root@localhost tmp]# cat ip_down.txt 
    10.1.1.2 is down
    10.1.1.5 is down
    10.1.1.6 is down
    10.1.1.8 is down
    10.1.1.10 is down
    10.1.1.3 is down
    10.1.1.1 is down
    10.1.1.9 is down
    10.1.1.4 is down
    10.1.1.7 is down
    
    • while循环语句:
    while 表达式
      do 
        command...
      done
    ----------------------------------------------------------------------------------------
    [root@localhost tmp]# i=1;while [ $i -le 5 ];do echo $i;let i++;done
    1
    2
    3
    4
    5
    [root@localhost tmp]# i=1;while(( $i <= 6 ));do echo $i;let i+=2;done
    1
    3
    5
    
    • 用脚本实现同步系统时间,需求:①每隔30秒同步一次系统时间,时间同步的服务器ip为10.1.1.1;②若同步失败,则发送邮件报警,每次失败都报警;③只有当同步成功100次才发一次邮件通知管理员。编写脚本文件(sync_time.sh):
    #!/bin/env bash
    # 该脚本用于同步系统时间
    NTP_SERVER=10.1.1.1
    count=0
    while true
      do
        rdate -s $NTP_SERVER &> /dev/null
        if [ $? -ne 0 ];then
            echo "system date failed!" | mail -s "check system date" root@localhost # 主题 收件人
        else
            let count++;
            if [ $count -eq 100 ];then
              echo "system date success..." | mail -s "check system date" root@localhost && count=0
            fi
        fi
        sleep 30
      done
    -------------------------------------------------------------------------------------------
    [root@localhost myshell]# vim sync_time.sh
    [root@localhost myshell]# chmod +x sync_time.sh 
    [root@localhost myshell]# ntpdate cn.ntp.org.cn(同步一下中国所在区域的标准时间)
    24 Oct 21:36:33 ntpdate[12874]: adjust time server 120.25.115.20 offset 0.000249 sec
    
    [root@localhost myshell]# ./sync_time.sh &(在后台执行该脚本)
    [1] 3560
    [root@localhost myshell]# jobs(查看后台正在执行的所有任务)
    [1]+  运行中               ./sync_time.sh &
    [root@localhost myshell]# kill -9 %1(杀死该后台进程)
    您在 /var/spool/mail/root 中有新邮件
    [root@localhost tmp]# true;echo $?
    0
    [root@localhost tmp]# false;echo $?
    1
    [root@localhost tmp]# :(true值)
    [root@localhost tmp]# echo $?
    0
    [root@localhost myshell]# cd /var/spool/mail(进入存放每个用户邮箱的目录)
    [root@localhost mail]# tail -f root(查看管理员的最新邮件)
    
    • until循环,条件为假就进入循环,条件为真就退出循环
    until expression
      do
        command
      done
    -------------------------------------------------------------------
    [root@localhost myshell]# i=1;until (($i>3));do echo $i;let i++;done
    1
    2
    3
    [root@localhost myshell]# i=1;while (($i<3));do echo $i;let i++;done
    1
    2
    
    • 批量创建用户,需求:①使用until语句批量创建10个用户,要求stu1~stu5用户的UID分别为1001~1005;②stu6~stu10用户的家目录分别在/rhome/stu6~/rhome/stu10。创建脚本文件(new_users.sh)
    #!/bin/env bash
    if [ -d /rhome ];then
      echo "/rhome目录已存在!"
    else
      mkdir /rhome
      echo "/rhome不存在,已完成创建!" 
    fi
    i=1
    until [ $i -gt 10 ]
      do
        if [ $i -le 5 ];then
          useradd -u $[1000+$i] stu$i  # -u:指定该用户的UID
        else
           useradd -d /rhome/stu$i stu$i
        fi
        echo 123 | passwd --stdin stu$i
        let i++
      done
    --------------------------------------------------------------------------
    [root@localhost myshell]# for i in {1..10};do userdel -r stu$i;done(删除用户以及对应的文件夹)
    [root@localhost myshell]# rm -rf /rhome/
    [root@localhost myshell]# bash -x new_users.sh 
    [root@localhost myshell]# bash -x new_users.sh 
    + '[' -d /rhome ']'
    + echo /rhome目录已存在!
    /rhome目录已存在!
    + i=1
    + '[' 1 -gt 10 ']'
    + '[' 1 -le 5 ']'
    + useradd -u 1001 stu1
    + passwd --stdin stu1
    更改用户 stu1 的密码 。
    ......
    [root@localhost myshell]# tail /etc/passwd
    stu1:x:1001:1005::/home/stu1:/bin/bash
    stu2:x:1002:1006::/home/stu2:/bin/bash
    stu3:x:1003:1007::/home/stu3:/bin/bash
    stu4:x:1004:1008::/home/stu4:/bin/bash
    stu5:x:1005:1009::/home/stu5:/bin/bash
    stu6:x:1006:1010::/rhome/stu6:/bin/bash
    stu7:x:1007:1011::/rhome/stu7:/bin/bash
    stu8:x:1008:1012::/rhome/stu8:/bin/bash
    stu9:x:1009:1013::/rhome/stu9:/bin/bash
    stu10:x:1010:1014::/rhome/stu10:/bin/bash
    
    • 生成随机数,只需调用系统变量:RANDOM,默认会产生0~32767的随机整数:
    [root@localhost myshell]# echo $RANDOM
    22633
    [root@localhost myshell]# set | grep RANDOM(查看系统上一次生成的随机数)
    RANDOM=22633
    
    • 获取某个文件的总行数:wc -l filename | cut -d' ' -f1
    • 获取某个文件中某一行的内容:head -行号 filename | tail -1
    • 批量创建用户并随机产生每个用户的密码,编写脚本文件(create_random_pwd_user.sh)
    #!/bin/env bash
    # create user and set passwd
    echo user0{1..5}:winner$[$RANDOM%9000+1000]#@~ | tr ' ' '\n' >> user_pass.file
    # 循环创建5个用户
    for((i=1;i<=5;i++))
      do
        user=`head -$i user_pass.file | tail -1 | cut -d: -f1`
        passwd=`head -$i user_pass.file | tail -1 | cut -d: -f2`
        useradd $user
        echo $passwd | passwd --stdin $user
      done 
    ------------------------------------------------------------------------------
    [root@localhost myshell]# echo user0{1..5}:winner$[$RANDOM%9000+1000]#@~
    user01:winner7494#@~ user02:winner7512#@~ user03:winner7176#@~ user04:winner9716#@~ user05:winner4187#@~
    [root@localhost myshell]# echo user0{1..5}:winner$[$RANDOM%9000+1000]#@~ | tr ' ' '\n'
    user01:winner9574#@~
    user02:winner2410#@~
    user03:winner1861#@~
    user04:winner4797#@~
    user05:winner7081#@~
    [root@localhost myshell]# vim create_random_pwd_user.sh
    [root@localhost myshell]# chmod +x create_random_pwd_user.sh 
    [root@localhost myshell]# ./create_random_pwd_user.sh 
    更改用户 user01 的密码 。
    passwd:所有的身份验证令牌已经成功更新。
    ......
    [root@localhost myshell]# cat user_pass.file 
    user01:winner7373#@~
    user02:winner2577#@~
    user03:winner7104#@~
    user04:winner5646#@~
    user05:winner3400#@~
    [root@localhost myshell]# echo -n hello(-n:表示输出不换行)
    hello[root@localhost myshell]# 
    

    相关文章

      网友评论

          本文标题:shell编程学习笔记(一)

          本文链接:https://www.haomeiwen.com/subject/frkumktx.html