shell 编程


  1. 绝对路径 /root/scripts/file.sh #要有执行权限
  2. 相对路径 ./file.sh #要有执行权限
  3. bash + 脚本名 #不需要执行权限和编译
  4. source 脚本名 #脚本中的变量会在当前 shll 生效

前面三种是在子 shell 中执行,第四种是在当前 shell 执行

$[],中括号里面可以进行运算

位置化参数

$0 脚本名
$1 第1个参数
$* 所有的参数,双引号引起来时参数视为单个字符串
$@ 所有的参数,双引号引起来时,每个参数作为一个个体
$# 参数的个数
$$ 当前进程的 PID
$? 上一个命令的返回值,0表示成功

read

从命令行中输入字符串,赋值给一个变量

1
read -p "Enter your name:" NAME
1
2
3
[student@example Documents]$ read -p "Enter your name:" NAME
Enter your name:zhangsan
[student@example Documents]$

不加变量名,默认赋值给 REPLY 变量。

-t 超时时间(秒)

exit 退出码

程序执行后会携带一个退出码
0 代表成功,1-255 代表失败;储存在系统变量 $? 中;exit num 可以中断程序并设置退出码为 num

例如,判断 /etc/profile 文件里是否有关键字 HISTSIZE

1
2
3
4
5
[student@example Documents]$ grep HISTSIZE /etc/profile
HISTSIZE=1000
export PATH USER LOGNAME MAIL HOSTNAME HISTSIZE HISTCONTROL
[student@example Documents]$ echo $?
0

条件执行运算符

&& 代表逻辑与,前一个命令执行成功会执行后一个
|| 代表逻辑或,前一个命令执行成功,后面的命令不会再执行

布尔运算符

! 非运算
-o 或运算
-a 与运算

数值运算符

判断变量是不是数字:

[["$num10" =~ ^[0-9]+$ ]]

ps: shell中各种括号的作用()、(())、[]、[[]]、{}

字符串运算符

= 检测两个字符串是否相等。 等于返回 0 [ $a = $b ]
!= 检测两个字符串是否相等。等于返回 1 [ $a != $b ]

[ -n “$A” ] 判断变量是否定义

条件判断

1
2
3
4
if 条件表达式
then
指令
fi
1
2
3
if 条件表达式;then
指令
fi
1
2
3
4
5
if 条件表达式;then
指令1
else
指令2
fi
1
2
3
4
5
6
7
if 条件表达式1;then
指令1
elif 件表达式2;then
指令2
else
指令3
fi

case语句

1
2
3
4
5
6
7
8
9
10
11
case 变量名 in
值1)
指令1
;;
值2)
指令2
;;
*)
指令4
;;
esac

for循环

直接列出元素

1
2
3
4
for i in 1 3 5
do
指令
done

使用大括号

1
2
3
4
for i in {1...5}
do
指令
done

使用 seq

1
2
3
4
for i in $(seq 1 5)
do
指令
done

使用命令的结果

1
2
3
4
for i in $(ls *.sh)
do
指令
done

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
2
3
  *     *     *     *     *       echo hello
分钟 小时 日 月 星期 具体的任务
0-59 0-23 1-31 1-12 0-7

简记:分,时,日,月,周

如果是同一位置的两个时间点,就以逗号隔开

举例

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 仅允许

系统的计划任务

  1. crond 服务管理

使用 /etc/crontab 文件创建的计划任务,一般只有系统管理员才能操作该文件

还可以写在 /etc/cron.d/ 文件夹下的文件里。 /etc/cron.hourly/ 中也有

建议不要使用命令,而是使用该文件创建计划任务

  1. 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
2
3
4
5
find /home -user admin
find /home -group root
find /home -uid 1001
find /home -nouser # 无属主的文件
find /home -nogroup # 无属组的文件

根据文件大小查找

-size [+|-] #UNIT

UNIT 表示常用单位:k, M, G,#表示给出的具体数值

#UNIT 对应 (#-1, #)
-#UNIT 对应 [0, #-1]
+#UNIT 对应 (#, \infty)

5M 对应 (4M, 5M]
-5M 对应 (0, 4M]
+5M 对应 (5M, \infty)

例如 find /tmp -size 5M


根据文件的权限查询

-perm

MODE 精确匹配
/MODE u, g, o 任何一类对象只要有一位匹配中即可
-MODE 每一类对象都必须同时拥有指定的权限才可匹配
0 表示不关注

1
2
3
4
5
6
find -perm 644  严格匹配 644 的文件权限
find -perm /222 u, g, o 任何一类对象有写权限即匹配
find -perm -222 严格匹配写权限,即每个用户必须要有写权限
find -perm -002 严格匹配 other 用户的写权限
find -perm /600 u 有 r 或 w 权限即可匹配
find -perm -600 u 必须有 rw 权限才可匹配

根据文件时间戳查找

以“天”为单位

-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. 创建特定大小的文件
1
dd if=/dev/zero of=/dev/file.txt bs=1M count=100
  1. 对磁盘进行备份(字节级别的拷贝)
1
dd if=/dev/nvme0n1 of=/dev/nvme0n4
  1. 抹除磁盘数据
1
dd if=/dev/zero of=/dev/nvme0n4
  1. 测试底层存储的速率

  2. 对分区备份

1
dd if=/dev/nvme0n1p1 of=boot.bak

恢复

1
dd if=/boot.bak of=/dev/nvme0n1p1

网络客户端


curl 命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
curl url (获取该网址的文本信息)

curl -i url (获取该网址的文本信息以及协议头部信息)

curl -x proxy url 使用代理获取网页文本信息


curl -o bilibili.html bilibili.com 将网页内容保存为本地的 bilibili.html

curl -O http://www.linux.com/hello.sh 下载文件

curl -o dodo1.jpg http://www.linux.com/dodo1.JPG 下载文件并保存为 dodo1.jpg

curl -O -u 用户名:密码 ftp://www.linux.com/dodo1.JPG

curl -oO ftp://用户名:密码@www.linux.com/dodo1.JPG

wget 命令,上传和下载文件

  1. wget url 下载文件
  2. wget -O 文件名 url 下载之后改名
  3. wget -b 后台下载
  4. wget --spider http://www.linux.com/dodo1.JPG 解析能否下载但不会下载
  5. 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
2
ssh-agent bash  # 开启认证代理
ssh-add # 将私钥密码添加到认证代理

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
2
[root@example ~]# ll /etc/shadow
----------. 1 root root 1616 Jul 18 13:42 /etc/shadow

会发现用户对该文件没有任何权限,但是用户修改密码的时候,确实是修改了该文件,怎么实现呢?

passwd 这个命令在这里

1
2
[root@example ~]# ll /usr/bin/passwd
-rwsr-xr-x. 1 root root 34512 Aug 13 2018 /usr/bin/passwd

该命令的权限是 4755,多了一个 “s” 的权限,即 suid。chmod u-s /usr/bin/passwd 将该权限减去。

1
2
3
4
5
6
7
8
9
[root@example ~]# ll /usr/bin/passwd
-rwsr-xr-x. 1 root root 34512 Aug 13 2018 /usr/bin/passwd
[root@example ~]# chmod u-s /usr/bin/passwd
[root@example ~]# ll /usr/bin/passwd
-rwxr-xr-x. 1 root root 34512 Aug 13 2018 /usr/bin/passwd
[root@example ~]# chmod u-x /usr/bin/passwd
[root@example ~]# chmod u+s /usr/bin/passwd
[root@example ~]# ll /usr/bin/passwd
-rwSr-xr-x. 1 root root 34512 Aug 13 2018 /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
2
3
4
5
6
7
8
9
[student@example tmp]$ getfacl data/
# file: data/
# owner: root
# group: root
user::rwx # 拥有人的权限
user:student:rwx # 单独给用户 student 设置的权限
group::r-x # 拥有组的权限
mask::rwx # mask 值表示该目录的最大权限
other::r- # 其他人的权限

删除 ACL 权限:

1
2
3
4
setfacl -x u:student data/
setfacl -x g:it data/
setfacl -b data/ # 清空所有的 acl 权限
setfacl -R data/ # 递归修改 acl 权限

优先级

如果以文件拥有者的身份访问,那么文件所有者的权限适用

如果访问用户身份设置了 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
2
3
4
5
6
7
8
9
10
11
12
13
[root@example tmp]# setfacl -m d:u:student:rwx data/
[root@example tmp]# getfacl data/
# file: data/
# owner: root
# group: root
user::r-x
group::r-x
other::r-x
default:user::r-x
default:user:student:rwx
default:group::r-x
default:mask::rwx
default:other::r-x

sudo 命令

sudo 是 linux 下常用的允许普通用户使用超级用户权限的工具,可以减少 root 用户的登录和管理实践,同时也提高了安全性。

sudo 的配置文件时 /etc/sudoers 和 /etc/sudoers.d/*,编辑时最好用 visudo 命令,会帮助检测语法

user MACHINE=(RunAs) NOPASSWD:COMMANDS

用户名 被管理主机=(可使用的身份) 授权命令(绝对路径)

例如:

1
2
3
用户                    哪些权限
root ALL=(ALL) ALL
student ALL=(root) /usr/sbin/useradd

student 可以以 root 身份运行 useradd 命令

root 也可以使用 sudo命令,sudo 默认是以 root 的身份执行命令,但是 -u 选项可以选择用户

1
2
3
[root@example tmp]# sudo -u student mkdir data
[root@example tmp]# ll -d data
drwxr-xr-x. 2 student student 6 Aug 7 21:09 data

设置 admin 组可以以 root 身份执行

%admin ALL=(root) /usr/sbin/useradd,/usr/bin/mkdir