1 bash 的登录主机欢迎信息: /etc/issue, /etc/motd
当你登录到系统之前,想了解这个系统的一些简单的硬件及软件版本信息等可以做如下设置:
/etc/motd
//编辑这个文件,会在用户登录之后看到一些欢迎信息
2 Bash 快捷键
Ctrl + a :移到命令行首
Ctrl + e :移到命令行尾
Ctrl + f :按字符前移(右向)
Ctrl + b :按字符后移(左向)
Ctrl + d :删除光标处的字符
Ctrl + h :删除光标前的字符
Ctrl + t :交换光标处和之前的字符
Ctrl + u :从光标处删除至命令行首
Ctrl + k :从光标处删除至命令行尾
Ctrl+c :终止目前在进行的命令
Ctrl+d :输入结束(EOF),例如写邮件结束的时候
BASH 的通配符
通配符如下表:
Linux 系统的语系会影响字符的排序和匹配
查看本系统的语系使用命令 locale
[dmtsai@study ~]$ LANG=C >==由于与编码有关,先设置语系一下
范例一:找出 /etc/ 下面以 cron 为开头的文件名
[dmtsai@study ~]$ ll -d /etc/cron* >==加上 -d 是为了仅显示目录而已
范例二:找出 /etc/ 下面文件名“刚好是五个字母”的文件名
[dmtsai@study ~]$ ll -d /etc/????? >==由于 ? 一定有一个,所以五个 "?" 就对了
范例三:找出 /etc/ 下面文件名含有数字的文件名
[dmtsai@study ~]$ ll -d /etc/*[0-9]* >==记得中括号左右两边均需 *
范例四:找出 /etc/ 下面,文件名开头非为小写字母的文件名:
[dmtsai@study ~]$ ll -d /etc/[^a-z]* >==注意中括号左边没有 *
范例五:将范例四找到的文件复制到 /tmp/upper 中
[dmtsai@study ~]$ mkdir /tmp/upper; cp -a /etc/[^a-z]* /tmp/upper
BASH 特殊符号
特殊符如下:
# 注释
\ 转移符号,可以将具有特殊意义的符号变为一般字符
; 连续下达命令的分隔符
& 将一个工作置于后台
`` 其内的命令可以先执行,建议用$( )取代
() 在中间为子shell的起始与结束
{} 在中间为命令区块的组合
一、什么是变量
简单说就是让一个特定的字符串代表不固定的内容
yy=123
yy是变量的名字,123是变量的值
echo $yy //查看变量的值
就是用一个简单的好记的字符串,来取代比较复杂或者容易变动的数据
二、变量的设定
设定规则:
=
的连结,且等号两边不能有空格。如下所示:myname=XiguaTian
2myname=xiguatian
"
或单引号'
将变量内容组合起来,但• 双引号内的特殊字符如 $
、 !
等,可以保有原本的特性,如下所示:
var="lang is $LANG"
#则
echo $var
#可得
lang is en_US
感叹号 !
不可以在shell命令行里直接在双引号里使用,但是可以在脚本中使用。
• 单引号内的特殊字符则仅为一般字符 (纯文本),如下所示:
var='lang is $LANG'
# 则
echo $var
#可得
lang is $LANG
\
将特殊符号(如 [Enter]
, $
, \
, 空格符
, 等)变成一般字符;$(命 令)
(推荐这种)。特别注意,那个反引号 是键盘上方数字键
1` 左边那个按键,而不是单引号!例如想要取得
核心版本的内容:
version=$(uname -r)
# 再
echo $version
#可得
4.9.125-linuxkit
"$变量名称"字符串
或 ${变量}字符串
(推荐)累加内容,如下所示:PATH="$PATH":/home/bin
PATH=${PATH}:/home/bin
export
来使变量变成环境变量:export PATH
通常大写字符为系统默认变量,自定义变量可以使用小写字符,方便判断 (纯粹个人习惯) ;
取消变量的方法为使用 unset :unset 变量名称
例如取消 myname
的设定:
unset myname
三、 环境变量
这种变量是会影响bash环境操作的,会在真正进入系统前由一个bash程序读入到系统中。通常都环境变量的名字以大写字符命名。
常见环境变量
PATH HOME MAIL SHELL PWD USER UID ID
等。
RANDOM 随机数
echo $RANDOM
PS1:(提示字符的设置)
\d :可显示出“星期 月 日”的日期格式,如:“Mon Feb 2”
\H :完整的主机名称。如www.sharkyun.com
\h :仅取主机名称在第一个小数点之前的名字,www
\t :显示时间,为 24 小时格式的“HH:MM:SS”
\T :显示时间,为 12 小时格式的“HH:MM:SS”
\A :显示时间,为 24 小时格式的“HH:MM”
@ :显示时间,为 12 小时格式的“am/pm”样式
\u :目前使用者的帐号名称,如“dmtsai”;
\v :BASH 的版本信息
\w :完整的工作目录名称,由根目录写起的目录名称。但主文件夹会以 ~ 取代;
\W :利用 basename 函数取得工作目录名称,所以仅会列出最后一个目录名。
# :下达的第几个指令。
$ :提示字符,如果是 root 时,提示字符为 # ,否则就是 $
读取环境变量的值
echo $HOME
列出shell环境下的所有环境变量及其内容
env
// env
是 environment (环境) 的简写,所有的环境变量(包含自定义的环境表里)
set
// 列出系统中所有的变量,包括自定义的变量
export
变量名 // 使自定义的变量 成为 环境变量,环境变量可以被向下继承
子进程 仅会继承父 shell 的环境变量, 不会继承父 shell 的自定义变量
name=shark
export name
bash
echo $name
并且
export
声明的环境变量只能被其子 shell 继承使用,不能被 父 shell 继承使用[root@kube-master py3]# bash # 打开一个子 shell [root@kube-master py3]# export a=hello # 在 子 shell 声明一个环境变量 [root@kube-master py3]# bash # 在子 shell 中再打开一个 子 shell [root@kube-master py3]# echo $a # 变量可以生效 hello [root@kube-master py3]# exit # 退出 子 shell 的 子 shell exit [root@kube-master py3]# exit # 退出 子 shell exit [root@kube-master py3]# echo $a # 在 当前 shell 中, 其子 shell 声明的环境变量是无效的 [root@kube-master py3]#
bash 的环境变量文件
longin shell
取得shell时需要完整的登入流程;特点是登入时需要用户帐号和密码
non-login shell
取得shell时不需要再次输入帐号和密码的情况下,所得到的 shell
longin shell 会读取以下两个文件:
或
~/.bash_login或
~/.profile` :属于个人的配置文件/etc/profile
会主动依序调用以下脚本文件:
/etc/inputrc
:定义快捷键/etc/profile.d/*sh
:定义bash操作接口颜色、语系、命令别名等etc/locale.conf
:定义系统的默认语系bash 在读完 /etc/profile
后,接下来会读取以下3个文件,且只会读去一个,会按照以下顺序优先读取
~/.bash_profile
//会调用 ~/.bashrc
,
也会有新的环境变量在下面的文件中被添加
~/.bash_login
~/.profile
最终,~/.bashrc
才是最后被读入到系统环境中的文件
让这些环境变量文件中的变量等设置及时在当前 shell
终端中生效,有下两种方式
source ~/.bashrc
或者
. ~/.bashrc
no-longin shell
当取得 no-longin shell
时,该 shell
仅会读取 ~/.bashrc
文件 而~/.bashrc
最后又会调用 /etc/bashrc
/etc/bashrc
的作用:
• 依据不同的UID定义出 umask
值
• 依据不同的UID定义出提示符(就是PS1变量)
• 呼叫 /etc/profile.d/*.sh
的设定
其他的相关配置文件
• /etc/man.config
这个文件最重要的就是定义了MANPATH
这个变量,它定义了man page 的路径;在以tarball的方式安装软件时有用
• ~/.bash_history
历史命令记录文件;记录的数量与HISTFILESIZE变量有关。在/etc/profile
里
• ~/.bash_logout
记录了当我注销bash后,系统再帮我做完什么动作后才离开的。
四、预定义变量
预定义的特殊变量有着特殊的含义,用户不可以更改,所有的预定义变量都由
$
符号和另外一个符号组成,常用的预定义特殊变量如下:
$! 上一个后台命令对应的进程号
$? 上一个命令的退出状态,为十进制数字,如果返回为0,则代表执行成功,则否为不成功。
$$ 当前的进程号PID
以上变量请配合 echo
使用,例如:
echo $!
echo $$
echo $?
五、从键盘的输入给变量赋值: read
read
[root@www ~]# read [-pt] variable
选项不参数:
-p :后面可以接提示字符!
-t :后面可以接等待的『秒数!』,比如 -t 5 提示用户输入信息时间是 5 秒钟
超过 5 秒钟,程序就继续向下运行
例如:
#vi read.sh
read -p “请输入你的姓名” name
echo "你的姓名是: $name"
六、变量内容的删除、取代与替换
准备工作,防止误操作导致变量失效。
先让小写的 path 自订变量设置的与 PATH 内容相同
[dmtsai@study ~]$ path=${PATH}
[dmtsai@study ~]$ echo ${path}
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/shark/bin
• 删除
echo ${path%:*}
%
从字符串的后面开始进行匹配删除,但匹配到第一个时就结束匹配。
:*
就是需要进行匹配的字符串 , :
是普通字符串 *
是通配符,代表任何数量的任意字符串。
所以下面的红色字体的字符串将会匹配后进行删除
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/shark/bin
接着上例的变量进行操作
echo ${path#*:}
#
是从前面开始匹配删除,匹配到道理和上面的 %
同样的道理
${path#*:}
就是 从变量值的最前面开始匹配,直到找到第一个符匹配合格的字符就结束
所以下面的红色字体会被匹配后删除
/usr/local/bin:
/usr/bin:/usr/local/sbin:/usr/sbin:/home/shark/bin
以上不需要都掌握,需要记住下面这个
var=${str:-expr}
str
和 var
可以是相同的字符串。比如 inpu_date=${inpu_date:-expr}
expr
可以是任意合法的表达式,比如一个字符串,一个子 shell 的命令 $(date +%F)
示例,生产中会遇到经常查数据库的操作,一般查询最多的就是查当天的数据,但也有别的日期的数据。现在就希望当用户没有输入时,就给一个默认值,这里给的是当天的日期,假如输入的具体的日期,就按照用户输入的日期查询。
假设今天是 20200202
read -p "输入日期>:" input_date
input_date=${input_date:-20200202}
echo "当前的日期是:${input_date}"
执行示例效果
[root@sharkyun script]# sh read-defaut-value.sh
输入日期>:
当前的日期是:20200202
[root@sharkyun script]# sh read-defaut-value.sh
输入日期>:20200201
当前的日期是:20200201
[root@sharkyun script]#
read -p "输入日期>:" input_date
input_date=${input_date:-$(date +%F)}
echo "当前的日期是:${input_date}"
七、时间运算
// 计算 3 小时之后是几点几分
date +%T -d '3 hours'
// 任意日期的前 N 天,后 N 天的具体日期
date +%F -d "20190910 1 day"
date +%F -d "20190910 -1 day"
// 计算两个日期相差天数, 比如计算生日距离现在还有多少天
d1=$(date +%s -d 20180728)
d2=$(date +%s -d 20180726)
echo $(( (d1-d2) / 86400 ))
# 输出
2
SheLL-3shell脚本
一、什么是 shell script
shell script 是利用 shell 的功能所写的一个“程序 (program)”,这个程序是使用纯文本文件,将一些 shell 的语法与指令(含外部指令)写在里面, 搭配正则表达式、管道命令与数据流重导向等功能,以达到我们所想要的处理目的。
简单的说,也就是可以使用一个普通的文本,写上多条 shell 命令,一起执行这些命令。
但是,在这个文件中可以添加一些逻辑判断什么的。
二、shell script 规范
三、简单脚本练习
vim show-name.sh
#!/bin/bash
# Program:
# User inputs 2 integer numbers; program will cross these two numbers.
# History:
# 2015/07/16 VBird First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo -e "You SHOULD input 2 numbers, I will multiplying them! \n"
read -p "first number: " firstnu
read -p "second number: " secnu
total=$(( ${firstnu} * ${secnu} ))
echo -e "\nThe result of ${firstnu} x ${secnu} is ==> ${total}"
四、执行脚本方式 (source script, sh script, ./script)
source script
父进程中执行脚本中代码
sh script
子进程中执行脚本中的代码,相当于打开了一个子 shell ,一个全新的环境。
五、script 的默认变量
位置变量
位置变量也属于预定义的变量
位置变量真对于脚本来说
位置变量是根据命令出现在命令行上的位置来确定的变量,在shell调用过程中,会按参数所在的位置进行调用。
命令(shell脚本名) 参数1 参数2 参数3
$0 $1 $2 $3 ...$9
例如:
wc /etc/passwd /etc/group
例如:
编辑一个任意文件(这里是 position-arg.sh),写入如下内容
#!/bin/bash
echo "脚本名称是 $0"
echo "第一个参数是 $1"
echo "第二个参数是 $2"
echo "第三个参数是 $3"
此脚本地执行时,使用sh执行即可
sh position-arg.sh a b c d e
假如脚本中使用了一个位置变量,但是执行脚本的时候,传入了多个参数,程序并不会报错。
但是执行的时候只传入了一个参数,同样不会报错,并且 $2
不会被赋值
特殊变量(重要)
$#
:代表后接的参数“个数”
$@
:代表"$1" "$2" "$3" "$4"
之意,每个变量是独立的(用双引号括起来);
$*
:代表"$1c$2c$3c$4"
,其中c
为分隔字符,这个分为空白键, 所以本例中代表"$1 $2 $3 $4"
之意。
#!/bin/bash
echo "脚本名称$0"
echo "第一个参数是 $1"
echo "第一个参数是 $2"
echo "第一个参数是 $3"
echo '所有的参数分别是 $@' "$@"
echo '所有的参数分别是 $*' "$*"
echo "一共有参数 $# 个"
六、set
set -u
当脚本中遇到未定义的变量时,默认是忽略。
有时候这并不是开发者想要的。
假如有未定义的变量,应该报错,并且终止脚本继续运行。
set -u
就可以办到
示例
#!/usr/bin/env bash
set -u
echo $a
echo shark
执行报如下错误
$ bash set.sh
set.sh: line 13: a: unbound variable
分析
注意
set -u
a=`ls |grep 'asfdasdf'`
echo "|$a|"
# -u 只针对未定义的变量有效
# 这种情况,变量的值会是一个空字符串
# 所以脚本会继续执行
set -e
set -e
的作用就是:
在脚本执行中,有任何命令的返回值是非0
的情况,则正在运行的脚本则会退出。
设置 -e 之前
[root@kube-master set]# cat set-e-before.sh
#!/bin/sh
foo
echo "继续执行"
执行结果
[root@kube-master set]# sh set-e-before.sh
set-e-before.sh: line 3: foo: command not found
继续执行
设置 -e 之后
解决办法是在脚本中设置 set -e
[root@kube-master set]# cat set-e-after.sh
#!/bin/sh
set -e
foo
echo "继续执行"
执行结果
[root@kube-master set]# sh set-e-after.sh
set-e-after.sh: line 5: foo: command not found
但是,对于一组含有管道的命令无效,比如:
#!/usr/bin/env bash
set -e
foo | echo "shark" # 注意这里有管道符
echo "程序会继续运行"
执行结果
[root@kube-master set]# sh set-e.sh
shark
set-e.sh: line 2: foo: command not found # 这是报错信息
程序会继续运行
foo
不是 shell 中的命令,执行会报错,但是其后面有个管道,最终管道后的echo
命令执行成功了,这种情况下 脚本会继续执行。
解决办法是使用下面的 set -o pipefail
set -o pipefail
需要和
set -e
配合使用。
如果设置,如果管道中的所有命令都成功退出,整条命令的返回值才是0
。否则返回非0
。
默认情况下禁用此选项
示例脚本内容
# 设置 set -o pipefail 后,此时脚本就会终止运行
set -e
set -o pipefail
foo |echo ''
echo shark
执行效果
[root@kube-master set]# sh set-e-pipefail.sh
set-e-pipefail.sh: line 4: foo: command not found
test
示例:
$ touch a.txt
$ test -e a.txt;echo $?
0 # 测试成功,命令返回值为 0
$ test -e s.txt;echo $?
1 # 测试失败,命令返回值为 非 0
$ test -f a.txt;echo $?
0
$ test -d a.txt;echo $?
1
示例:
$ test -r a.txt; echo $?
0
$ test -x a.txt; echo $?
1
$ test -w a.txt; echo $?
0
$ test -u a.txt; echo $? # 判断 a.txt 文件是否具有 SUID 属性
1
$ cat a.txt # 查看 a.txt ,此文件内容为空
$ test -s a.txt; echo $? # 判断 a.txt 文件中有内容
1 # 命令返回值为 1 ,说明文件中没有内容
$ echo "123" > a.txt
$ test -s a.txt; echo $?
0
示例:
$ touch b.txt
$ ls -l a.txt
-rw-r--r-- 1 shark staff 4 12 17 22:59 a.txt
$ ls -l b.txt
-rw-r--r-- 1 shark staff 0 12 17 23:05 b.txt
$ test a.txt -nt b.txt; echo $? # 判断 a.txt 是否比 b.txt 新
1 # 返回 1, 表示判断表达式不成立
$ test b.txt -nt a.txt; echo $?
0
$ test a.txt -ef a-hard.txt; echo $?
0
示例:
$ test 10 -eq 20; echo $?
1
$ n1=10
$ n2=20
$ test $n1 -eq $n2; echo $?
1
$ test $n1 -lt $n2; echo $?
0
$ test $n1 -ne $n2; echo $?
0
注意:
这里的string
可以是实际的字符串,也可以是一个变量
这里说的字符串是否为0
的意思是字符串的长度是否为 0
示例
$ test -z ''; echo $? # 空字符串
0
$ test -z ' '; echo $? # 含有一个空格的字符串
1
$ test ! -z ' '; echo $? # 判断含有一个空格的字符串,其长度为非 0 的字符串, 空格也算是字符串。
0
$ test -z ${name}; echo $? # 变量未定义,shell 中认为其长度为 0
0
$ name=shark
$ test -z ${name}; echo $?
1
$ age='' # 定义变量,并且赋值为空字符串
$ test -z ${age}; echo $? # shell 中,被赋值为空字符串的变量长度也为 0
0
注意:
再次强调一下, 在 shell 中,以下两种情况,变量的长度均视为
0
- 1.变量未定义
- 变量定义了,但赋值为空字符串,比如
a=''
,b=""
[root@kube-master script]# name=shark
[root@kube-master script]# age=shark
[root@kube-master script]# test $name == $age ;echo $?
0
[root@kube-master script]# test $name != $age ;echo $?
1
[root@kube-master script]#
示例
判断符号 []
[ -z "${HOME}" ] ; echo $?
必须要注意中括号的两端需要有空白字符来分隔! 假设我空白键使用“□”符号来表示,那么,在这些地方你都需要有空白键:
错误示范
# 定义变量
name="shark ops"
# 开始测试值是否相等
[ ${name} == "xiguatian" ]
会报如下错误信息:
bash: [: too many arguments
之前的错误写法 [ ${name} == "xiguatian" ]
的,会变成这样 [ shark ops == "xiguatian" ]
正确写法应该写成这样 [ "${name}" == "xiguatian" ]
的, 会变成这样 [ "shark ops" == "xiguatian" ]
有没有引号很重要
[root@shark ~]# n=""
[root@shark ~]# [ $n ] && echo "为真" || echo "为假"
为假
[root@shark ~]# n=" "
[root@shark ~]# [ $n ] && echo "为真" || echo "为假"
为假
[root@shark ~]# [ "$n" ] && echo "为真" || echo "为假"
为真
也可以使用 !
进行取反
[root@shark ~]# [ ! "$n" ] && echo "为假" || echo "为真"
为真
[root@shark ~]# n=
[root@shark ~]# [ ! "$n" ] && echo "为假" || echo "为真"
为假
[root@shark ~]#
中括号中使用 多条件判断
[root@a0652462c802 ~]# [ "${name}" == "xi" -o 10 -eq 10 ];echo $?
0
[root@a0652462c802 ~]# [ "${name}" == "xi" ] || [ 10 -eq 10 ] ;echo $?
0
[root@a0652462c802 ~]# [ "${name}" == "xi" ] && [ 10 -eq 10 ] ;echo $?
1
[root@a0652462c802 ~]# [ "${name}" == "xi" -a 10 -eq 10 ];echo $?
1
[root@a0652462c802 ~]#
判断一个变量的值 的长度是否为零
[root@a0652462c802 ~]# name=""
[root@a0652462c802 ~]# [ "$name" ];echo $? # 变量值的长度不为0
1
[root@a0652462c802 ~]# [ ! "$name" ];echo $? # 变量值的长度为0
1
[root@a0652462c802 ~]# name="dd"
[root@a0652462c802 ~]# [ "$name" ];echo $? # 变量值的长度不为0
0
[root@a0652462c802 ~]# name=" "
[root@a0652462c802 ~]# [ "$name" ];echo $? # 变量值的长度不为0
0
[root@a0652462c802 ~]# [ ! -z "$name" ];echo $? # 变量值的长度不为0
0
[root@a0652462c802 ~]#
[root@a0652462c802 ~]# echo ${#name} # 计算变量值的长度
5
[root@a0652462c802 ~]#
一、if 判断
if … then … fi
单层
如果 是真的 那么 …
if [ 条件判断式 ];then
当条件判断式成立时,可以进行的命令。
fi # 结束这个判断语句结构
示例:
提示输入一个整数,程序判断输入的值是否大于 18
风格 1
#!/usr/bin/env sh
read -p "请输入一个整数:" num
if [ "$num" -gt 18 ];then
echo "你输入的数值大于 18"
fi
echo "hello..."
风格2
#!/usr/bin/env sh
read -p "请输入一个整数:" num
if [ "$num" -gt 18 ]
then
echo "你输入的数值大于 18"
fi
echo "hello..."
if … then … else…fi
多重、复杂条件判断式
如果 是真的 那么 … 否则 …
一颗红心,两手准备
if [ 条件判断式 ]; then
当条件判断式成立时,可以进行的指令工作内容;
else
当条件判断式不成立时,可以进行的指令工作内容;
fi
示例:
判断用户输出的字符串
输入y
就输出文本内容 “script is running…”
否则输出文本内容 “STOP”
if … elif … elif … else … fi
多个条件判断 (if … elif … elif … else) 分多种不同情况执行
if [ 条件判断式一 ]; then
当条件判断式一成立时,可以进行的指令工作内容;
elif [ 条件判断式二 ]; then
当条件判断式二成立时,可以进行的指令工作内容;
else
当条件判断式一与二均不成立时,可以进行的指令工作内容;
fi
注意
elif
也是个判断式,因此出现elif
后面都要接then
来处理!但是else
已经是最后的没有成立的结果了, 所以else
后面并没有then
示例
通过判断输入的整数,判断是什么常用服务
#!/usr/bin/env sh
read -p "请输入一个常用的服务默认端口号:" port
if [ "$port" -eq 80 ];then
echo "HTTP 服务"
elif [ "$port" -eq 3306 ];then
echo "Mysql 服务"
elif [ "$port" -eq 21 ] || [ "$port" -eq 20 ];then
echo "FTP 服务"
elif [ "$port" -eq 22 ];then
echo "SSHD 服务"
elif [ "$port" -eq 23 ];then
echo "Telnet 服务"
else
echo "未知服务"
fi
在判断中支持正则
使用双中括号 [[ ]]
=~
比如: [[ "$val" =~ [a-z]+ ]]
=~
表示 匹配
[a-z]+
是正表达式,不需要用任何引号,用了引号就会被识别为普通字符串
!
比如: [[ ! "$val" =~ [0-9]+ ]]
示例
#!/usr/bin/env sh
name=shark
if [[ "$name" =~ [a-z]+ ]];then
echo "ok"
fi
if [[ ! "$name" =~ [0-9]+ ]];then
echo "good"
fi
嵌套
#!/usr/bin/env sh
read -p "请输入一个常用的服务默认端口号:" port
if [[ "$port" =~ [0-9]+ ]];then
if [ "$port" -eq 80 ];then
echo "HTTP 服务"
elif [ "$port" -eq 3306 ];then
echo "Mysql 服务"
elif [ "$port" -eq 21 ] || [ "$port" -eq 20 ];then
echo "FTP 服务"
elif [ "$port" -eq 22 ];then
echo "SSHD 服务"
elif [ "$port" -eq 23 ];then
echo "Telnet 服务"
else
echo "未知服务"
fi
else
echo "nmber"
fi
二、 利用 case … esac 判断
基本语法
case $变量名称 in <==关键字为 case ,还有变量前有钱字号
"第一个变量的值") <==每个变量内容建议用双引号括起来,关键字则为小括号 )
程序段
;; <==每个类别结尾使用两个连续的分号来处理!
"第二个变量的值")
程序段
;;
*) <==最后一个变量内容都会用 * 来代表所有其他值
不包含第一个变量内容与第二个变量内容的其他程序运行段
exit 1
;;
esac <==反过来写,结束当前语句结构!
运维工具箱
case
========================================================
模式还可以是多个
#!/bin/bash
cat << EOF
m|M) show memory usages;
d|D) show disk usages;
q|Q) quit
EOF
read -p "Your choice" choice
case $choice in
m|M)
free -m
;;
d|D)
df -h
;;
q|Q)
exit
;;
*)
echo "Invalid input"
;;
esac
三、while … do … done (不确定的循环)
while [ condition ] ==>中括号内的状态就是判断式
do ==> do 是循环的开始!
命令或者代码
命令或者其他逻辑语句
done ==> done 是循环的结束
计数
每次循环体执行完,while 都会检查条件是否为真,为真继续循环,否则终止循环。
n=0
while [ "$n" -lt 5 ]
do
let n++
echo "$n"
done
读文件
while read line
do
echo $line
done < /etc/passwd
每次循环, 都会把
/etc/passwd
中的每一个行内容 赋值给变量line
read
[root@sharkyun ~]# read id name age
1 shark 18
[root@sharkyun ~]# echo $age
18
[root@sharkyun ~]# echo $id $name
1 shark
[root@sharkyun ~]# old_ifs=$IFS; IFS=','
[root@sharkyun ~]# read id name age
2, xiguatian, 20
[root@sharkyun ~]# echo "$id| $name| $age"
2| xiguatian| 20
[root@sharkyun ~]# export IFS=$old_ifs
[root@sharkyun ~]# read id name age
2, xiguatian, 20
[root@sharkyun ~]# echo "$id| $name| $age"
2,| xiguatian,| 20
[root@sharkyun ~]# read id name age
2,xiguatian,20
[root@sharkyun ~]# echo "$id| $name| $age"
2,xiguatian,20| |
while read
$ cat db.sql
1 shark 18
2 xiguatian 20
$ cat while-read-m.sh
while read id name age
do
echo "$id | $name | $age"
done < db.sql
$ sh while-read-m.sh
1 | shark | 18
2 | xiguatian | 20
五、 for do done (固定的循环)
var 是变量名
con1 con2 con3
是常量,就是具体的数据
也可以是一个已经被赋值的变量, 写法${v1} ${v2}
以上面的例子来说,这个
$var
的变量内容在循环工作时会动态的改变:
- 第一次循环时,
$var
的内容为 con1 ;- 第二次循环时,
$var
的内容为 con2 ;- 第三次循环时,
$var
的内容为 con3 ;- …
for 循环中 变量的取值方式
a. 从一组字符串中取值
for var in one two three four five
do
echo "****************************"
echo '$var is ' $var
done
b. 从位置变量中取值
for var
do
echo '-----------------------------'
echo '$var is ' $var
done
c. 从累计变化的格式中取值
#!/bin/bash
for ((var=1;var<=10;var++))
do
echo "------------------------"
echo '$var is ' $var
done
d. 从命令结果中取值
#!/bin/bash
for var in $(cat ip.txt)
do
echo " ------------------------"
echo '$var is ' $var
done
IFS
修改 for
循环中的分界符,默认是 空格
示例 a.txt
文件的内容
hello world name age
hello world name age
# 先保存原来的值
old_ifs=$IFS
# 设置分界符为 回车
IFS=$'\n'
for line in $(cat a.txt)
do
echo $line
done
# 把变量的值回复成原来的状态
export IFS=$old_ifs
嵌套循环
for n in {1..3}
do
for i in {a..e}
do
echo "外层循环的值$n--内层循环的值$i"
done
echo "----------------"
done
执行结果
补充:数值运算
➜ ~ n=1
➜ ~ let n++
➜ ~ echo $n
2
➜ ~ (( n++ ))
➜ ~ echo $n
3
➜ ~ a=2
➜ ~ b=3
➜ ~ let f=a+b
➜ ~ echo $n
3
➜ ~ echo $f
5
➜ ~ let f = a + b ==> 错误
zsh: bad math expression: operand expected at `='
➜ ~ let "f = a + b"
➜ ~ echo $f
5
六、break 和 continue
break
就是退出循环,循环不再继续了。假如是嵌套的循环,就是退出当前层级的循环。
continue
当执行 contniue 的时候,循环体内,continue 之后的代码不再执行,继续进行下一次循环
什么是正则表达式
简单的说,正则表达式就是处理字串的方法,他是以行为单位来进行字串的处理行为, 正则表达式通过一些特殊符号的辅助,可以让使用者轻易的达到“搜寻/删除/取代”某特定字串的处理程序!
正则表达式基本上是一种“表达式”, 只要工具程序支持这种表达式,那么该工具程序就可以用来作为正则表达式的字串处理之用。 例如 vi, grep, awk ,sed 等等工具,因为她们有支持正则表达式, 所以,这些工具就可以使用正则表达式的特殊字符来进行字串的处理。但例如 cp, ls 等指令并未支持正则表达式, 所以就只能使用 Bash 自己本身的通配符而已。
是 Linux 基础当中的基础,如果学成了之后,一定是“大大的有帮助”的!这就好像是金庸小说里面的学武难关:任督二脉! 打通任督二脉之后,武功立刻成倍成长!
关于语系
在英文大小写的编码顺序中,zh_TW.big5 及 C 这两种语系的输出结果分别如下:
LANG=C 时:0 1 2 3 4 ... A B C D ... Z a b c d ...z
LANG=zh_TW 时:0 1 2 3 4 ... a A b B c C d D ... z Z
尤其要记住:
[:alnum:] 代表所有的大小写英文字符和数字 0-9 A—Z a-z
[:alpha:] 代表任意英文大小写字符 A-Z a-z
[:lower:] 代表小写字符 a-z
[:upper:] 代表大写字符 A-Z
[:digit:] 代表数字 0-9
练习示例文件
数据来源于鸟哥私房菜
"Open Source" is a good mechanism to develop programs.
apple is my favorite food.
Football game is not use feet only.
this dress doesn't fit me.
However, this dress is about $ 3183 dollars.
GNU is free air not free beer.
Her hair is very beauty.
I can't finish the test.
Oh! The soup taste good.
motorcycle is cheap than car.
This window is clear.
the symbol '*' is represented as start.
Oh! My god!
The gd software is a library for drafting programs.
You are the best is mean you are the no. 1.
The world is the same with "glad".
I like dog.
google is the best tools for search keyword.
goooooogle yes!
go! go! Let's go.
# I am VBird
匹配示例
输出:
输出:
输出:
输出:
匹配 2 个连续的 a
字符
匹配 2个以上连续的 a
字符
匹配 3 个以下连续的字符 a
进阶 grep
-A n 把匹配成功行之后的n行也同时列出。 A 就是 after 的首字母
就是 之后 的意思
-B n 把匹配成功行之前的n行也同时列出。B 就是 before 的首字母
就是 之前 的意思
范例:
显示
/etc/passwd
含有
[root@e9818e4ea8b3 ~]# grep mail -B 2 -A3 /etc/passwd
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
显示 目标行(这里是含有 mail 字符)的
前
后
各 3 行
grep mail -C 3 /etc/passwd
只显示匹配到的字符
grep -o 'nologin' /etc/passwd
加上统计数量,匹配到的行数,而不是匹配到的个数
[root@shark ~]# grep root /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
[root@shark ~]# grep root /etc/passwd -c
2
[root@shark ~]#
只要文件名
[root@shark ~]# grep -l 'nologin' /etc/* 2>> /dev/null
/etc/passwd
/etc/passwd-
[root@shark ~]#
递归查找,就是在一个目录下查找
[root@shark ~]# grep -l -r 'nologin' /etc/* 2>> /dev/null
/etc/pam.d/sshd
/etc/pam.d/login
/etc/pam.d/remote
/etc/passwd
/etc/passwd-
/etc/selinux/targeted/active/policy.linked
/etc/selinux/targeted/active/file_contexts
/etc/selinux/targeted/active/policy.kern
/etc/selinux/targeted/contexts/files/file_contexts.pre
/etc/selinux/targeted/contexts/files/file_contexts
/etc/selinux/targeted/contexts/files/file_contexts.bin
/etc/selinux/targeted/tmp/policy.linked
/etc/selinux/targeted/tmp/file_contexts
/etc/selinux/targeted/tmp/policy.kern
/etc/selinux/targeted/policy/policy.31
[root@shark ~]#
搜索 oo 但其前面不要有g
grep -n '[^g]oo' regular_express.txt
注意:当搜索的行内含有
要符合搜索条件
时后,此行就会忽略
明确不要的条件(就是明确不要的条件会无效),比如以上的例子就可能会搜索到下面的内容
3:tool is a good tool
8:goooooogle
显示开头不是 英文字符的行
grep -n ‘^[^[:alpha]]’ regular_repress.txt
符号 ^ 在 [] 内时是取反的意思,在 [] 之外是行首的意思
显示行首不是#和;的行
grep '^[^#;]' regular_repress.txt
找到以 . 结尾的行
grep -n '\.$' regular_repress.txt
需要用
\
进行转意
查找 开头是 g 和结尾也是 g ,中间的字符可有可无
grep -n 'g.*g' regular_repress.txt
.
代表一个任意字符
*
代表重复零到多个在 其前面的一个字符
.*
代表零个或多个任意字符
查找以a为开头的任意文件名
方法一:
通配符
ls -l a*
方法二:
ls |grep -n '^a.*'
列出 /etc 目录下的链接文件
ls -l /etc |grep '^l'
再统计一下多少个
ls -l /etc |grep -c '^l'
扩展正则
关于 分组 小括号 ()
的深入理解
写出匹配日期格式 YYYY-MM-DD
的正则表达式
[root@sharkyun ~]# echo "2019-12-30" |grep -E '[1-9][0-9]{3}-((0[1-9])|(1[0-2]))-((0[1-9])|([12][0-9])|(3[01]))'
2019-12-30
[root@sharkyun ~]# echo "1919-12-30" |grep -E '[1-9][0-9]{3}-((0[1-9])|(1[0-2]))-((0[1-9])|([12][0-9])|(3[01]))'
1919-12-30
支持扩展正则的工具
grep -E
egrep
sed
awk
正则高级部分: 贪婪|非贪婪(扩展)
贪婪 就是尽可能的多匹配
非贪婪 就是尽可能的少匹配,只需要在一些表示量词(就是次数)的后面加上
?
, 比如:.*?
+?
grep 实现非贪婪
grep 或者 egrep 默认都是贪婪模式,不支持非贪婪模式。
要想实现非贪婪需要使用-P
参数,这会使用Perl
语言环境的正则
Perl 语言中:
数组 (array) 变量类型
定义和取值
数组中的值: wukong bajie shaseng
值的索引号: 0 1 2
数组的索引只能是
整数
# 定义一个数组
var=(wukong bajie shaseng)
echo ${var[2]} //显示数组中索引号为 2 的值,索引号从 0 开始
输出 shaseng
echo ${var[*]} //显示数组中所有的值
输出 wukong bajie shaseng
定义数组,并且其值从命令的结果中获取
# 把文件中的每一行作为数组中的一个值
line=(`cat /etc/passwd`)
切片
普通字符串切片
# 获取到 CPU 型号信息,作为基础数据进行处理
[root@shark ~]# cpu=$(grep 'model name' /proc/cpuinfo |uniq|cut -d: -f2)
[root@shark ~]# echo "|$cpu|"
| Intel(R) Xeon(R) Platinum 8163 CPU @ 2.50GHz|
[root@shark ~]# echo ${cpu:1} # 从索引号 1 开始向后取值,取到最后
Intel(R) Xeon(R) Platinum 8163 CPU @ 2.50GHz
[root@shark ~]# cpu=${cpu:1}
[root@shark ~]# echo "|$cpu|"
|Intel(R) Xeon(R) Platinum 8163 CPU @ 2.50GHz|
数组切片
[root@shark ~]# nums=(a b c d e)
[root@shark ~]# echo ${nums[*]:2} # 从索引号 2 开始向后取值,取到最后
c d e
[root@shark ~]# echo ${nums[*]:2:2} # # 从索引号 2 开始向后取值,取 2 个
c d
[root@shark ~]#
循环数组
[root@shark ~]# nums=(a b c d e)
[root@shark ~]# for i in ${nums[*]}
> do
> echo $i
> done
a
b
c
d
e
declare 声明关联数组
数组的索引可以是
普通字符串
声明关联数组使用 A
选项
declare -A 数组名称
# 声明关联数组,数组名称为 info
[shark@sharkyun ~]$ declare -A info
示例:
[shark@sharkyun ~]$ info["name"]="shark"
[shark@sharkyun ~]$ info["age"]=18
[shark@sharkyun ~]$ echo ${info["name"]} # 显示索引对应的值
shark
[shark@sharkyun ~]$ echo ${info["age"]}
18
每个值之间使用 空格 隔开
var=([key1]="value1" [key2]="value2")
示例:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iQAbTXd6-1600593675664)(assets/image-20200910101909271.png)]
删除
[root@kube-master arry]# unset info[name]
[root@kube-master arry]# echo ${!info[*]}
age
循环关联数组
[root@shark ~]# echo ${info[@]}
shark 18
[root@shark ~]# for i in ${info[@]}
> do
> echo $i
> done
shark
18
[root@shark ~]#
一、awk简介
awk 是一种编程语言,用于在linux/unix下对文本和数据进行处理。
数据可以来自标准输入、一个或多个文件,或其它命令的输出。
支持用户自定义函数和动态正则表达式等先进功能,是linux/unix
下的一个强大编程工具。
在命令行中使用,但更多是作为脚本来使用。
awk的处理文本和数据的方式是这样的,它逐行扫描文件,从第一行到最后一行,寻找匹配的特定模式的行,并在这些行上进行你想要的操作。如果没有指定处理动作,则把匹配的行显示到标准输出(屏幕),如果没有指定模式,则所有被操作所指定的行都被处理。
awk分别代表其作者姓氏的第一个字母。因为它的作者是三个人,分别是Alfred Aho、Brian Kernighan、Peter Weinberger。
gawk是awk的GNU版本,它提供了Bell实验室和GNU的一些扩展。
二、awk的两种形式语法格式
awk [options] 'commands’ file1 file2
awk [options] -f awk-script-file filenames
options:
-F
对于每次处理的内容,可以指定一个子定义的分隔符,默认的分隔符是空白字符(空格或 tab 键 )
command:
BEGIN{} {} END{}
处理所有内容之前的动作 处理内容中的动作 处理所有内容之后的动作
示例
awk 'BEGIN{print "----开始处理了---"} {print "ok"} END{print "----都处理完毕---"}' /etc/hosts
----开始处理了---
ok
ok
ok
----都处理完毕---
BEGIN{}
通常用于定义一些变量,例如 BEGIN{FS=":";OFS="---"}
三、awk工作原理
[root@5e4b448b73e5 ~]# awk -F: '{print $1,$3}' /etc/passwd
root 0
bin 1
daemon 2
adm 3
...略...
(1)awk,会处理文件的每一个行,每次处理时,使用一行作为输入,并将这一行赋给内部变量$0,每一行也可称为一个记录,以换行符结束
(2)然后,行被***(默认为空格或制表符)分解成字段(或称为域),每个字段存储在已编号的变量中,从$1开始,
最多达100个字段
(3)awk如何知道用空白字符来分隔字段的呢? 因为有一个内部变量FS来确定字段分隔符。初始时,FS赋为空白字符
(4)awk打印字段时,将以内置的方法使用 print
函数打印,awk 在打印出的字段间加上空格。这个空格是内部的一个变量 OFS
输出字段的分隔符, 逗号 ,
会和 OFS
进行映射,通过 OFS
可以控制这个输出分隔符的值。
(5)awk输出之后,将从文件中获取另一行,并将其存储在$0中,覆盖原来的内容,然后将新的字符串分隔成字段并进行处理。该过程将持续到所有行处理完毕
四、记录与字段相关内部变量:
查看帮助:
man awk
$0
: awk变量$0
保存当前正在处理的行内容
NR
: 当前正在处理的行是 awk 总共处理的行号。
FNR
: 当前正在处理的行在其文件中的行号。
NF
:每行被处理时的总字段数
$NF
: 当前处理行的分隔后的最后一个字段
的值
FS
: 输入行时的字段分隔符,默认空白字符(空格 tab键)
OFS
: 输出字段分隔符,默认是一个 空格awk 'BEGIN{FS=":"; OFS="+++"} /^root/{print $1,$2,$3,$4}' /etc/passwd
ORS
输出记录分隔符, 默认是换行符.
示例
将文件每一行合并为一行
ORS默认输出一条记录应该回车,但是这里是加了一个空格
awk 'BEGIN{ORS=" "} {print $0}' /etc/passwd
输出:
root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin adm:x:3:4:adm:/var/adm:/sbin/nologin lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin sync:x:5:0:sync:/sbin:/bin/sync shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown halt:x:7:0:halt:/sbin:/sbin/halt mail:x:8:12:mail:/var/spool/mail:/sbin/nologin operator:x:11:0:operator:/root:/sbin/nologin games:x:12:100:games:/usr/games:/sbin/nologin ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin nobody:x:99:99:Nobody:/:/sbin/nologin systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin dbus:x:81:81:System message bus:/:/sbin/nologin tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin [root@5e4b448b73e5 ~]#
五、格式化输出:
printf 函数
awk -F: '{printf "%-15s %-10s %-15s\n", $1,$2,$3}' /etc/passwd
awk -F: '{printf "|%-15s| %-10s| %-15s|\n", $1,$2,$3}' /etc/passwd
%s
字符类型%d
十进制整数%f
浮点类型 ,保留小数点后 2 位printf "%.2f\n" 10
%-15s
占15字符 -
表示左对齐,默认是右对齐printf
默认不会在行尾自动换行,加 \n
六、awk模式和动作
任何 awk
语句都由 模式 和 动作 组成。
模式部分
决定动作语句何时被触发。
如果省略模式部分,命令中的动作讲作用于每一行。
模式可以是
1 正则表达式:
就是当前处理的行有没有包含 指定的模式(书写的正则表达式)
/正则/
正则需要写在双斜线内
!
用于取反,就是找到不匹配正则模式的行
AWK默认的动作就是打印出整行,所以当打印整行内容的时候,打印的动作命令可以省略。
awk '/^root/' /etc/passwd
awk '!/^root/' /tec/ passwd
可以使用的匹配操作符(
~
和!~
)
字段 ~ /正则/
awk -F: '$3 ~ /^1/' /etc/passwd
awk -F: '$NF !~ /bash$/' /etc/passwd
bin
的或者开头是 root
的行awk -F: '/^(bin|root)/' /etc/passwd
# 输出
root:x:0:0:root:/root:/bin/zsh
bin:x:1:1:bin:/bin:/sbin/nologin
2 逻辑表达式
逻辑表达式采用对文本进行比较,只有当条件为真,才执行指定的动作。
逻辑表达式使用***关系运算符***进行两个值的比较,可以用于比较数字与字符串。
实现 字符串的完全相等需要使用 ==
和 !=
其中字符串需要使用双引号引起来
awk -F: '$NF == "/bin/bash"' /etc/passwd
awk -F: '$1 == "root"' /etc/passwd
其他的运算符号:
关系运算符有
<
小于 例如x
>
大于 例如x>y
<=
小于或等于 例如x<=y
==
等于 例如x==y
!=
不等于 例如x!=y
>=
大于等于 例如x>=y`
示例
awk -F: '$3 == 0' /etc/passwd
awk -F: '$3 < 10' /etc/passwd
df -P | grep '/' |awk '$4 > 25000 {print $0}'
+
, -
, *
, /
, %(模: 取余)
, ^(幂:2^3)
可以在逻辑表达式中执行计算,awk都将按浮点数方式执行算术运算
awk -F: '$3 * 10 > 5000{print $0}' /etc/passwd
3 复合模式
符合模式中,通常会使用逻辑运算符号
&&
逻辑与, 相当于 并且
||
逻辑或,相当于 或者
!
逻辑非 , 取反
awk -F: '$1~/root/ && $3<=15' /etc/passwd
awk -F: '$1~/root/ || $3<=15' /etc/passwd
awk -F: '!($1~/root/ || $3<=15)' /etc/passwd
,
隔开使用语法是: 起始表达式, 终止表达式
下面的意思是: 从开头是
bin
的行开始匹配成功一直到含有adm
的行结束匹配
也就是 开头是bin
的行到含有adm
的行 的所有内容都符合匹配条件。
awk -F: '/^bin/,/adm/ {print $0 }' /etc/passwd
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
综合练习
机构号 机构名称 省别 尾箱号 尾箱状态 领用柜员号 领用柜员姓名 币种 余额
11007eee 北京东城区街支行 北京 03 未领用 156 19001.68
11007fff 北京东城区街支行 北京 03 未领用 840 2672.00
11007aaa 北京东城区街支行 北京 04 未领用 156 7261.31
11007ccc 北京朝阳区路支行 北京 02 未领用 156 161490.08
110088ee 北京朝阳区路支行 北京 03 未领用 840 19711.00
34009eff 山西煤矿区路支行 山西 03 未领用 156 282370.23
11007eee 山西煤矿区路支行 山西 03 未领用 156 282370.23
11007eee 山西煤矿区路支行 山西 03 未领用 156 282370.23
11007264 山东平阴县支行 山东 02 未领用 156 304516.23
11007889 山东济阳县支行 北京 04 未领用 840 24551.00
11007264 北京朝阳区支行 北京 02 未领用 156 304516.23
11007284 北京朝阳区支行 北京 02 领用 1002 巫妖王 156 304516.23
11007194 北京朝阳区行 北京 02 未领用 156 304516.23
11007264 河南中原区支行 河南 02 未领用 156 304516.23
11007284 河南二七支行 河南 03 领用 1003 钟馗 156 9046.23
七、awk 脚本编程
1 if语句
格式
{ if (表达式) {语句; 语句; ...}}
awk -F: '{ if ($3==0) {print $1 " is administrator."}}' /etc/passwd
输出: root is administrator.
# 统计系统级别用户的数量
awk -F: '{ if ($3>0 && $3<1000) {count++} } END{print count}' /etc/passwd
输出: 22
2 if…else语句
格式
{ if (表达式){语句;语句;...}else {语句;语句;...}}
awk -F: '{ if ($3==0){print $1} else {print $7} }' /etc/passwd
awk -F: '{ if ($3==0){count++} else{i++} } END{print "管理员个数: "count "系统用户数: "i}' /etc/passwd
输入:
管理员个数: 1系统用户数: 24
awk -F: '{ if($3==0){count++} else{i++} } END{print "管理员个数: "count ; print "系统用户数: "i}' /etc/passwd
输出:
管理员个数: 1
系统用户数: 24
3 if…else if…else语句
格式
{ if(表达式1) {语句;语句;...} else if (表达式2) {语句;语句;...} else if(表达式3){语句;语句;...} else {语句;语句;...} }
awk -F: '{if($3==0){i++} else if($3>999){k++} else{j++}} END{print i; print k; print j}' /etc/passwd
输出:
1
2
22
awk -F: '{if($3==0){i++} else if($3>999){k++} else{j++}} END{print "管理员个数: "i; print "普通用个数: "k; print "系统用户: "j}' /etc/passwd
输出:
管理员个数: 1
普通用个数: 2
系统用户: 22
八、 awk使用外部变量:
1 使用自定义的 shell 变量
方法一:awk参数-v(推荐使用,易读)
[root@shark ~]# read -p ">>:" user
>>:root
[root@shark ~]# awk -F: -v awk_name=$user '$1==awk_name {print "用户存在"}' /etc/passwd
用户存在
[root@shark ~]#
2 使用 shell 的环境变量
[root@shark ~]# read -p ">>:" user
>>:root
[root@shark ~]# export user
[root@shark ~]# awk -F: '$1==ENVIRON["user"] {print "用户存在"}' /etc/passwd
用户存在
[root@shark ~]# unset user
九、指定多个分隔符:[]
echo "a b|c d| ||||e | |" |awk -F'[ |]' '{print $10}'
e
echo "a b|c d| ||||e | |" |awk -F'[ |]+' '{print $5}'
e
[root@shark ~]# echo "110.183.58.144 - - [10/May/2018:23:49:27 +0800] GET http://app." |awk -F'[][ ]' '{print $5 }'
10/May/2018:23:49:27
[root@shark ~]# echo "110.183.58.144 - - [10/May/2018:23:49:27 +0800] GET http://app." |awk -F'[][ ]+' '{print $4 }'
10/May/2018:23:49:27
注意: 中括号内的任意字符均视为普通字符, 比如
.
*
都被看做是 普通字符。
例如:$ echo "a.b*c" |awk -F'[.*]' '{print $1, $2,$3}' a b c
作业:
1. 取得网卡IP(除ipv6以外的所有IP)
2. 获得内存使用情况
3. 获得磁盘使用情况
5. 打印出/etc/hosts文件的最后一个字段(按空格分隔)
6. 打印指定目录下的目录名
十、生产实例:
统计日志中某个时间范围的 IP 访问量,并进行排序
部分日志
110.183.58.144 - - [10/May/2018:23:49:27 +0800] "GET http://app.znds.com/html/20180504/y222sks_2.2.3_dangbei.dangbei HTTP/1.1" 200 14306614 "-" "okhttp/3.4.1"
1.69.17.127 - - [10/May/2018:23:49:31 +0800] "GET http://app.znds.com/down/20180205/ttjs_3.0.0.1_dangbei.apk HTTP/1.1" 200 13819375 "-" "okhttp/3.4.1"
1.69.17.127 - - [10/May/2018:23:49:40 +0800] "GET http://app.znds.com/down/20180416/ttyj_1.1.6.0_dangbei.apk HTTP/1.1" 200 16597231 "-" "okhttp/3.4.1"
1.69.17.127 - - [10/May/2018:23:50:00 +0800] "GET http://app.znds.com/down/20170927/jydp_1.06.00_dangbei.apk HTTP/1.1" 200 36659203 "-" "okhttp/3.4.1"
具体实现
日志文件名:app.log
$ start_dt='10/May/2018:23:47:43
$ end_dt='10/May/2018:23:49:05'
$ awk -v st=${start_dt} -v ent=${end_dt} -F'[][ ]' '$5 == st,$5 == ent {print $1}' app.log |sort |uniq -c |sort -nr |head -n 10
66 223.13.142.15
6 110.183.13.212
4 1.69.17.127
1 113.25.94.69
1 110.183.58.144
时间转换工具
[root@shark ~]# unset months_array
[root@shark ~]# declare -A month_array
[root@shark ~]# month_array=([01]="Jan" [02]="Feb" [03]="Mar" [04]="Apr" [05]="May" [06]="Jun" [07]="Jul" [08]="Aug" [09]="Sept" [10]="Oct" [11]="Nov" [12]="Dec")
[root@shark ~]# echo ${month_array[01]}
[root@shark ~]# Jan
[root@shark ~]# m=10
[root@shark ~]# echo ${month_array[$m]}
[root@shark ~]# Oct
字符串切片
语法: ${var:从这个索引号开始:取出多少个字符:}
索引号从 0 开始
[root@shark ~]# st="20180510234931"
[root@shark ~]# m=${st:4:2}
[root@shark ~]# echo $m
[root@shark ~]# 05
常用日志分析语句
# 访问TOP 20 的IP
16348 58.16.183.52
awk '$9==200 {print $1}' 2018-05-10-0000-2330_app.log | sort |uniq -c |sort -r |head -20
# 访问状态码为20X的 TOP 10的IP
2097 125.70.184.99
2000 183.225.69.158
awk '$9 > 200 && $9 < 300{print $1}' 2018-05-10-0000-2330_app.log | sort |uniq -c |sort -r |head
# 访问TOP 20 的url
250563 http://app.xxx.com/update/2018-04-04/dangbeimarket_4.0.9_162_znds.apk
awk '$9 == 200{print $7,$9}' 2018-05-10-0000-2330_app.log | sort |uniq -c |sort -r |head -20
# 访问状态码为20X的 TOP 10的url
248786 http://app.znds.com/update/2018-04-04/dangbeimarket_4.0.9_162_znds.apk
awk '$9 > 200 && $9 < 300{print $7,$9}' 2018-05-10-0000-2330_app.log | sort |uniq -c |sort -r |head
# 访问次数超过1W的IP
58.16.184.247
58.16.183.52
awk '{print $1}' 2018-05-10-0000-2330_app.log| sort |uniq -c |sort -r |awk '$1 > 10000 {print $1}'
# 访问状态码为404的 TOP 10的url
1017 http://app.xxx.com/update/fixedaddress/kuaisou_qcast.apk.md5
awk '$9 == 404 {print $1}' 2018-05-10-0000-2330_app.log | sort |uniq -c |sort -r |head
十一、提高篇
1 拆分信息到文件中
awk拆分文件很简单,使用重定向就好了。
下面这个例子,是按第 3 例分隔文件,相当的简单(其中的NR!=1表示不处理表头)。
$ awk 'NR!=1{print > $3}' gy.txt
$ ls
gy.txt 北京 山西 山东 河南
你也可以把指定的列输出到文件:
awk 'NR!=1{print $2,$4,$5 > $3}' gy.txt
再复杂一点:(注意其中的if-else-if语句,可见awk其实是个脚本解释器)
$ awk 'NR!=1 {if($3 ~ /北京|山东/) print > "1.txt";
else if($3 ~ /山西/) print > "2.txt";
else print > "3.txt" }' gy.txt
$ ls ?.txt
1.txt 2.txt 3.txt
2 AWK 数组
语法: array_name[index]=value
array_name
数组名称index
索引value
值awk 中的数组和 shell 中的关联数组是一样的,索引都是可以是任意的字符串,索引也可以使用对应有效的变量。
[root@shark ~]# awk 'BEGIN{
arr["a"]=1;
arr["b"]=2+3;
print arr["a"];
print arr["b"]
}'
1
5
[root@shark ~]# echo "a b" |awk 'BEGIN{
arr["a"]=1;
arr["b"]=2+3;
print arr["a"];
print arr["b"]
}'
1
5
示例文件
虚空行者 数学 68
虚空行者 英语 88
黑暗之女 语文 98
黑暗之女 数学 68
无极剑圣 语文 78
无极剑圣 数学 48
琴瑟仙女 语文 90
琴瑟仙女 数学 68
琴瑟仙女 英语 61
影流之主 语文 68
影流之主 数学 88
影流之主 英语 98
[root@shark ~]# awk '{arr[$1]++;print $1,arr[$1]}' hero
虚空行者 1
虚空行者 2
黑暗之女 1
黑暗之女 2
无极剑圣 1
无极剑圣 2
琴瑟仙女 1
琴瑟仙女 2
琴瑟仙女 3
影流之主 1
影流之主 2
影流之主 3
[root@shark ~]# awk '{arr[$1]++} END{print $1,arr[$1]}' hero
影流之主 3
[root@shark ~]# awk '{arr[$1]++} END{for (i in arr){print i, arr[i]}}' hero
黑暗之女 2
虚空行者 2
琴瑟仙女 3
影流之主 3
无极剑圣 2
[root@shark ~]#
我们再来看一个统计各个各省份网点数量的用法:
$ awk 'NR!=1 {a[$3]++;} END {for (i in a) print i ", " a[i];}' gy.txt
北京, 69
山西, 20
江苏, 10
山东, 16
a
是数组名称
[$3]
是数组的索引号,这个索引号可以是普通字符
i
是for
循环 数组 得到的 索引号
a[i]
是获取到索引对应的值
3 内置函数示例
#从file文件中找出长度大于80的行
awk 'length>80' file
# length 是 awk 的内置函数,作业是统计每行的字符串长度
4 随便玩玩
下面的命令计算所有的 txt 文件的文件大小总和。
$ ls -l *.txt | awk '{sum+=$5} END {print sum}'
2511401
其他
# 再来看看统计每个用户的进程的占了多少内存(注:sum的RSS那一列)
$ ps aux | awk 'NR!=1 {a[$1]+=$6;} END { for(i in a) print i ", " a[i]"KB";}'
#按连接数查看客户端IP
netstat -ntu | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -nr
#打印99乘法表
seq 9 | sed 'H;g' | awk -v RS='' '{for(i=1;i<=NF;i++)printf("%dx%d=%d%s", i, NR, i*NR, i==NR?"\n":"\t")}'
#!/bin/bash
declare -A month_array
month_array=([01]="Jan" [02]="Feb" [03]="Mar" [04]="Apr" [05]="May" [06]="Jun" [07]="Jul" [08]="Aug" [09]="Sept" [10]="Oct" [11]="Nov" [12]="Dec")
st=$1
end=$2
logfile=$3
start_yer=${st:0:4}
start_day=${st:6:2}
start_month=${st:4:2}
start_h=${st:8:2}
start_m=${st:10:2}
start_s=${st:12:2}
# 获取到映射月份
start_month=${month_array[$start_month]}
# 把用户输入的日期时间替换日志中的格式: 10/May/2018:23:49:31
start_dt="$start_day/$start_month/${start_yer}:${start_h}:${start_m}:$start_s"
echo $start_dt
end_yer=${end:0:4}
end_day=${end:6:2}
end_month=${end:4:2}
end_h=${end:8:2}
end_m=${end:10:2}
end_s=${end:12:2}
end_month=${month_array[$end_month]}
end_dt="$end_day/$end_month/${end_yer}:${end_h}:${end_m}:$end_s"
echo $end_dt
echo $logfile
export start_dt end_dt
awk -F '[][ ]+' '$4==ENVIRON["start_dt"], $4==ENVIRON["end_dt"] ' $logfile
unset start_dt
unset end_dt
十二、参考资料
内建变量,参看:http://www.gnu.org/software/gawk/manual/gawk.html#Built_002din-Variables
流控方面,参看:http://www.gnu.org/software/gawk/manual/gawk.html#Statements
内建函数,参看:http://www.gnu.org/software/gawk/manual/gawk.html#Built_002din
正则表达式,参看:[http://www.gnu.org/software/gawk/manual/gawk.html#Regexp
一、sed工作流程
sed 是一种在线的、非交互式的编辑器,它一次处理一行内容。
处理时,先把当前处理的行内容存储在临时缓冲区中,称为“模式空间”(pattern space),
之后再用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容打印到屏幕。
接着处理下一行,这样不断重复,直到文件末尾。
注意:模式空间的内容和 AWK 中的 $0 是一样的,处理每行的时候,都会被重新赋值为当前行的内容
文件内容并没有改变,除非你使用重定向存储输出。
Sed主要用来自动编辑一个或多个文件;简化对文件的反复操作;编写转换程序等。
二、命令格式
先准备一个文件
[root@e5ac44e88027 ~]# head /etc/passwd |grep -n '' > mypasswd
[root@e5ac44e88027 ~]# cat mypasswd
1:root:x:0:0:root:/root:/bin/bash
2:bin:x:1:1:bin:/bin:/sbin/nologin
3:daemon:x:2:2:daemon:/sbin:/sbin/nologin
4:adm:x:3:4:adm:/var/adm:/sbin/nologin
5:lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
6:sync:x:5:0:sync:/sbin:/bin/sync
7:shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
8:halt:x:7:0:halt:/sbin:/sbin/halt
9:mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
10:operator:x:11:0:operator:/root:/sbin/nologin
处理单个文件的命令格式
sed [options] '[匹配模式] sed 的内部命令' file1
处理多个文件的命令格式
sed [options] '[匹配模式] [sed 的内部命令]' file1 file2
options
选项是可选的,意思就是没有也行
匹配模式 是可选的用于在文件中每一行进行匹配到模式,模式可以是正则,也可以是文件的行号
内部的命令也是可选的,没想到吧,但是两个单引号是必须的[root@kube-master sed]# sed '' mypasswd root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin ...以下输出省略...
注:
sed和grep不一样,不管是否找到指定的模式,它的退出状态都是0
只有当命令存在语法错误时,sed的退出状态才是非0
三、支持正则表达式
与grep一样,sed在文件中查找模式时也可以使用正则表达式(RE)和各种元字符。正则表达式是
括在斜杠间的模式,用于查找和替换,以下是sed支持的元字符。
使用基本元字符集 ^, $, ., *, [], [^], \< \>,\(\),\{\}
使用扩展元字符集 ?, +, { }, |, ( )
使用扩展元字符的方式:
sed -r
在实际使用的时候,都会加上
-r
参数,即使没有用的扩展正则也不会有任何影响。
四、sed基本用法
打印
sed 默认会输出文件的每一行,无论这行内容是否能匹配上匹配模式,假如被匹配到的则会再输出一次。
sed -r '' mypasswd
sed -r 'p' mypasswd
p
是 sed 的内部命令,是 打印(输出) 的作用
屏蔽默认输出使用 -n
选项
sed -rn 'p' mypasswd
sed -rn '/root/p' mypasswd 显示root的行 ^ 匹配root 开头 ^root
搜索替换 – 这是重点 实际中用的最多
sed会自动打印文件的每一行,同时查找模式匹配的行,找到后执行后面的命令,默认是
p
打印(不加-n
的情况下)
> 搜索每一行,找到有 root 的,把第一个替换为 shark
sed -r 's/root/shark/' mypasswd
> 搜索每一行,找到所有的 root 字符,进行全局替换为 `shark`
sed -r 's/root/shark/g' mypasswd
> i 是同时忽略大小写
sed -r 's/root/shark/gi' mypasswd
> 找到含有 root 的进行删除
sed -r '/root/ d' mypasswd
> 可以使用不同的 字符 作为界定符号,注意进行转义
sed -r '\#root#d' mypasswd
注意:
当在模式匹配中使用其他界定符号时,需要对其进行转义。
其他界定符用在s
搜索替换时不必转义。例如:sed -r 's#root#shark#' mypasswd sed -r 's%root%shark%' mypasswd sed -r 's|root|shark|' mypasswd
五、sed扩展
地址(定址)
地址用于决定对哪些行
进行编辑。地址形式可以是数字、正则表达式或二者的结合。如果没有指定地址,sed将处理输入文件中的所有行。
> 全部分删除
sed -r 'd' mypasswd
> 第 3 行删除
sed -r '3 d' mypasswd
> 第 1 行到第 3 行都删除
sed -r '1,3 d' mypasswd
> 含有 root 字符串的行删除
sed -r '/root/ d' mypasswd
> 从含有 root 字符串的行开始匹配,一直删除到 第 5 行
sed -r '/root/,5 d' mypasswd
> 从含有 halt 的行开始删除,并删除此行之后的 2 行,就是总共删除 3 行
sed -r '/halt/,+2 d' mypasswd
> 含有 root 的行不删除,其他都删除
sed -r '/root/ !d' mypasswd
> 使用行号除以 2 ,余数是 1 的行删除
sed -r '1~2 d' mypasswd
> 使用行号除以 2, 余数 是 0 的 打印出来
sed -rn '0~2 p' mypasswd
> 试试下面这个, 就是 每次处理的行号是被除数,第二个数是除数,第一数是 余数
sed -rn '0~3 p' mypasswd
六、sed命令
sed命令告诉 sed 对匹配到的行进行何种操作,包括打印、删除、修改等。
sed 部分命令示例
替换命令:s
sed -r 's/[0-9][0-9]/&.5/' mypasswd //&代表在查找串中匹配到的所有内容
sed -r 's/(no)login/\1不可登录/' mypasswd
部分输出为
bin:x:1:1:bin:/bin:/sbin/no不可登录
追加命令:a
sed -r '$a 1.1.1.1 www.qfedu.com' /etc/hosts
# $ 符号在这里标识一个文件的最后一行,
# a 是 sed 追加的命令
# a 命令后面的内容均视为 要追加的内容
# 整体意思是向文件的末尾追加一行内容
插入命令:i
# sed -r '2i\1111111111111' /etc/hosts
# sed -r '2i111111111\
> 2222222222\
> 3333333333' /etc/hosts
修改(替换)命令:c
# sed -r '2c\1111111111111' /etc/hosts
# sed -r '2c\111111111111\
> 22222222222\
> 33333333333' /etc/hosts
七、sed常见操作
删除配置文件中 # 号注释的行
sed -ri '/^#/d' file.conf
删除开头的一个或者多个空格或者 Tab 键 加上 '#' 或者开头是 '#' 的行
sed -ri '/^[ \t]*#/d' file.conf
YUM 源修改
sudo sed -ri s/^#baseurl/baseurl/g /etc/yum.repos.d/CentOS-Base.repo
sudo sed -ri s/^mirrorlist/#mirrorlist/g /etc/yum.repos.d/CentOSBase.repo
删除配置文件中//号注释行
sed -ri '\#^[ \t]*//#d' file.conf
删除无内容空行
- 开头和结尾之间什么都没有的行
- 开头和结尾之间有多个空格的行
- 开头和结尾之间有多个 Tab 键的行
sed -ri '/^[ \t]*$/d' file.conf
删除注释行及空行:
以下 3 中效果一样,挑一个自己喜欢的
sed -ri '/^[ \t]*#/d; /^[ \t]*$/d' /etc/vsftpd/vsftpd.conf
sed -ri '/^[ \t]*#|^[ \t]*$/d' /etc/vsftpd/vsftpd.conf
sed -ri '/^[ \t]*($|#)/d' /etc/vsftpd/vsftpd.conf
修改文件:
sed -ri '/^SELINUX=/cSELINUX=disabled' /etc/selinux/config
sed -ri '/UseDNS/cUseDNS no' /etc/ssh/sshd_config
sed -ri '/GSSAPIAuthentication/cGSSAPIAuthentication no' /etc/ssh/sshd_config
给文件行添加注释:
sed -r '2,6s/^/#/' a.txt
使用小括号进行分组,可以有多个分组, 后面可以使用 \1 获取到第一个分组的内容
sed -r '2,6s/(.*)/#\1/' a.txt
sed -r '2,6s/.*/#&/' a.txt &匹配前面查找的内容
sed -r '3,$ s/^#*/#/' a.txt 将行首零个或多个#换成一个#
sed -r '30,50s/^[ \t]*#*/#/' /etc/nginx.conf
sed -r '2,8s/^[ \t#]*/#/' /etc/nginx.conf
sed中使用外部变量
var1=11111
# 无效
sed -r '3a$var1' /etc/hosts
# 正确
sed -r "3a$var1" /etc/hosts
# 有效
sed -r 3a$var1 /etc/hosts
# 报错
sed -r "$a$var1" /etc/hosts
# 有效,但是中间不能有空格
sed -r '$a'"$var1" /etc/hosts
# 有效, 将第一个 $ 进行转义
sed -r "\$a $var1" /etc/hosts
多重编辑选项:-e
[root@kube-master sed]# sed -e '1,3 d' -e 's/root/shark/' mypasswd
4:adm:x:3:4:adm:/var/adm:/sbin/nologin
5:lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
6:sync:x:5:0:sync:/sbin:/bin/sync
7:shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
8:halt:x:7:0:halt:/sbin:/sbin/halt
9:mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
10:operator:x:11:0:operator:/shark:/sbin/nologin
[root@kube-master sed]#
sed -e '1,3 d' -e 's/root/shark/' mypasswd
等同于
sed '1,3 d; s/root/shark/' mypasswd
函数
就是对代码的封装,通常会完成一个功能,而出现的一种组织和代码的方式。
函数式编程
函数必须先定义才可以使用
一、定义函数
方法一:
函数名() {
函数要实现的功能代码
}
方法二:
function 函数名 () {
函数要实现的功能代码
}
例如:
say_you_say_me(){
echo "我看过很多书,但都没有你好看^_^"
echo "我看过很多书,但都没有你好看^_^"
}
二、调用函数
函数名
say_you_say_me
函数名 参数1 参数2
# 定义函数
say_you_say_any(){
echo "我看过很多书,但都没有 "$1" 好看^_^"
}
# 调用函数
say_you_say_any xinyi
执行效果
[root@kube-master function]# sh say.sh
我看过很多书,但都没有 xinyi 好看^_^
作业:
编写系统初始化脚本
配置YUM
安装 bash-completion epel-release lsof
关闭 selinux
关闭 firewalld
配置静态 IP
按q键退出程序
编写系统管理工具箱
查看内存的使用情况
查看磁盘的使用情况
查看系统的负载
查看目前登录的用户数量
按q键退出程序
…
三、函数参数
在Shell中调用函数时可向其传递参数。在函数体内部通过 $n
的形式来获取参数的值,如:$1
表示第1个参数,$2
表示第2个参数…;当n>=10时,表示为 ${n}
,如:${10}、${11}
位置参数变量:是预定义变量中一种
位置参数变量 | 作用 |
---|---|
$n |
利用参数向程序中传递需要调用的值n为数字,n≤9直接用数字,n≥10都需要用{}包含:$0 表示命令本身 $1-$9 表示第1-9个参数 ${10} 表示第10个参数 |
$* |
表示命令行中所有的参数,所有参数看作一个整体 |
$@ |
表示命令行中的所有参数,每个参数区分对待 |
$# |
表示命令行中所有参数的个数(不统计$0 ) |
传参示例
# 定义函数
show_args (){
echo "函数的第一个参数$1"
echo "函数的第二个参数$2"
echo "函数的所有参数$@"
echo '函数中 $0 还是' $0
}
# 调用函数
show_args hello world
输出
[root@kube-master function]# sh func-args.sh
函数的第一个参数hello
函数的第二个参数world
函数的所有参数hello world
函数中 $0 还是 func-args.sh
脚本的位置参数和函数的位置参数
[root@shark function]# cat func-arg.sh
#!/bin/bash
f1 (){
echo "--->$1"
}
f1 $2
[root@shark function]# sh func-arg.sh a
--->
[root@shark function]# sh func-arg.sh a b
--->b
[root@shark function]#
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5ObK6Xf6-1600594004835)(assets/image-20200914165713151.png)]
五、函数中调用函数
f1(){
echo "f1...."
}
function f2(){
f1
echo "f2..."
}
f2
输出
[root@kube-master function]# sh func.sh
f1....
f2...
六、函数的返回值
我们可以把函数中产生的数据,交给脚本中的其他代码使用,以便做进一个的处理,比如存入数据库,作为其他函数的参数,作为判断条件等。
f1(){
ip="192.168.1.100"
pwd="123"
echo "$ip" # 函数返回值1
echo "$pwd" # 函数返回值2
# echo "$ip $pwd" # 函数返回值
}
# 用变量接收函数的返回值, 函数需要先在子 shell 中执行
# 语法:
# 变量名=$(函数名) 或者 变量名=`函数名`
info=$(f1)
echo $info
输出
192.168.1.100 123
七、定义函数的局部变量: local
[root@shark function]# cat scirpt-arg.sh
ip="dd"
f1 (){
# local 定义了一个局部变量,局部变量只能
# 在函数内生效
local ip="192.168.1.10"
local pwd="123"
echo "函数内:$ip"
}
f1
echo "函数外:$ip $pwd"
[root@shark function]# sh scirpt-arg.sh
函数内:192.168.1.10
函数外:dd
[root@shark fu
基本思路
shell 操作 MySQL 是通过给
mysql
这个客户端程序传递相应的参数实现的
mysql -u用户 -p'password' db_name -e "sql 语句"
#!/bin/bash
HOSTNAME="localhost" #数据库信息
PORT="3306"
USERNAME="root"
PASSWORD="QFedu123!"
DBNAME="d1" #数据库名称
TABLENAME="t1" #数据库中表的名称
exec_mysql="mysql -h${HOSTNAME} -P${PORT} -u${USERNAME} -p${PASSWORD}"
#创建数据库
create_db_sql="create database IF NOT EXISTS ${DBNAME}"
${exec_mysql} -e "${create_db_sql}"
${exec_mysql} -e "show databases;"
#创建表
create_table_sql="create table IF NOT EXISTS ${TABLENAME} ( name varchar(20), id int(11) default 0 )"
${exec_mysql} ${DBNAME} -e "${create_table_sql}"
#插入数据
insert_sql="insert into ${TABLENAME} values('billchen',2)"
${exec_mysql} ${DBNAME} -e "${insert_sql}"
查询
查询时候可能需要避免不必要的输出
-N
不输出列名(字段名)
-B
不输出数据之间的边框竖线 (|
)
输出格式可以是其他的,比如
-H
输出 HTML 格式
[root@shark ~]# cat exec-mysql.sh
#!/bin/bash
HOSTNAME="localhost" #数据库信息
PORT="3306"
MYSQLPWDFILE="./.mysqldb"
DBNAME="jspgou" #数据库名称
TABLENAME="jc_address" #数据库中表的名称
exec_mysql="mysql --defaults-file=${MYSQLPWDFILE} -h${HOSTNAME} -P${PORT}"
city="北京市"
city2="上海市"
p_id=NULL
id=8
sql="select Id,parent_id,name from ${TABLENAME}
where name='"$city"' or name='"$city2"' or Id=$id
or parent_id is $p_id;"
echo $sql
${exec_mysql} ${DBNAME} -e "$sql"
# 执行脚本
[root@shark ~]# sh exec-mysql.sh
select Id,parent_id,name from jc_address where name='北京市' or name='上海市' or Id=8 or parent_id is NULL;
+----+-----------+-----------+
| Id | parent_id | name |
+----+-----------+-----------+
| 1 | NULL | 江西省 |
| 8 | 7 | 西湖区 |
| 9 | NULL | 安徽省 |
| 10 | NULL | 北京市 |
| 11 | NULL | 上海市 |
+----+-----------+-----------+
[root@shark ~]#
隐藏用户名和密码
[mysql]
user=root
password='QFedu123!'
mysql --defaults-file=./mysql_pwd -h 172.17.0.2 -e "show databases;" -N -B
expect 内部命令
首先需要安装
yum install -y expect
语法结构
spawn shell 命令程序
expect "捕获到shell 命令程序执行之后输出的字符串"
send "发送给 shell 命令程序的字符串"
在命令行直接输入
expect
可以进入expect
程序的解释器终端
[root@e5ac44e88027 ~]# expect
expect1.1> spawn echo "hello" # 发送一条 shell 命令, 这里是指 echo "hello"
spawn echo hello
1490
expect1.2> expect "hello" # 捕获这个字符串,只要包含这个字符串就可以
hello
expect1.3> send "yes\n" # 发送一个字符串
expect1.4> expect off # 结束这次捕获
yes
expect1.5>
在脚本中使用
# 开始 expect 解释器程序
/usr/bin/expect<
".ssh/id_rsa)" { send "\n"; exp_continue }
意思是 捕获到字符串 “.ssh/id_rsa)” 后 发送字符串 “\n” 就是相当于按下回车键
exp_continue
意思是继续进行捕获,不退出 expect 程序
实战案例
# 写个用于自动生成密钥对的函数
auto_keygen (){
/usr/bin/expect<