shell基础
参考文章:https://www.cnblogs.com/jingmoxukong/p/7867397.html#一篇文章让你彻底掌握-shell-语言
shell 概述
shell是一个命令解释器,它接收应用程序/用户命令,然后调用操作系统内核
shell是一个功能强大的编程语言,易编写,易调试,灵活性强。
-
Linux提供的shell解释器有
cat /etc/shells
-
bash 和 sh的关系
cd /bin ll | grep bash [root@lys bin]# echo $SHELL /bin/bash
脚本入门
脚本以#!/bin/bash
开头(指定解析器)
#!/bin/bash
echo "hello world"
常见解释器
# 以下两种方式都可以指定 shell 解释器为 bash,第二种方式更好
#!/bin/bash
#!/usr/bin/env bash
执行脚本:
方式一:
bash helloworld.sh
方式二:
./helllworld.sh
会出现权限不够的启示
chmod 777 ./helloworld.sh
多命令处理
#!/bin/bash
cd /home/linux/
touch banzhang.txt
echo "I LOVE YOU" >> banzhang.txt
注释
- 单行注释 - 以
#
开头,到行尾结束。 - 多行注释 - 以
:<<EOF
开头,到EOF
结束。
#--------------------------------------------
# shell 注释示例
# author:zp
#--------------------------------------------
# echo '这是单行注释'
########## 这是分割线 ##########
:<<EOF
echo '这是多行注释'
echo '这是多行注释'
echo '这是多行注释'
EOF
echo
echo 用于字符串的输出
$ cat exc1.sh
#!/bin/bash
# 这是一个echo的练习
# 1.输出普通字符串:
echo "hello world"
# 2.输出含变量的字符串
echo "hello, \"zp\""
# 3.输出含变量的字符串
name=zp
echo "hellp \"$name\""
# 4.输出含换行符的字符串
echo "YES \nNO"
echo "YES -e \nNO" # -e 开启转义
# 输出不换行字符串
echo "YES"
echo "NO"
echo "YES\c"
echo "NO"
# 输出重定向到文件
echo "test" test.txt
# 输出执行结果
echo `pwd`
printf
printf 用于格式化输出字符串
默认,printf不会像echo一样自动添加换行符,如果需要换行,可以手动添加
\n
#!/bin/bash
# printf 格式化输出字符串
# 单引号
printf '%d %s\n' 1 "abc"
# 双引号
printf "%d %s\n" 1 "abc"
# 无引号
printf %s absdf
# 格式只指定了一个参数,但多出的参数仍会按照格式输出
printf "%s\n" abc def
# 如果没有参数,那么 %s 用NULL代替,%d用0代替
printf "%s and %d \n"
自定义变量
基本语法:
- 定义变量:变量=值,等号两边不能有空格
- 撤销(删除)变量:unset 变量
- 输出变量:echo $变量
- 声明静态变量:readonly 变量, 注意:不能unset
变量定义规则:
-
变量名:可以由字母,数字和下划线组成,不能以数字开头,环境变量名建议大写
-
等号两侧不能有空格
-
在bash中,变量默认类型都是字符串类型,无法直接进行数值运算
-
变量的值如果有空格,需要使用双引号或单引号括起来
-
可以把变量提升为全局变量(export),可供其他shell程序使用
export 变量
访问变量的语法形式: ${var} 和 $var
变量名外面的花括号是可选的,加不加都行,但是推荐加花括号
特殊变量
$n
$n (描述:n为数字,$0代表脚本名称,10以内参数用$1-9 表 示 , 10 以 上 的 需 要 用 大 括 号 包 含,{10})
#!/bin/bash
echo "$0 $1 $2 $3"
12
$#
$# (功能描述:获取所有输入参数个数,常用于循环)
#!/bin/bash
echo "$0 $1 $2 $3"
echo $#
123
$* 和$@
- $* (描述:代表命令行中所有的参数,把所有参数看成一个整体)
- $@ (描述:也代表命令行中所有的参数,不过把每个参数区分对待)
[linux@localhost datas]$ bash parameter.sh test1 test2
parameter.sh test1 test2
2
test1 test2
test1 test2
12345
$?
$? (描述:最后一次执行命令的状态,0:正确执行)
运算符
-
$((运算式)) 或 $[运算式]
-
expr +,-,*,/,% 加减乘除取余
expr运算符间要有空格
# 计算2+3 [linux@localhost datas]$ expr 2 + 3 5 # 计算(2+3)*4 ## 方式1 [linux@localhost datas]$ expr `expr 2 + 3` \* 4 20 ## 方式2 [linux@localhost datas]$ s=$[(2+3)*4] [linux@localhost datas]$ echo $s 20
常见的系统环境变量
变量 | 描述 |
---|---|
$HOME |
当前用户的用户目录 |
$PATH |
用分号分隔的目录列表,shell 会到这些目录中查找命令 |
$PWD |
当前工作目录 |
$RANDOM |
0 到 32767 之间的整数 |
$UID |
数值类型,当前用户的用户 ID |
$PS1 |
主要系统输入提示符 |
$PS2 |
次要系统输入提示符 |
$USER | 获取当前用户 |
条件判断
基本语法
[ condition ] ( 注意 Condition 前后要有空格 )
常用判断条件
两整数之间比较
符合 | 描述 |
---|---|
-lt | (less than) 小于 |
-le | (less equal)小于等于 |
-eq | (equal)等于 |
-gt | (greater than)大于 |
-ge | (greater equal) 大于等于 |
-ne | (not equal) 不等于 |
文件权限判断
- -r 有读的权限
- -w 有写的权限
- -x 有执行的权限
文件类型判断
- -f 文件存在并且是一个常规文件
- -e 文件存在
- -d 文件存在并是一个目录
# 判断23是否大于2
[linux@localhost datas]$ [ 23 -gt 2 ]
[linux@localhost datas]$ echo $?
0
# 判断helloworld.sh是否有写入权限
[linux@localhost datas]$ [ -w hellowrld.sh ]
[linux@localhost datas]$ echo $?
1
# 判断目录中文件是否存在
[linux@localhost datas]$ [ -e /home/linux/datas ]
[linux@localhost datas]$ echo $?
0
多条件判断
&& ||
流程控制
IF判断
[linux@localhost datas]$ cat if.sh
#!/bin/bash
if [ $1 -eq 1 ]
then
echo "班长真帅"
elif [ $1 -eq 2 ]
then
echo "班长真丑"
fi
[linux@localhost datas]$ bash if.sh 2
班长真丑
casse 语句
[linux@localhost datas]$ cat case.sh
#!/bin/bash
case $1 in
1)
echo "班长"
;;
2)
echo "学习委员"
;;
3)
echo "体育委员"
;;
*)
如果变量的值都不是以上的值,则执行此程序
;;
esac
[linux@localhost datas]$ bash case.sh 2
学习委员
注意事项:
- case行尾必须为单词
in
,每个模式匹配必须以有)
结束 - 双分号
;;
表示命令序列结束,相当于java的break - 最后的
*)
,表示默认模式,相当于java中的default
for 循环
-
语法1
[linux@localhost datas]$ cat for.sh #!/bin/bash s=0 for((i=1;i<=100;i++)) do s=$[$s+$i] done echo $s [linux@localhost datas]$ bash for.sh 5050
-
语法2
[linux@localhost datas]$ cat for2.sh #!/bin/bash for i in $* do echo $i done [linux@localhost datas]$ bash for2.sh 1 2 1 2
while 循环
[linux@localhost datas]$ cat while.sh
#!/bin/bash
s=0
i=1
while [ $i -le 100 ]
do
s=$[$s + $i]
i=$[$i + 1]
done
echo $s
[linux@localhost datas]$ bash while.sh
5050
read 读取控制台输入
read(选项)(参数)
- -p 指定读取值时的提示符
- -t 指定读取值时等待的时间(秒)
# 提示7秒内,读取控制台输入的名称
[linux@localhost datas]$ cat read.sh
#!/bin/bash
read -t 7 -p "在7s内请输入你的名字" NAME
echo $NAME
[linux@localhost datas]$ bash read.sh
在7s
函数
系统函数
basename
basename [string / pathname] [suffix] (描述:basename命令会删掉所有的前缀包括最后一个‘/’字符,然后将字符串显示出来)
# 方式1
[linux@localhost datas]$ basename /home/linux/banzhang.txt
banzhang.txt
# 方式2
[linux@localhost datas]$ basename /home/linux/banzhang.txt .txt
banzhang
dirname
dirname 文件绝对路径 (描述:从给定的包含绝对路径的文件名中去除文件名(非目录的部分),然后返回剩下的路径(目录的部分))
[linux@localhost datas]$ dirname /home/linux/banzhang.txt
/home/linux
自定义函数
# 格式
[ function ] funname[()]
{
Action:
[return int;]
}
funname
# DESC 计算输入两个参数的值
[linux@localhost datas]$ cat sum.sh
#!/bin/bash
function sum(){
s=0;
s=$[$1 + $2]
echo $s
}
read -p "input your param1:" P1
read -p "input your param2:" P2
sum $P1 $P2
[linux@localhost datas]$ bash sum.sh
input your param1:1
input your param2:2
3
综合案例
归档文件
实际生产中,往往需要对重要数据进行归档备份。
需求:实现一个每天对指定目录归档备份的脚本,输入一个目录名称(末尾不带/),将目录下所有文件按天归档保存,并将归档日期附加在归档文件名上,放在/root/archive下。
这里使用到了归档命令:tar
后面可以加上-c表示归档。加上-z选项表示同时进行压缩,得到的文件后缀名为tar.gz
脚本实现:
#!/bin/bash
#首先判断输入参数个数是否为1
if [ $# -ne 1 ]
then
echo "参数个数错误!应该输入一个参数,作为归档目录"
exit
fi
# 从参数中获取目录名称
if [ -d $1 ]
then
echo
else
echo
echo "目录不存在!"
exit
fi
DIR_NAME=$(basename $1)
DIR_PATH=$(cd $(dirname $1); pwd)
# 获取当前日期
DATE=$(date +%y%m%d)
# 生成的归档文件名称
FILE=archive_${DIR_NAME}_$DATE.tar.gz
DEST=/root/archive/$FILE
# 开始归档
echo "开始归档..."
echo
tar -zcf $DEST $DIR_PATH/$DIR_NAME
if [ $? -eq 0 ]
then
echo
echo "归档成功"
echo "归档文件为:$DEST"
else
echo "归档出现问题"
echo
fi
exit
shell 工具
cut命令从文件的每一行剪切字节,字符和字段并将这些字节,字符和字段输出
cut [选项参数] filename
- -f 列号,提取第几列
- -d 分隔符,按照指定分隔符分隔列
- -c 按字符进行切割,后加n 表示取第几列 比如:-c 10
# DESC 切割cut.txt第一列
[linux@localhost datas]$ cat cut.txt
dong shen
guan zhen
wo wo
lai lai
le le
[linux@localhost datas]$ cut -d " " -f 1 cut.txt
dong
guan
wo
lai
le
# DESC 获取第三行第一个单词
[linux@localhost datas]$ cat cut.txt | grep guan | cut -d " " -f 1
guan
选取系统PATH变量值,第2个“:”开始后的所有路径
echo $PATH
/java-project/jdk1.8.0_321/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bi
echo $PATH | cut -d ":" -f 3-
/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
sed
sed是一种流编辑器,它一次处理一行内容,处理时,把当前处理的行存储在临时缓冲区中,成为“模式空间”,接着sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕,接着处理下一行,这样不断重复,知道文件末尾,文件内容并没有改变,除非你使用重定向存储输出
sed [选项参数] ‘command’ filename
- -e 直接在指令列模式上进行sed的动作编辑
命令功能描述
- a 新增
- d 删除
- s 查找并替换
# DESC 在第二行后增加mei nv字符
[linux@localhost datas]$ sed -e "2a mei nv" sed.txt
dong zhen
guan zhen
mei nv
wo wo
lai lai
awk
awk 一个强大文件分析工具,把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行分析处理
awk [选项参数] 'pattern1 {action1} pattern2{action2}..' filename
pattern: 表示awk在数据中查找的内容,就是匹配模式
action:在匹配内容时所执行的一系列命令
- -F 指定输入文件分隔符
- -v 赋值一个用户定义变量
案例实操
-
数据准备
搜索passwd文件以root关键字开头的所有行,并输出该行的第7列
#cut 实现 cat /etc/passwd | grep ^root | cut -d ";" -f 7 # awk实现 cat /etc/passwd | awk -F ":" '/^root/ {print $7}' ps -ef | awk '{print $}' # 打印文件大小 ll -l | awk '{print $5}'
搜索passwd文件以root关键字开头的所有行,并输出该行的第1列和第7列,中间以“,”号分割。
cat /etc/passwd | awk -F ":" '/^root/ {print $1","$7}'
只显示/etc/passwd的第一列和第七列,以逗号分割,且在所有行前面添加列名"begin"在最后一行添加"end"
cat /etc/passwd | awk -F ":" 'BEGIN{print "begin"}{print $1","$7}END{print "end"}'
将passwd文件中的用户id增加1并输出
cat /etc/passwd | awk -F ":" '{print $3+1}' # 使用变量 cat /etc/passwd | awk -v i=1 -F ":" '{print $3+i}'
awk的内置变量
变量 | 说明 |
---|---|
FILENAME | 文件名 |
NR | 已读的记录数(行号) |
DF | 浏览记录的域的个数(切割后,列的个数) |
案例实操
-
统计password文件名,每行的行号,每列的列数
cat /etc/passwd | awk -F ":" '{print "文件名 :"FILENAME "行号:" NR "列数: "NF }'
-
查询ifconfig命令和输出结果中的空行所在的行号
ifconfig | awk '/^$/ {print "空行: " NR}'
综合应用
我们可以利用 Linux 自带的 mesg 和 write 工具,向其它用户发送消息。
需求:实现一个向某个用户快速发送消息的脚本,输入用户名作为第一个参数,后面直 接跟要发送的消息。脚本需要检测用户是否登录在系统中、是否打开消息功能,以及当前发送消息是否为空。
前置知识
who 查看有多少控制台
[root@bogon logs]# who
root pts/0 2022-05-26 17:34 (124.64.252.49)
root pts/1 2022-05-27 10:12 (120.244.202.117)
root pts/2 2022-05-27 11:37 (58.34.52.34)
root pts/5 2022-05-27 10:16 (120.244.202.117)
root pts/6 2022-05-27 19:10 (120.245.102.201)
root pts/8 2022-05-25 10:18 (124.64.252.49)
# 向某个控制台发出消息
write root pts/1
hi
脚本实现如下:
#!/bin/bash/
# 查看用户是否登录
login_user=$(who | grep -i -m 1 $1 | awk '{print $1}' )
if [ -z $login_user ]
then
echo "$1 不在线!"
echo "脚本退出"
exit
fi
# 查看用户是否开启消息功能
is_allowed=$(who -T | grep -i -m 1 $1 | awk '{print $2}' )
if [ $is_allowed != "+" ]
then
echo "$1 没有开启消息功能"
echo "脚本退出..."
exit
fi
# 确认是否有消息发送
if [ -z $2 ]
then
echo "没有消息发送"
echo "脚本退出"
exit
fi
# 从参数中获取要发送的消息
whole_msg=$(echo $* | cut -d " " -f 2-)
# 获取用户登录的终端
user_terminal=$(who | grep -i -m 1 $1 | awk '{print $2}' )
# 写入要发送的消息
echo $whole_msg | write $login_user $user_terminal
if [ $? != 0 ]
then
echo "发送失败!"
else
echo "发送成功!"
fi
exit
sort
sort 命令是在Linux里非常有用,它将文件进行排序,并将排序结果标准输出
sourt [选项] (参数)
参数 | 描述 |
---|---|
-n | 依照数值大小排序 |
-t | 以相反的顺序排序 |
-t | 设置排序时使用的分隔字符 |
-k | 指定需要排序的列 |