shell 编程
- 绝对路径 /root/scripts/file.sh #要有执行权限
- 相对路径 ./file.sh #要有执行权限
- bash + 脚本名 #不需要执行权限和编译
- source 脚本名 #脚本中的变量会在当前 shll 生效
前面三种是在子 shell 中执行,第四种是在当前 shell 执行
$[]
,中括号里面可以进行运算
位置化参数
$0 脚本名
$1 第1个参数
$* 所有的参数,双引号引起来时参数视为单个字符串
$@ 所有的参数,双引号引起来时,每个参数作为一个个体
$# 参数的个数
$$ 当前进程的 PID
$? 上一个命令的返回值,0表示成功
read
从命令行中输入字符串,赋值给一个变量
1 | read -p "Enter your name:" NAME |
1 | [student@example Documents]$ read -p "Enter your name:" NAME |
不加变量名,默认赋值给 REPLY 变量。
-t 超时时间(秒)
exit 退出码
程序执行后会携带一个退出码
0 代表成功,1-255 代表失败;储存在系统变量 $? 中;exit num 可以中断程序并设置退出码为 num
例如,判断 /etc/profile
文件里是否有关键字 HISTSIZE
1 | [student@example Documents]$ grep HISTSIZE /etc/profile |
条件执行运算符
&&
代表逻辑与,前一个命令执行成功会执行后一个
||
代表逻辑或,前一个命令执行成功,后面的命令不会再执行
布尔运算符
! 非运算
-o 或运算
-a 与运算
数值运算符
判断变量是不是数字:
[["$num10" =~ ^[0-9]+$ ]]
ps: shell中各种括号的作用()、(())、[]、[[]]、{}
字符串运算符
= 检测两个字符串是否相等。 等于返回 0 [ $a = $b ]
!= 检测两个字符串是否相等。等于返回 1 [ $a != $b ]
[ -n “$A” ] 判断变量是否定义
条件判断
1 | if 条件表达式 |
1 | if 条件表达式;then |
1 | if 条件表达式;then |
1 | if 条件表达式1;then |
case语句
1 | case 变量名 in |
for循环
直接列出元素
1 | for i in 1 3 5 |
使用大括号
1 | for i in {1...5} |
使用 seq
1 | for i in $(seq 1 5) |
使用命令的结果
1 | for i in $(ls *.sh) |
ps: shell 编程内容较多,建议找一本相应的书籍学习,本文仅列出了很少一部分。
计划任务
一次性计划任务
at 管理一次性计划任务
at timespec
创建计划任务,ctrl+d 结束输入
at timespec < script
从脚本输入
at -l
列出计划任务
at -c jobnum
查看计划任务的详细信息
at -d jobnum
删除计划任务
timespec 举例
8:.05am
4pm+3days
04:00 2021-08-02
now+5min
teatime tomorrow
(下午茶时间)
时间的具体定义在文件 /usr/share/doc/at/timespec
/etc/at.deny
和 /etc/at.allow
文件可以设置其他用户的计划任务权限
周期性计划任务
crontab 命令管理周期性计划任务
crontab -e 编辑当前用户的计划任务
crontab -l 列出当前用户的计划任务
crontab -r 删除当前用户所有计划任务
crontab -u 管理其他用户的计划任务
例如:
crontab -u admin -l 列出 admin 用户的计划任务
1 | * * * * * echo hello |
简记:分,时,日,月,周
如果是同一位置的两个时间点,就以逗号隔开
举例
1 | 0 10 1 * * 任务 |
每月的 1 号 10:00 执行一次
1 | 0 10 1 * 6 任务 |
每月的 1 号 10:00 且该天为星期六时执行一次
1 | 10,20 * * * * 任务 |
每小时的第 10 分和第 20 分执行一次
1 | */5 10 * * * 任务 |
每天10点,每隔5分钟执行一次
1 | * 10-12 * * * 任务 |
每天的 10 到 12 点,每分钟执行一次(注意,12:00 ~ 12:59在该时间段范围内)
1 | 30-59/2 14 * * * 任务 |
// TODO
每天的 14:30 ~ 15:00,每两分钟执行一次 ?????
用户的计划任务
cron 任务(使用 crontab 命令创建的任务)保存在 /var/spool/cron
中的文件中,文件的名称与用户名相同
用户设置计划任务的权限
/etc/cron.deny 仅拒绝
/etc/cron.allow 仅允许
系统的计划任务
- crond 服务管理
使用 /etc/crontab 文件创建的计划任务,一般只有系统管理员才能操作该文件
还可以写在 /etc/cron.d/ 文件夹下的文件里。 /etc/cron.hourly/ 中也有
建议不要使用命令,而是使用该文件创建计划任务
- anacorn
配置文件 /etc/anacrontab
该服务管理 /etc/cron.daily/、/etc/cron.monthly/、/etc/cron.weekly/ 三个目录
查找和处理文件
find 查找文件
根据文件名查找
-name 文件名,支持使用 glob(7) * ? [] [^]
-iname 文件名,不区分字母大小写
-inum n 按 node 号查找
-link n 链接数为 n 的文件
例如 find /etc -name '*pass*'
根据文件属主、属组查找
1 | find /home -user admin |
根据文件大小查找
-size [+|-] #UNIT
UNIT 表示常用单位:k, M, G,#表示给出的具体数值
#UNIT 对应 (#-1, #)
-#UNIT 对应 [0, #-1]
+#UNIT 对应 (#, )
5M 对应 (4M, 5M]
-5M 对应 (0, 4M]
+5M 对应 (5M, )
例如 find /tmp -size 5M
根据文件的权限查询
-perm
MODE 精确匹配
/MODE u, g, o 任何一类对象只要有一位匹配中即可
-MODE 每一类对象都必须同时拥有指定的权限才可匹配
0 表示不关注
1 | find -perm 644 严格匹配 644 的文件权限 |
根据文件时间戳查找
以“天”为单位
-atime
-mtime
-ctime
以“分钟”为单位
-amin
-mmin
-cmin
例如 find /tmp -atime +7
在查找到的文件上继续操作
例如
find /etc/ -name *.conf -exec cp {} /root/data \;
对查找到的每个文件执行拷贝到 /root/data 下的命令
find ! -perm -100 -exec chmod u+x {} \;
对查找到的文件,没有执行权限的给其加上执行权限
dd 命令
dd: device to device
if: input file
of: output file
bs: block size 块大小,文件系统 文件最小组成单元
count: 块的个数
- 创建特定大小的文件
1 | dd if=/dev/zero of=/dev/file.txt bs=1M count=100 |
- 对磁盘进行备份(字节级别的拷贝)
1 | dd if=/dev/nvme0n1 of=/dev/nvme0n4 |
- 抹除磁盘数据
1 | dd if=/dev/zero of=/dev/nvme0n4 |
-
测试底层存储的速率
-
对分区备份
1 | dd if=/dev/nvme0n1p1 of=boot.bak |
恢复
1 | dd if=/boot.bak of=/dev/nvme0n1p1 |
网络客户端
curl 命令
1 | curl url (获取该网址的文本信息) |
wget 命令,上传和下载文件
- wget url 下载文件
- wget -O 文件名 url 下载之后改名
- wget -b 后台下载
- wget --spider http://www.linux.com/dodo1.JPG 解析能否下载但不会下载
- wget -P 指定下载目录
OpenSSH
OpenSSH 在 RHEL 上使用 SSH 协议,能以加密和安全的方法进行通信。
ssh [user@]hostname 远程登录
ssh [user@]hostname command 远程执行命令
ssh -X [user@]hostname 远程调用图形界面
ssh 作为服务端的配置文件 /etc/ssh/sshd_config
公钥和私钥
每次输入密码比较麻烦,使用秘钥验证较为方便和安全。
非对称加密:
两把钥匙,公钥和私钥
公钥加密,私钥解密
私钥加密,公钥解密
客户端生成公钥和私钥,把公钥给服务端
客户端连接的时候,服务端会使用公钥加密一段随机的字符串,然后将字符串发送给客户端;
客户端使用私钥解密加密后的字符串,并将解密后的字符串发送给服务端;
服务端对比发送过来的字符串与加密之前的字符串是否相同,相同即成功。
在客户端生成公钥和秘钥
1 | ssh-keygen |
会生成私钥文件 id_rsa 和公钥文件 id_rsa.pub。客户端将公钥发送给服务端(服务端会将其存储在 ~/.ssh/authoried_keys)
1 | ssh-copy-id -i id_rsa.pub root@192.168.153.139 |
成功后,可以尝试登陆
1 | ssh root@192.168.153.139 |
此时可以直接连接,不需要再输入密码
为了私钥的安全,在生成私钥的时候,给私钥加密。但如果这样做,连接服务端的时候需要输入私钥密码。鉴于此,需要为 ssh 开启认证代理。
1 | ssh-agent bash # 开启认证代理 |
scp 远程传输命令
用法:
scp [option] 本地文件 远程账户@远程IP地址:远程目录
scp [option] 远程账户@远程IP地址:远程文件 本地文件
-r: 复制目录
-p: 保留时间和权限
-P: 指定远程主机的端口号
-C: 压缩数据
rsync 增量备份工具
rsync [options…] src… [dest]
Pull: 远程->本地
1 | rsync root@192.168.153.39:/home/student/ /tmp/test/ |
Push: 本地->远程
1 | rsync /tmp/test/ root@192.168.153.39:/home/student/ |
注意,源路径如果是一个目录的话,/home/student
和 /home/student/
是不一样的。带上尾斜线表示的是目录中的文件,不包括目录本身;不带尾斜线表示的是整个目录包括目录本身,传送目录需要用 -r 参数
增量备份的原理:
mtime 时间戳:文件内容是否修改
option 说明:
-r: 同步目录
-v: 显示 rsync 过程中的详细信息
-n: 进测试传输,而不实际传输
-t: 保持 mtime 属性
-o: 保持 owner 属性
-g: 保持 group 属性
-p: 保持 perms 属性(权限,不包括特殊权限)
–delete: 以 SRC 为主,对 DEST 进行同步。多则删之,少则补之。
高级用户组和权限
密码期限
用户密码文件 /etc/shadow
1 | root:$6$mpoRoTVDJTTcy9q3$wd3uxrPXXgwqpTUDkAWGAbThCiD/zutRc6ekGpcDf34zLR4Fd/WnyZLQWGOSmudO/ZwVIXsXRYbl0qYWle/yJ0:18818:0:99999:7::: |
用户名:加密密码:最近一次修改密码的时间:密码最短使用天数: 密码最长有效天数:密码即将到期警告天数:密码过期后账户保持活动天数:账号失效时间:保留字段
chage 命令调整密码期限
chage -m 0 -M 90 -W7 -I 14 user3
分别修改用户密码的最短期限,最长期限,警告周期,失效期限
chage -d 0 user3
强制要求用户在下一次登录时更新密码
chage -I user3
显示用户密码的信息
chage -E 2020-10-10 user3
用户将于 2020-10-10 到期
默认权限
目录的最大权限是 777
文件的最大权限是 666
创建文件或目录时,默认会有一个权限。这个权限,由用户的 umask 值来决定(文件或目录的最大权限减去 umask 值)
设置 umask 值用 umask 命令,例如 umask 033
,这种是临时修改方式;修改 ~/.bashrc 可以永久修改。
普通用户的 umask 值是 002,因此,创建的文件权限是 664,目录权限是 775
root用户的 umask 值是022,因此,创建的文件权限是 644,目录权限是 755
文件的特殊权限
例如,执行以下命令
1 | [root@example ~]# ll /etc/shadow |
会发现用户对该文件没有任何权限,但是用户修改密码的时候,确实是修改了该文件,怎么实现呢?
passwd 这个命令在这里
1 | [root@example ~]# ll /usr/bin/passwd |
该命令的权限是 4755,多了一个 “s” 的权限,即 suid。chmod u-s /usr/bin/passwd
将该权限减去。
1 | [root@example ~]# ll /usr/bin/passwd |
注意,‘S’ 比 ‘s’ 少了 ‘x’ 权限
-
suid: 4 如果一个文件有 suid 的权限,那么任何人执行该文件,将有文件拥有人的权限
设置在文件上,设置在目录上无意义。
只能作用在二进制程序上(命令就是二进制的可执行程序),不能作用在脚本上。
设置方法:chmod u+s file
或者chmod 4755 file
。 -
sgid: 2 如果一个文件有 sgid 的权限,那么任何人执行该文件,将有文件拥有组的权限
对于目录而言,如果一个目录有 sgid 的权限,那么任何人在该目录下面创建文件和目录时,自动继承该目录的组。
chmod g+s dir
-
stciky: 1 如果一个目录有 sticky 权限,那么只有 root 用户和文件的拥有人能够删除该目录下的文件
设置在目录上,设置在文件上无意义
对于一个多人可写的目录,如果设置了 sticky ,则每个用户仅能删除和修改自己的文件或目录
设置方法chmod o+t dir
或者chmod 1777 dir
ACL(Access Control List) 权限:访问控制列表
设置和删除
如果一个目录的权限是
1 | drwxr-xr-x. 2 root root 6 Aug 7 18:37 data |
如果我想使 admin 用户对该目录有 rwx 权限,在不修改该目录权限和拥有人拥有组的情况下,可以通过下面的命令设置
1 | setfacl -m u:admin:rwx data/ |
此时,该目录的权限变成了
1 | drwxrwxr-x+ 2 root root 6 Aug 7 18:37 data/ |
注意这个’+'即表示该目录有设置过 acl 权限,但是这样以来,这么看它的权限是不准确的。使用
1 | [student@example tmp]$ getfacl data/ |
删除 ACL 权限:
1 | setfacl -x u:student data/ |
优先级
如果以文件拥有者的身份访问,那么文件所有者的权限适用
如果访问用户身份设置了 acl 权限,那么该用户的 acl 权限适用(只要掩码允许)
如果以文件拥有组的身份访问,或者访问用户的组设置了 acl 权限,那么则匹配相应的 acl 权限(只要掩码允许)
否则,将适用文件的其他人的权限
设置默认 ACL 权限
目的是为了确保用户在目录中创建的文件继承和目录相同的 acl 权限,setfacl -m d:u:student:rx dir
给指定组设置默认 ACL 权限:setfacl -m d:g:it:rwx dir
删除指定用户的默认 acl 权限:setfacl -x d:u:student dir
删除指定组的默认 acl 权限:setfacl -x d:g:it dir
删除所有的默认权限:setfacl -k dir
举例:
1 | [root@example tmp]# setfacl -m d:u:student:rwx data/ |
sudo 命令
sudo 是 linux 下常用的允许普通用户使用超级用户权限的工具,可以减少 root 用户的登录和管理实践,同时也提高了安全性。
sudo 的配置文件时 /etc/sudoers 和 /etc/sudoers.d/*,编辑时最好用 visudo 命令,会帮助检测语法
user MACHINE=(RunAs) NOPASSWD:COMMANDS
用户名 被管理主机=(可使用的身份) 授权命令(绝对路径)
例如:
1 | 用户 哪些权限 |
student 可以以 root 身份运行 useradd 命令
root 也可以使用 sudo命令,sudo 默认是以 root 的身份执行命令,但是 -u 选项可以选择用户
1 | [root@example tmp]# sudo -u student mkdir data |
设置 admin 组可以以 root 身份执行
%admin ALL=(root) /usr/sbin/useradd,/usr/bin/mkdir