Shell的作用
Shell是Shell解释器的简称,机器本身是不知道我们编写的脚本是什么意思,需要使用shell解释器将命令翻译为机器能够读懂的信息。
Shell脚本
Shell解释器需要解析Shell脚本像机器传达信息,Shell脚本就是用户输入的指令。
Shell脚本的分类:
- 交互式执行指令:人工干预,写一行命令,计算机执行一次(平常输入的命令就是使用这种方式)
- 非交互式执行指令:将脚本写成一个文档,然后执行该文档,然后文档中的命令就可以安静地在后台执行。
常见的Shell解释器
Shell解释器,是一种在操作系统中为用户提供命令行界面
(CLI)和脚本编程环境
的程序。
常见的Shell解释器有
- Bourne Shell (sh):这是最早的标准Unix Shell,由Stephen R. Bourne开发,是许多现代Shell的基础。
- Bourne-Again Shell (bash):bash是GNU Project的默认Shell,基于Bourne Shell发展而来,目前在大多数Linux发行版中作为默认的交互式Shell使用。
- Korn Shell (ksh):由David Korn开发,继承了Bourne Shell的优点,并增加了一些增强功能,广泛应用于商业Unix系统。
- POSIX Shell (sh):严格遵循POSIX标准的Shell,很多系统中会有一个指向bash或其他兼容POSIX标准的Shell链接。
- Z Shell (zsh):zsh是一款强大的、可高度定制的Shell,拥有丰富的特性集,如高级别自动补全、主题支持和更强的命令行编辑能力。
- C Shell (csh) 和 Tenex C Shell (tcsh):源自Berkeley大学,其语法和命令结构与C语言相似,提供了一些独特的特性,如命令历史的补全和编辑功能。
- Almquist Shell (ash) 和 BusyBox Ash (dash):ash是一款小巧的Shell,常用于嵌入式系统或资源有限的环境中。Ubuntu系统曾用dash作为默认的/bin/sh解释器。
- Fish Shell: 一款强调用户友好性和自动补全功能的Shell,致力于简化命令行操作。
查看系统拥有的Shell解释器
命令:cat /etc/shells
/bin/sh
/bin/bash
/sbin/nologin
/usr/bin/sh
/usr/bin/bash
/usr/sbin/nologin
/bin/tcsh
/bin/ csh
也可以安装系统没有的解释器
yum install ksh
切换系统Shell解释器
系统默认的解释器是bash
,系统运行会自动运行一个解释器用来解析用户输入的命令。
如果想要切换解释器直接在终端输入解释器命令即可,比如接换代sh解释器,直接在终端输入:
sh
解释器可以层层嵌套,即你可以在一个bash解释器中再打开一个sh解释器或者其他的解释器。
退出当前解释器只需要再终端直接输入:
exit
bash解释器
bas解释器还是比较好用的解释器,支持历史记录、快捷键、tab、管道(|)、重定向(>>)
bash的快捷键有:
快捷键 | 描述 |
---|---|
Ctrl + A | 将光标移至行首 |
Ctrl + E | 将光标移至行尾 |
Ctrl + C | 终止操作 |
Ctrl + D | 结束输入 |
Ctrl + M | 回车 |
Ctrl + U | 删除光标至行首的所有内容 |
Ctrl + W | 删除光标前面的一个单词 |
Ctrl + S | 挂起,冻结终端,只是冻结终端的显示 |
Ctrl + Q | 解除冻结终端 |
Alt + . | 使用前一个命令的最后一个词 |
上下键 | 历史命令 |
Tab键 | 补齐命令 |
Shell脚本
Shell脚本一般以sh
后缀结尾,但是改后缀不是给机器识别的,机器根据Shell脚本的解释器声明来识别该文件是一个Shell脚本,.sh
后缀是为了方便人看的。
Shell脚本的构成
一个标准的shell脚本的构成:
声明解释器:需要在脚本头部声明该脚本需要使用什么解释器。
#!/bin/ bash
注释: 编写注释标明脚本功能、变量含义、每个步骤要解决的问题
脚本以`#`开头
具体代码:表示shell脚本要执行的内容
注意Shell脚本中不能编写交互式命令,如vim
命令。所以如果需要使用输入操作可以使用echo "编写的内容" > 目标文件
示例:
test.sh
#! /bin/bash
#这是一个测试脚本
echo "hello world"
运行shell脚本
执行脚本有很多方法
法一:赋予脚本执行权限(X),然后直接在终端输入该文件的
路径
即可运行该脚本。
如果想要直接输入文件名就可以运行该脚本,需要将该脚本的路径放置在全局变量PATH
中。法二:直接使用
解释器 脚本文件
的命令运行脚本$ bash test.sh hello world $ sh test.sh hello world
法三:使用
source 脚本文件
运行脚本, 可以简写成. 脚本文件
表示使用当前解释器运行脚本$ source test.sh hello world
法一使用的解释器是脚本文件中声明的解释器
法二使用的解释器是执行命令中的解释器
法三使用的解释器是当前系统的解释器
法二和法三是有区别的:法二使用的是另一个解释器,解释器解析完命令之后就会退出当前解析器;法三使用的是当前解释器,解释器解析完之后不会退出。
如果执行如下脚本:
#!/bin/bash
mkdir abc
cd abc
如果使用的是法二,执行的最后的结果是:所在的目录是当前执行的目录,因为使用指定解释器执行完之后就会退出该解释器,指定解释器进入过abc目录,但是指定解释器退出了,所以最后所在的目录是当前执行的目录。
如果使用的是法三,执行的最后的结果是:所在的目录是abc
目录,因为使用的就是当前的解释器无需退出解释器。
Shell脚本示例
配置yum源的脚本示例:
#!/bin/bash echo "[仓库名] name=仓库地址 baseurl=yum下载源 enabled=1 gpgcheck=1" > /etc、yum.repos.d/abc.repo
一键部署vsftpd脚本
#!/bin/bash yum -y install vsftpd &> /dev/null systemctl start vsftpd systemctl enable vsftpd
创建用户并且配置密码
adduser.shuseradd $1 echo $2 | passwd --stdin $1
这里使用了变量,$1代表命令的第一个参数, $2代表第二个参数
bash adduser.sh yang 123
表示创建yang用户并且设置密码为123
Shell脚本变量
变量分类:
- 常量:固定不变的内容
- 变量:存储可能会发生变化的内容,添加脚本灵活。变量同时又如下分类:
自定义变量:变量名称可以使用字母,数字,下划线,不能以数字开头,不能使用特殊符号
- 定义变量:变量名 = 值
- 调用变量:
$变量名
- 在字符串中调用变量:字符串
${变量名}
其它字符串 - 取消变量定义:unset 变量名
环境变量:系统已经定义好的变量,我们可以直接使用。终端输入
env
可以查看所有环境变量。调用环境变量也是$环境变量
常见的几个环境变量:
– USER: 当前用户
– HOME:家目录
– UID:用户的uid
– SHELL: 当前的解释器
– PS1:终端一级提示符
– PS2:终端二级提示符
– PATH:命令程序
存放路径
查看这些变量的命令是echo $PATH
:echo是输出命令,$指明是一个变量,PATH是变量名位置变量:
$1
、$2
、$3
等,$i
表示执行命令的第 i 个参数预定义变量 :
$0
、$$
、$?
、$#
、$*
– $0:表示脚本的执行名称
– $$ : 表示当前的进程号
– $?:表示上一条指令是否有问题,0表示运行正常,非0表示异常
– $#:执行命令所有参数的个数
– $*:执行命令的所有参数
– $! : 放在后台进程的进程号,比如sleep 1000 &
就是将sleep进程放到后台去执行,执行该进程使用 $! 就可以看到进程号。
eg:var.sh#!/bin/bash echo $1 echo $2 echo $3 echo $$ echo $# echo $* echo $? echo $0 echo $!
运行该脚本:
bash var.sh a b c
运行结果:
a b c 1052 3 a b c 0 var.sh
使用set
命令可以查看机器上的所有变量
变量还可以分为局部变量和全局变量:
- 局部变量:局部变量就是只能在当前解释器中访问该变量。如果在当前解释器中再进一层解释器,该解释器中就访问不到在上一层解释器中定义的变量
- 全局变量:全局变量就是可以在所有的解释器中访问变量
默认系统的环境变量都是全局变量,我们自己定义的变量就是局部变量。
如果想要将自定义全局变量,只需要使用export
指令即可
export 变量名
:定义全局变量export -n 变量名
:取消全局变量的定义,变为局部变量。
引号用法
""
:界定范围,使用双引号,双引号里面的内容是一个整体,可以在双引号里面输入空格,换行''
:和双引号一样可以界定范围,和双引号不同的是会屏蔽特殊符号,即如果命令中使用$变量名
,就不能中识别出该变量- `` :反引号用于,获取反引号中执行指令的结果,等价于
$()
touch a b
: 就会生成两个文件a
和b
touch "a b"
: 就会生成一个名字为a b
的文件
url=123
echo "$url" // 会输出123
echo '$url' // 会输出$url
abc=date
echo abc //输出date
abc=`date`
echo abc //输出定义a时的时间
终端输入提示
有的时候执行脚本需要一些参数,但是如果是另一个人直接使用这个脚本他并不知道需要携带什么参数,可以使用read
命令在终端做一些提示信息:
-p参数
是终端的提示信息
#!/bin/bash
read -p "请输入用户名:" u
useradd $u
read -p "请输入密码:" p
echo $p | passwd --stdin $u
这种脚本是交互式脚本。
优化:我们不想让密码被看到,可以使用stty
指令屏蔽控制台回显
stty -echo
:屏蔽回显,之后再终端输入的任何内容都不可见,但是注意他不会屏蔽系统输出的内容,只能隐藏你输入的内容。stty echo
:取消屏蔽回显模式
所以上面的代码可以优化为:
#!/bin/bash
read -p "请输入用户名:" u
useradd $u
stty -echo
read -p "请输入密码:" p
stty echo
echo $p | passwd --stdin $u
Shell中的计算
expr进行计算
使用expr
命令可以进行计算,并且计算结果可以直接输出到终端,注意后面的运算符前后都有一个空格
expr 2 + 1
,输出结果:3
expr 2 - 1
, 输出结果:1
expr 2 / 1
, 输出结果:3
expr 3 % 2
,输出结果:1注意由于乘法符号
*
是通配符的意思,所以使用的时候需要使用\
或者''
屏蔽字符的特殊含义- \ : 只屏蔽一个特殊字符
- ‘’ : 屏蔽一串特殊字符
expr 2 \* 1
或expr 2 '*' 1
$[]
进行计算
$[表达式]
计算的结果不会自动输出到终端,需要借助echo
输出。
$[表达式]
等同于$(())
echo $[1+1] echo $[1-1] echo $[1*1] echo $[1/1] echo $[1%1]
let 修改变量的值
想要修改变量的值还需要使用let 变量名 = 表达式
进行修改。let a=a+1 let a++ let a/=3
bc
注意上面的expr
、$[]
、let
都不支持小数,计算小数的时候需要使用bc
,
bc模式下,可以计算1.1+1;使用scale=3
表示保留小数点后3位,默认不保留小数。echo "scale=3;10/3" | bc echo "1.1+1" | bc
输出
3.33
条件判断
条件判断:有两种方式进行条件判断
[ 条件表达式 ]
: [ abc == 123 ]结果就为false,注意表达式前后都有空格test 条件表达式
:test abc == 123 结果就为false
字符串的判断符号
== : 判断两个表达式是否相同,前后需要有空格 != : 哦单端两个表达式是否不同,前后需要有空格 -z : 判断是否为空,[ -z "$a" ]就是判断a变量是否为空, ! -z : 判断是否不为空,[ ! -z "$a" ]就是判断a变量是否不为空,等价于 -n
逻辑连接符
条件表达式一般都会用到逻辑连接符:&& : 与操作,前后需要有空格。A && B ,AB都为真结果才为真,如果A为真才会去执行B,否则直接返回false || : 或操作,前后需要有空格。A && B ,AB有一个为真结果就为真,如果A为假才会去执行B,否则直接返回true ; :前面的任务执行完之后执行后面的任务,前后并没有直接的关系。可以将多条命令放在一行中执行。
367@DESKTOP-D5NN2T1 MINGW64 ~/Desktop/sh $ [ 1 == 1 ] && echo 123 123 367@DESKTOP-D5NN2T1 MINGW64 ~/Desktop/sh $ [ 1 != 1 ] && echo 123 367@DESKTOP-D5NN2T1 MINGW64 ~/Desktop/sh $ [ 1 == 1 ] || echo 123 367@DESKTOP-D5NN2T1 MINGW64 ~/Desktop/sh $ [ 1 != 1 ] || echo 123 123
执行完条件判断命令之后可以使用
echo $?
进行判断条件是否为ture,如果为真,那么输出结果就是0,否则为1。数字的判断符号
==
和!=
是用于字符串的之间是否相等的判断,不适合用于数字之间是否相等判断,如 [ 0123 == 123 ] 的返回结果就是false。
所以数字有专用的判断符:-eq 等于 -ne 不等于 -gt 大于 -ge 大于等于 -lt 小于 -le 小于等于
文件相关的判断符号
对于文件是否有没有等需要使用文件专用的条件判断符进行判断:
(对文件和文件夹都是生效的)-e 是否存在该文件 [ -e /opt/abc ] 取反 [ ! -e /opt/abc ] -f 判断对象是否存在并且是文件 -d 判断对象是否尊在并且是目录 -r 判断当前用户对该文件是否有读权限 [ -r /opt/abc ] -w 判断当前用户对该文件是否有写权限 -x 判断当前用户对该文件是否有执行权限
优化用户添加脚本:如果用户没有输入提示用户输入并且退出程序。
#!/bin/bash read -p "请输入用户名:" u [ -z "$u" ] && exit useradd $u read -p "请输入密码:" p echo $p | passwd --stdin $u
优化yum源配置脚本:如果脚本已经安装就不进行配置
#!/bin/bash yum list installed | grep vsftpd &> /dev/null [ $? == 0 ] && exit echo "[仓库名] name=仓库地址 baseurl=yum下载源 enabled=1 gpgcheck=1" > /etc、yum.repos.d/abc.repo
脚本示例:每两分钟检查系统登陆的用户数量,如果超过3人给管理员报警。
使用who可以查看终端连接的用户数,几行就有几个用户连接,wc 查看用户数。
task.sh#!/bin/bash n=`who | wc -l` [ $n -gt 3] && echo "报警,有人攻击服务器!" | mail -s test root
设置计划任务
crontab -e */2 * * * * bash /opt/task.sh
也可以即将需要发送的内容提前准备好,然后使用输入重定向<
作为邮件内容进行发送。
[ $n -gt 3] && mail -s test root < a.txt
if语句
单分支写法:
if 判断条件 ; then 条件成立时执行的命令 fi
或者
if 判断条件 then 条件成立时执行的命令 fi
fi 是跳出 if 结构
eg:#!/bin/bash read -p "请输入用户名:" u if [ -z "$u" ] ; then echo "必须输入用户名" exit fi useradd $u read -p "请输入密码:" p echo $p | passwd --stdin $u
双分支写法
if 判断条件;then 命令序列1 else 命令序列2 fi
多分支写法
if 判断条件;then 命令序列1 elif 判断条件2;then 命令序列2 elif 判断条件3;then 命令序列3 else 命令序列X fi
脚本示例:写一个ping的脚本
ping命令在脚本中编写时需要添加如下参数- -c:指定ping几次,不指定的话会一直ping
- -i :隔多久ping一次
- -W : 如果不同,多久返回结果,默认是3秒
ping的结果可以使用$?
指令获取,如果为0表示ping成功了,如果为1表示ping失败了。
ping -c 3 -i 0.2 -W 1 ping的目的地址 &> /dev/null if [ $? -eq 0 ];then echo "通了" else echo "不通" fi
for语句
循环执行指令用的。
for循环结构
for 变量名 in 值列表 do 命令序列 done
注意,变量名代表的是当前循环的列表的值。
#!/bin/bash for i in a b do echo $i done
输出结果是
a b
列表的简写
命令脚本中
{a..b}
表示的是a到b的连续的所有数字,但是不支持变量
{1…5} :表示1,2,3,4,5
a=10 ;{1…$a}:这种写法不支持seq n:表示1到n的所有数字排序展示
seq 5:1,2,3,4,5
a=5;seq $a
:和上述的输出结果是一致的。
案例:实现循环ping脚本
for i in {1..15} do ping -c 3 -i 0.2 -W 1 192.168.1.$i &> /dev/null if [ $? -eq 0 ]; then echo "192.168.1.$i 通了" else echo "192.168.1.$i 不通" fi done
while语句
while循环结构
while 条件测试 do 任务序列 done
死循环:无限循环,将条件测试使用
:
表示即可。while : do 任务序列 done
死循环一般不直接使用,消耗cpu,如果非要使用一般结合
sleep
命令,让cpu休息下while : do 任务序列 sleep 1 done
案例:猜数游戏
#!/bin/bash a=0 x=$[ RANDOM % 101 ] while: do let a++ read -p "请输入一个数字(0-100)" n if [ $x -eq $n ];then echo "猜对了!,猜了$a次" exit elif [ $n -lt $x ];then echo "猜小了" else exho "猜大了" fi done
case语句
case分支,是简化版本的if,代码编写比if精简,但功能不如 if 强大。
case 结构
case 变量 in 模式1) 命令序列1;; 模式2) 命令序列2;; *) 命令序列3 esac
case示例1:
case $1 in aa|a|A) echo aaaaa;; bb) echo bbbbb;; *) echo "aa|bb" esac
nginx安装和部署的脚本
安装nginx的脚本
#!/bin/bash yum -y install gcc openssl-devel pcre-devel tar -xf /opt/nginx-1.12.2.tar.gz cd nginx-1.12.2.tar.gz ./configure make make install
运行nginx
cd 到nginx的目录,然后执行niginx
文件,运行nginxcd /user/local/nginx/sbin ./nginx
关闭防火墙
systemctl stop firewalld
关闭nginx
./nginx -s stop
编写打开和关闭nginx的脚本
#!/bin/bash case $1 in start|st) /usr/local/nginx/sbin/ngxin;; stop) /usr/local/nginx/sbin/ngxin -s stop;; restart) /usr/local/nginx/sbin/ngxin -s stop /usr/local/nginx/sbin/ngxin;; stat) netstat -ntulp | grep -q nginx [ $? -eq 0] && echo "服务已启动" || echo "服务未启动" *) echo "参数错误"
Shell函数
函数,可以定义公共的语句块,在脚本中反复调用,达到精简脚本的目的
定义函数:
函数名 () { // 函数的参数无需在()中定义,直接在函数中使用即可,`$n`就是表示第n个参数 }
或者
function 函数名 { // 函数的参数无需在()中定义,直接在函数中使用即可,`$n`就是表示第n个参数 }
调用函数:
函数名 参数列表
eg1:
abc () {
mkdir $1
cd $1
}
abc dir1
创建并跳转到dir1
目录。
eg2:循环执行ping命令
myping(){
ping -c1 -W1 $1 &>/dev/null
if[ $? -eq 0 ];then
echo "$1 is up"
else
echo "$1 is down"
fi
}
for i in {1..254}
do
myping 192.168.4.$i &
done
wait
&>/dev/null
将输出结果放置在“黑洞”里,不输出在屏幕中myping 192.168.4.$i &
中的&
表示将任务放置在后台wait
表示将后台的任务执行完之后再结束脚本,和&
搭配使用。
eg3:fork炸弹
.(){
.|.&
}
.
该脚本会消耗cpu所有资源,会中断服务器,因为函数的自调用。
中断控制
- exit:直接跳出脚本
- break:跳出当前循环,继续执行循环后的内容
- continue:跳出当次循环,执行下一次循环
break示例:
求和,当输入0的时候计算出结果:
#!/bin/bash
x=0
while:
do
read -p "请输入数字(0是结束)" n
[ $n -eq 0 ] && break
let x+=n
done
echo ""所有数字之和是$x
continue示例:
1-20找出6的倍数并分别加10
#!/bin/bash
for i in {1..20}
do
x=$[ i%6 ]
[ $x -ne 0 ] && continue
xx = $[ i+10 ]
echo $xx
done
字符串操作
截取字符串:
${字符串变量名: 开始截取的index: 需要截取几位}
86198@LAPTOP-GJMAFV0K MINGW64 ~ $ a=abc 86198@LAPTOP-GJMAFV0K MINGW64 ~ $ echo ${a:1:1} b
案例:生成一个随机的8位密码
#!/bin/bash a=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 for i in {1..8} do x=$[ RANDOM%62 ] pass=${a:x:1} pass2=$pass2$pass done echo $pass2
替换字符串:
${字符串变量名/需要替换的字符/替换后的字符}
,只替换第一个匹配的内容,并且不会替换原字符串。
${字符串变量名//需要替换的字符/替换后的字符}
,替换所有匹配的内容,并且不会替换原字符串。86198@LAPTOP-GJMAFV0K MINGW64 ~/Desktop/test $ a=aabbcc 86198@LAPTOP-GJMAFV0K MINGW64 ~/Desktop/test $ echo ${a/a/5} 5abbcc 86198@LAPTOP-GJMAFV0K MINGW64 ~/Desktop/test $ echo ${a//a/5} 55bbcc
字符串删除
可以利用字符串的替换功能,将想要删除的字符串替换成空字符串:${字符串变量名/需要替换的字符/}
也可以使用字符串特定的删除方法:${字符串变量名#需要删除的字符}
:从左向右删,只删除第一次匹配的${字符串变量名##需要删除的字符}
:从左向右删,删除所有匹配到的字符,需要删除的字符前面一般可以添加一个*
号,表示该字符串及字符串前面的内容前删除。${字符串变量名%需要删除的字符}
:从右向左删,只删除第一次匹配的${字符串变量名%%需要删除的字符}
:,删除所有匹配到的字符,需要删除的字符后面一般可以添加一个*
号,表示该字符串及字符串后面的内容前删除。
案例:将所有txt文件修改为doc文件
#!/bin/bash for i in `ls *.txt` do x=${i%.*} mv $i $x.doc done
定义变量的初值
${字符串变量名:-需要设定的初值}
正则表达式
正则表达式规则
普通正则:
shell中的正则表达式一般配合grep
使用.正则符号 含义 ^ 匹配行首 $ 匹配行尾 [] 集合,匹配集合中任意单个字符 [^] 对集合取反,匹配不是集合中的任意一个字符 . 任意单个字符(不包含空行,但是包含空格) * 匹配前一个字符任意次 (0-n) \{n,m\}
表示匹配前一个字符 n 到 m 次 \{n\}
匹配前一个字符n次 \{n,\}
匹配前一个字符n次及以上 \(\)
保留 简写:
[a-z]
: 表示从a到z的任意字符[^a-z]
:表示除a到z外的任意字符
扩展正则:
扩展正则需要配合egrep
使用,单纯使用grep
是不支持的
扩展正则是对正则的一种简写,但是可能对一些较新的命令不支持,使用之前,需要查看兼容性正则符号 描述 + 最少匹配一次 ? 最多匹配一次 {n,m} 匹配n到m次 () 组合为整体 | 或者 (可以连用) \b 单词边界, 可以视为单词边界的字符有:空、空格、tab、特殊字符;字母、数字、下划线都将视为单词的一部分
正则表达式具体使用
需要配合grep
命令使用, grep
后面的正则表达式最好使用""
包裹,避免出现歧义。
预备文件:a.txt
anaconda-ks.cfg
a.txt
initial-setup-ks.cfg
公共
模板
视频
图片
文档
下载
音乐
桌面
查找文件中以anaconda开头的内容
grep ^anaconda a.txt
查找文件中的空行
grep "^$" a.txt
查找文件中匹配 abc中任意一个字符的内容
grep [abc] a.txt
查找文件中的任意内容:
grep ".*" a.txt
- 边界单词
b.txt
bin bin *bin%9bino abina bin_
获取边界单词bin:
grep \bbin\b b.txt
输出:bin bin
- 边界单词
sed 流式编辑器
可以非交互式
修改文本,逐行操作,不指定地址符的话就逐行执行命令
使用方式:
前置命令 | sed 选项 '(地址符)指令'
sed 选项 '(地址符)指令' 编辑的目标文本
选项取值:
- -n:屏蔽默认输出
- -r:支持
扩展正则
- -i:写入文件,必须添加
-i
选项才能将修改的值写入源文件
地址符取值:
地址符表示的是行号,有如下表示形式
- n:表示第n行
- n,m:表示n到m行
- n,+m:表示第n行后面再加m行
- n~m:表示新操作第n行,然后每隔m行就操作一次
- 也可以使用正则表达式表示:
/正则表达式/
指令取值:
p:输出指定内容
输出2,3行内容:[root@localhost ~]# sed -n '2,3p' a.txt bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin
- 隔行输出:
sed -n '1~2p' a.txt
- 输出第二行和第四行:
sed -n '2p;4p' a.txt
- 使用正则表达式,输出以bin开头的内容:
sed -n '/^bin/p' a.txt
- 隔行输出:
=:显示行号
- 显示所有行行号
[root@localhost ~]# sed -n '=' a.txt 1 2 3 4 5
- 显示最后一行行号:
sed -n '$=' a.txt
d:删除内容 —— 以行为单位进行删除
用法和p指令的用法类似,只不过他是删除命令。- 删除第一行:
[root@localhost ~]# sed '1d' a.txt 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
输出的是剩余行的内容
- 删除带有
bash
或者nologin
的行
[root@localhost ~]# sed -r '/bash|nologin/d' a.txt
- 删除不带有
bash
或者nologin
的行
[root@localhost ~]# sed -r '/bash|nologin/!d' a.txt
- 删除 最后一行
[root@localhost ~]# sed '$d' a.txt
- 删除 所有空行
[root@localhost ~]# sed '/^$/d' a.txt
s:替换内容
s/要被替换的内容/替换后的内容/
:默认替换的是每一行的第一个匹配的内容s/要被替换的内容/替换后的内容/g
:替换所有匹配到的内容s/要被替换的内容/替换后的内容/n
:替换每一行第n个匹配到的内容- 如果要替换的内容中有
/
,为了防止歧义,使用#
做替换符:s#要被替换的内容#替换后的内容#
(注意不单单是#
可以只要是数字键盘上对应的字符都可以) - 只显示替换的内容:
sed -n 's/要被替换的内容/替换后的内容/p'
将a.txt中的每一行的第一个2017替换为2018:
sed 's/2017/2018/' a.txt
将a.txt中的每一行的第3个2017替换为2018:sed 's/2017/2018/3' a.txt
将a.txt中的第1行的第3个2017替换为2018:sed '1s/2017/2018/3' a.txt
将a.txt中的每一行的开头添加aaa:sed 's/^/aaa/' a.txt
去掉 a.txt中的每一行的开头的aaa:sed 's/^#an/an/ a.txt'
删除 a.txt中的每一行的2017:sed 's/2017// a.txt'
。 和-d
命令不同的是他可以删除某一行中的内容。
删除每一行的第二个字符:sed 's/.//2' a.txt
删除每一行的第二个字符和最后一个字符:sed 's/.//2' a.txt;sed 's/.$//' a.txt
替换文本中的第一个字符和最后一个字符:sed 's/^(.)(.*)(.)$/\3\2\1/' a.txt
。 \3\2\1表示将第三个"参数"和第一个“参数”交换位置。
将所有的大写字母添加():sed -r '/([A-Z])/(\1)/g' a.txt
a:在行的上面追加内容
sed 'a 888' a.txt
在每一行的上面追加888i:在行的下面追加内容
sed 'i 888' a.txt
在每一行的下面追加888c:替换行
sed 'c 888' a.txt
把每一行替换为888
awk:强大的搜索功能
awk基于模式匹配检查输入文本,逐行处理
并输出。
使用方式:
前置命令 | awk [选项] '[条件]{指令}'
awk [选项] '[条件]{指令}' 操作的目标文件
预备文件:
b.txt
hello the world
welcome to beijing
指令:
print:输出指令。
指令有参数:
$n
:表示列的参数,每一行默认以空格
进行分割为列,每一个列以$n
来表示。
NF:表示有多少列
NR:表示有多少行
"常量"
:可以用于输出常量值- 输出文档全部内容:
[root@localhost ~]# awk '{print}' b.txt hello the world welcome to beijing
- 输出第一列的内容
[root@localhost ~]# awk '{print $1}' b.txt hello welcome
选项:
-F符号
,表示以指定的符号分隔列, awk默认以空格分割列,但是也可以指定是用什么符号分割列。也可以指定多个分隔符-F[多个分隔符]
- 输出以
:
分隔的第5列
awk -F: '{print $5}' a.txt
- 输出以
:
和/
分隔的第5列
awk -F[:/] '{print $5}' a.txt
- 输出每行的列数
awk -F[:/] '{print "当前行有"NF"列"}' a.txt
- 输出以
条件:
条件可以是正则表达式
- 输出以’ak’开头的行的列数
awk -F[:/] '/^ak/{print "当前行有"NF"列"}' a.txt
- 查看连接本机失败的主机
awk '/Failed/{print $11}' /var/log/secure
awk的任务:
逐行任务:逐行任务就是默认执行的任务,单纯使用
{指令}
包括的任务就是逐行任务逐行任务前执行的任务:在逐行任务执行之前也可以执行部分任务,需要使用
BEGIN{指令}
逐行任务后执行的任务:在逐行任务执行之后也可以执行部分任务,需要使用
END{指令}
- 逐行任务执行前执行操作
[root@localhost ~]# awk 'BEGIN{print 1+1}' 2
- 输出行数
[root@localhost ~]# awk 'BEGIN{x=0}{x++}END{print x}' b.txt
- 输出用户的用户名、uid、Home目录
[root@localhost ~]# head -5 /etc/passwd | awk -F: 'BEGIN{print "User\tUID\tHome"}{print $1"\t"$3"\t"$6}END{print "总计"NR" 行"}' User UID Home root 0 /root bin 1 /bin daemon 2 /sbin adm 3 /var/adm lp 4 /var/spool/lpd 总计5行
awk的包含符号:
~:表示包含; !~:表示不包含
如果print前面有内容,print可以省略
- 输出b.txt的内容
awk '/root/' b.txt
- 输出第一列包含
root
的内容
awk -F: '$1~/root/' b.txt
- 输出第一列不包含
root
的内容
awk -F: '$1!~/root/' b.txt
awk的比较符号
== (等于)
!=(不等于)
>
(大于)<
(小于)<=
(小于等于)查看第5行的内容
awk 'NR==5{print}' b.txt
查找第一列内容是
root
的内容awk '$1=="root"' b.txt
awk的逻辑符号
&&: 与运
|| : 或运算
awk -F: '$3<=10 || $3>1000' /etc/passwd
查找100以内包含7或者是7的倍数的数据:
seq 100 | awk '$1%7==0||$1~/7/' /etc/passwd
需求:
查找使用bash解释器的用户,并输出他的密码:
users=awk -F: 'bash${print $1}' /etc/passwd for i in $u do grep $i /etc/shadow | awk -F: '{print $1"-->"$2}' done
awk的if判断
if(条件){}else{}
awk -F: '{if($3<=1000){i++}else{j++}}END{print i j}'
awk数组
[数据1,数据2,...]
, 但是必须使用下标调用数组,不能直接使用数组名调用下标和值都可以不一定是数字
[root@localhost ~]# awk 'BEGIN{a[0]=1;a[1]=2;a["abc"]="abc";print a[1]}' 2
awk的for循环
for(i in 变量名 数组){}
[root@localhost ~]# awk 'BEGIN{a[0]=1;a[1]=2;a["abc"]="abc";for(i in a){print i,a[i]}}' abc abc 0 1 1 2
应用:查找数据个数
awk '{a[$i]++}END{for(i in a){print i,a[i}}' b.txt
案例
查看 cpu负载,内存容量,硬盘空间,网卡流量,安装的软件包数量,账户数量,当前登录的账户数量
- cpu负载: uptime命令
uptime | awk '{print "cpu平均负载是" $8,$9,$10}'
- 内存容量:free
free -m | awk '/Mem/{print "主机剩余内存:"$4}'
- 硬盘空间
df -h | awk '/vda1/{print "硬盘剩余空间是"$4}'
- 网卡流量
ifconfig eth0 | awk '/RX p/{print "网卡eth0接收的数据量是"$5"字节"}'
- 查看安装的软件包的数量
p=`rpm -qa | wc -l` echo "当前主机安装的软件包的数量是$p"
- 用户量
awk '{x++}END{print "主机拥有账户总数是"x"个"}'
- 当前登录的用户数
x=`who | wc -l` echo "当前登陆账户数量是$x个"
- 查看运行进程数
psn=`ps aux | wc -l` echo "主机运行的进程有$psn个"
监控机器是否被人攻击,如果一个用户登录3次仍未登陆成功就报警
#!/bin/bash x=`awk '/Failed/{ip[$11]++}END{for(i in ip){print i","ip[i}}' /var/log/secure` for i in $x do p=${i%,*} s=${i#*,} [ $s -gt 3 ] && echo "报警,$p访问本机失败了$s次,请尽快处理" | mail -s test root done
Windows运行shell
Windows的shell命令通常指的是在命令提示符(cmd)或Windows PowerShell中执行的命令。
PowerShell中的命令与Linux shell(如Bash、Zsh等)有很大的不同:- 语法差异:PowerShell采用了面向对象的命令和参数设计,命令通常较长;Linux shell命令通常较短,命令和参数较为简洁,
- 数据处理:PowerShell支持管道传递对象,不仅仅是文本流;inux shell也支持管道(|),但是默认处理的是文本流。
- 脚本语言特性:PowerShell具有更丰富和完善的数据类型;Linux shell中的脚本语言虽然功能强大,但在类型系统方面不如PowerShell严谨,更多依赖于字符串操作和模式匹配。
- 生态系统和模块:PowerShell有自己的模块系统,可以通过Import-Module命令加载扩展模块,提供更多的命令和功能。Linux shell可以通过脚本、函数和外部命令(如apt-get、yum等)扩展功能,也有包管理工具如pip(Python)、npm(Node.js)等提供跨语言的命令行工具。
- 集成环境:PowerShell深度集成了Windows操作系统,可以直接管理Windows特有的资源,如注册表、AD域服务、IIS等;Linux shell则是Linux/Unix环境的自然组成部分,它可以方便地管理Linux系统和各种开源服务。
自Windows 10以来,微软还引入了Windows Subsystem for Linux (WSL),它允许用户在Windows内核上原生运行Linux发行版,这意味着用户可以在Windows系统上使用真正的Linux shell(如
Bash
、Zsh等)以及Linux命令行工具。
echo补充
- echo是输出命令
[root@localhost ~]# echo "Hello" Hello
- 添加颜色
需要添加-e
参数,然后使用\033[32m
修改字体颜色,\033[xxm是修改颜色的指令
xx是3x
:表示修改字体颜色
xx是4x
:表示修改背景色
xx是0:表示修改字体颜色为原来的颜色[root@localhost ~]# echo -e "\033[32mHello\033[0m" Hello
ab工具
ab工具可以模仿网站访问数:
ab -c 1 -n 50000 http://172.25.0.10/index.html/
使用ab测试网站访问量,-c是用户数量,-n是访问次数
sort工具
排序工具,
添加-r
参数,是倒序排序
添加-n
参数,是按数字排序,默认是按字母顺序排序的
5.txt:
2
4
5
3
1
[root@localhost ~]# sort 5.txt
1
2
3
4
5
[root@localhost ~]# sort -r 5.txt
5
4
3
2
1
uptime
该命令用于查看机器的负载
[root@localhost ~]# uptime
16:55:19 up 7:50, 3 users, load average: 0.10, 0.29, 0.31
[root@localhost ~]# uptime | awk '{print "cpu平均负载是" $8,$9,$10}'
cpu平均负载是0.19, 0.22, 0.28