ShellScript-技巧积累

System Unit File 的注意事项

ExecStart 指令中的命令不直接支持使用 shell 特性 (如重定向符号 >, >>, | 等), 因为 ExecStart 默认不会通过 shell 执行, 而是直接以二进制的方式调用指定的命令.

格式化 JSON 文件

1
jq hello.json

Ubuntu 查看具体库文件位置

1
dpkg -L libgtk2.0-0

库文件的查找路径为:

1
echo $LD_LIBRARY_PATH

Ubuntu 安装 deb 包

1
sudo dpkg -i your-package.deb

如果有依赖问题:

1
sudo apt-get install -f

生成数字序列

1
2
3
for i in $(seq 1 10); do
echo $i
done

set -e

set -e 能让脚本在遇到任何错误 (任何命令返回非零状态码) 时立即退出.

获取文件名不包含后缀的部分

一种是用通配符:

1
2
fileName=hello.txt
newName=${fineName%.*}
  • % 后面的模式表示从变量的值的末尾开始匹配模式并删除匹配部分

另一个方法是用 basename 命令:

1
2
fileName=hello.txt
newName=basename ${fineName} .txt

zip 过滤不打包的文件

1
zip -r ll.zip * -x@exclude.lst

exclude.lst 文件中指定要过滤的匹配项, 如:

1
2
3
hello*
world*
mm/

过滤掉以 helloworld 开头的文件, 以及 mm 目录.

Maybe, 限制用户执行的命令

1
2
3
4
5
6
7
8
9
10
11
12
13
if [ "$USER" == "test" ]; then
allowed_commands=("cd" "ls")

function restrict_commands() {
local cmd=$(echo "$BASH_COMMAND" | awk '{print $1}')
if [[ ! " ${allowed_commands[@]} " =~ " ${cmd} " ]]; then
echo "You are not allowed to run this command."
kill -SIGINT $$
fi
}

trap restrict_commands DEBUG
fi

解释几点:

  • $BASH_COMMAND 是一个 Bash 内置变量, 表示当前执行的命令, 比如脚本里若有一行 echo $BASH_COMMAND, 则输出同样为 echo $BASH_COMMAND
  • kill -SIGINT $$, 发送一个 SIGINT 信号给当前的进程, 通常是终止进程

对于 trap restrict_commands DEBUG:

  • trap 命令用于在收到指定信号 (这里是 DEBUG) 后执行一段操作 (这里是 restrict_commands)
  • DEBUG 信号, 会在执行一条命令之前触发

查看和清除 DNS 缓存

利用 DNS 缓存进行查询:

1
systemd-resolve orkarin.com

清除 DNS 缓存:

1
systemd-resolve --flush-caches

删除字体缓存并重新生成字体缓存

1
2
3
rm -rf ~/.cache/fontconfig/*
sudo rm -rf /var/cache/fontconfig/*
fc-cache -v -f

之后可查看:

1
fc-list

docker 导入 tar 包到本机的 docker 镜像仓库

1
2
3
4
5
6
7
8
9
10
11
12
[root@localhost ~]# docker load -i kdb_aarch64_V009R001C001B0025.tar
1402882e34df: Loading layer 397.6MB/397.6MB
c5cb28c6c6c7: Loading layer 320kB/320kB
56edef0e1a3a: Loading layer 7.168kB/7.168kB
bb1d5eb322ac: Loading layer 8.192kB/8.192kB
452e78297bca: Loading layer 8.192kB/8.192kB
d2140620dc0b: Loading layer 382.7MB/382.7MB
401e99b4b6d0: Loading layer 8.192kB/8.192kB
c18e61c669b9: Loading layer 5.632kB/5.632kB
323f47598d3b: Loading layer 3.072kB/3.072kB
3e4e580d9909: Loading layer 4.096kB/4.096kB
Loaded image: kingbase_v009r001c001b0025_single_arm:v1

验证文件完整性

一般用文件计算 MD5 值或者 SHA1 值来验证完整性, 如:

1
2
$ md5sum kdb_aarch64_V009R001C001B0025.tar
37da9f5f535014bf9847542f9eb96c23 kdb_aarch64_V009R001C001B0025.tar

以及:

1
2
$ sha1sum kdb_aarch64_V009R001C001B0025.tar
85681b7b1b48e23a5dd862a7fdfdbd305c4ed1d8 kdb_aarch64_V009R001C001B0025.tar

docker 拉取镜像时指定平台

1
docker pull --platform linux/arm64 gitlab/gitlab-ce:latest

docker 删除指定镜像

1
docker rmi xxx

查看 docker 镜像支持的平台

1
docker manifest inspect gitlab/gitlab-ce:latest

递归查看文件大小

1
du -ah

查看 .tar.gz 压缩包中含有哪些文件

1
tar -tzf file.tar.gz

Ubuntu 查找某命令来自哪个包

可以用 apt-file 命令来查找:

1
2
3
sudo apt install apt-file
apt-file update
apt-file search /usr/bin/ranger

判断地址冲突

arping 命令:

1
arping 10.0.0.10

若输出有多个 MAC, 说明冲突.

可用:

1
arp -n

查看本地的 arp 表内容.

指定网络接口和端口抓包

1
tcpdump -i eth1 -nn port 80
  • -i, --interface, 指定接口
  • -nn, --number, 以数字形式显示, 不将 IP 地址和端口号解析为主机名和服务名
  • port 80, 指定端口

查看响应头

1
curl -I baidu.com

输出:

1
2
3
4
5
6
7
8
9
10
11
HTTP/1.1 200 OK
Date: Mon, 15 Jul 2024 13:06:06 GMT
Server: Apache
Last-Modified: Tue, 12 Jan 2010 13:48:00 GMT
ETag: "51-47cf7e6ee8400"
Accept-Ranges: bytes
Content-Length: 81
Cache-Control: max-age=86400
Expires: Tue, 16 Jul 2024 13:06:06 GMT
Connection: Keep-Alive
Content-Type: text/html

查看与软件包相关的软件和信息

1
rpm -ql httpd

即查看 httpd 这个包内有哪些文件. -ql 是 “query list”.

1
rpm -qi httpd

查看 httpd 这个包的一些基础信息. -qi 是 “query info”

过滤出文件所有非注释以及空行

1
grep -Ev "^[ ]#|^$|^#" file.conf

显示网络接口的统计信息

1
ip -s link show wlan0
  • -s-stats, -statistics

指定某个用户权限运行

sudo -u <uid|name>, 如:

1
sudo -u jie touch hello.txt

VIM 进入 EX 模式

命令行进入为:

1
vim -e

vim 中快捷键进入如:

1
gQ

Symbolic link 的注意事项

由于 Symbolic link 的本质就是创建一个存储源文件路径信息的文件, 一次指定源文件时需要用绝对路径:

1
ln -s /home/jie/cal.pl /tmp/cal.pl

启用 vi 快捷键模式

1
set -o vi

查看服务是否启用

1
systemctl is-active firewalld.service

grep 返回匹配之外的行

如:

1
cat /etc/ansible/ansible.cfg | grep -v ^# | grep -v ^$

查看当前目录下各目录大小

1
du -sbh *

grep 显示匹配项后 n 行

如:

1
grep -A 3 "pattern" file.txt

systemctl 分析服务间的依赖

语法为:

1
systemctl list-dependencies [unit] [--reverse]

如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@localhost]# systemctl get-default
graphical.target
[root@localhost]# systemctl list-dependencies
default.target
○ ├─display-manager.service
● └─multi-user.target
● ├─auto-cpufreq.service
● ├─cronie.service
● ├─dbus-broker.service
● ├─dhcpcd.service
● ├─httpd.service
● ├─iwd.service
● ├─libvirtd.service
● ├─systemd-ask-password-wall.path
...
...

default.target 就是指 systemctl get-default 的输出, 这里是 graphical.target

通过 systemctl 观察系统上所有的服务

语法为:

1
systemctl [command] [--type=TYPE] [--all]

可用的 command 有:

  • list-units, 列出目前启动的 unit, 若加上 --all 则会同时列出没启动的
  • list-unit-files, 根据 /usr/lib/systemd/system 目录内的文件内容来列表说明

可指定的 unit type 主要有 service, socket, target.

列出系统上所有的 unit

1
systemctl

列出系统上所有的 unit 以及其状态

1
systemctl list-unit-files

将系统上所有 unit 分类后列出

1
systemctl list-units --all

若指查看某一类型, 有:

1
systemctl list-units --type=service --all

wget 继续上次中断的下载

1
wget -c https://download.rockylinux.org/pub/rocky/9.3/isos/x86_64/Rocky-9-latest-x86_64-dvd.iso
  • -c, --continue

只获取 ip 地址

1
hostname -I

调整进程优先级

使用 nicerenice 命令.

优先级从 -20 (最高), 到 19 (最低), 默认为 0.

nice 用于先设置优先级然后运行一个命令如:

1
nice -n 10 command
  • -n, --adjustment=N

renice 用于修改正在运行的进程的优先级:

1
renice -n 5 PID

cron 服务

cron 会搜索:

  • /var/spool/cron 目录下, 以 /etc/passwd 文件中用户名命名的 crontab 文件
  • /etc/crontab 文件
    (注意两个文件语法略有不同, 前者不需要加执行用户)

常用选项:

  • -u, 指定一个用户, 同时指定一个 crontab 文件来运行
  • -e, “edit”, 编辑当前用户的 crontab
  • -l, “list”, 列出当前用户的 cron 服务详细内容
  • -r, “remove”, 删除当前用户的 crontab

at 命令指定时间以及运行

指定时间不需要开启任何选项, 可以用 -f “file” 从文件中读取要执行的命令或者从 STDIN 读取:

1
2
3
at -f run.sh 12:00 today
at -f run.sh now+4 hours
at -f run.sh 12:00 6/7/12

6/7/12M/D/Y. 相对计时还可以为 minutes, hours, days, weeks, today.

查看 job 列表:

1
at -l

(这里不会显示出具体的命令, 但会有 job 编号)

查看某个 job 要执行的具体命令:

1
at -c job_id

-c 可能指 “command”.

移除一个 job 可以为:

1
at -r job_id

-r 可能指 “remove”.

at.allowat.deny 控制 at 的执行权限, 文件内容为一行一个用户名, 如:

1
2
jie
orkarin

字符转译

tr 命令, 如将小写转换为大写:

1
echo "hello world" | tr a-z A-Z

wc 常见选项

  • -l, --lines, 只统计行数
  • -w, --words, 统计单词数

提取列或字段

cut 命令, 常用选项有:

  • -d, --delimiter=DELIM 指定分隔符, 如指定 : 则为 -d:
  • -f, --fields=LIST, 显示指定的 filed, 如 -f 1,2

修改文件时间戳

格式:

1
touch [option] -t YYYYMMDDHHMM.SS file

如同时更改访问时间和修改时间:

1
touch -t 202203151430.00 example.txt

只更改访问时间:

1
touch -a -t 202203151430.00 example.txt
  • -a 指 “access”

只更改修改时间:

1
touch -m -t 202203151430.00 example.txt
  • -m, 指 “modify”

查看当前主机的 ssh 连接数

1
netstat -ntpla | grep ssh

查看与 ssh 相关的线程

1
ps -eTf | grep ssh

引用另一个脚本的内容

可以用 source, 或者直接运行脚本, 如 test1.sh 文件为:

1
2
user=$(whoami)
directory=$(pwd)

test2.sh 中调用 test1.sh 文件中定义的变量:

1
2
3
source ./test1.sh
echo $user
echo $directory

函数创建

两种格式:

  • 第一种格式采用关键字 function:

    1
    2
    3
    function name {
    commands
    }
  • 第二种格式:

    1
    2
    3
    name() {
    commands
    }

    函数名后的空括号表明正在定义一个函数.

注意函数调用时不能加上括号.

tee 命令使用

tee 命令能将输入同时输入到标准输出以及一个或多个文件中. (默认会覆盖原文件内容)

常用参数:

  • -a, 追加到文件中
  • -i, 忽略中断信号
  • -p, 允许继续使用管道
1
2
3
4
ls -l | tee output.txt
echo "Hello, World!" | tee -a output.txt
ls | tee file1.txt file2.txt
cat file.txt | tee -p | grep "keyword"

创建临时文件

mktemp:

1
tempfile=$(mktemp test.XXXXXX)

加上 -t 参数会强制在系统的临时目录创建并返回绝对路径.

同时重定向 STDOUT 和 STDERR

使用 &> 这个符号.

结合分隔符和 for 语句

1
2
3
4
5
6
7
8
9
10
11
12
IFS=:
for folder in $PATH
do
echo "$folder:"
for file in $folder/*
do
if [ -x $file ]
then
echo " $file"
fi
done
done

在命令行查看 bash shellscript 的语法

1
man bash

C 语言风格的 for 循环

1
2
3
4
for (( variable assignment ; condition ; iteration process ))
do
...
done

区分 [], [[]] 和 (()) 的作用

[]

[ condition ] 等价于 test condition 命令, condition 成立则返回 0.

[[]]

[[ condition ]], 其也用于条件测试, 但是相比 [ condition ] 支持逻辑运算符以及模式匹配等.

常用字符串比较如:

1
2
3
4
5
[[ "$string1" == "$string2" ]]
[[ "$string1" != "$string2" ]]
[[ "$string" =~ pattern ]]
[[ -z "$string" ]]
[[ -n "$string" ]]

(())

(( 1+1 )) 用于数学运算和数字比较, 如:

1
test=$((1 + 1))

常用数字比较有:

1
2
3
4
5
6
((num1 == num2))
((num1 != num2))
((num1 > num2))
((num1 < num2))
((num1 >= num2))
((num1 <= num2))

命令替换,变量内插和数学运算语句的区别

命令替换为 $(command).

变量内插为 ${var}.

数学运算为 $[1+1]

! 系列命令

  • !!, 执行上一条命令
  • !n, 执行历史记录中的第 n 条命令
  • !string, 执行最近的以指定字符串开头的命令

常见特殊变量

  • $0, 脚本名
  • $1,$2… 脚本参数
  • $@, 以数组形式包含全部参数
  • $#, 参数长度
  • $?, 最后一个命令的运行状态
  • $$, 当前脚本的进程 ID
  • $!, 最后一个在后台运行的进程的进程 ID
  • $*, 以字符串形式包含全部参数
  • $IFS, 内部字段分隔符

花括号扩展语法

在这种语法中, 花括号内部的内容会被扩展成多个字符串, 每个字符串通过逗号分隔.

如创建两个不同后缀的文件, 可以写为:

1
touch hello.{c,pl}

其会创建 hello.chello.pl 两个文件.

或创建连续数字:

1
touch hello{1..3}

其会创建 hello1, hello2, hello3 三个文件.

显示数组的所有元素

1
2
arr=("hello" "shell" "array")
echo ${arr[*]}

删除数组元素

unset, 元素被删除后, 后面的元素不会自动往前挪:

1
2
3
4
arr=("hello" "shell" "array")
unset arr[1]
echo ${arr[1]}
echo ${arr[*]}

此时 ${arr[1]} 没有对应值.

列出所有普通用户

1
cut -d: -f1 /etc/passwd

直接 enable 并启动应用

如:

1
sudo systemctl enable --now dhcpd

直接修改 hostname 以及时区

1
2
3
hostnamectl set-hostname pxe-test.net
timedatectl set-timezone Asia/Shanghai
chronyc sources

Firewalld

Firewalld 是基于 Netfilter 框架的动态防火墙管理器,它使用 iptables 作为底层的数据包过滤引擎。Firewalld 提供了一个抽象的、高级的防火墙管理接口,并通过使用 iptables 或其他后端工具来实际修改底层的防火墙规则。

nmcli 命令

其为 NetWorkManager 的命令行工具.

挂载 ISO 镜像

如:

1
mount -t iso9660 -o loop,ro Fedora-Server-dvd-x86_64-38-1.6.iso /var/www/html/pxe/fedora38

-t (type) 指定文件系统类型.

-o (options) 指定挂载的参数, loop 选项表示将文件作为块设备挂载,ro 选项表示以只读模式挂载

变量内插

直接写成 ${Varname} 就可以, 如:

1
2
3
Test="A string"
Haha="${Test}"
echo ${Haha}

更换重定向输出

如:

1
$ mycommand > myoutputfile 2>&1

将标准错误输出(stderr)重定向到标准输出(stdout),也就是将标准错误输出和标准输出合并为一起输出。

具体地说,2 表示标准错误输出的文件描述符,> 表示重定向输出,&1 表示将输出重定向到标准输出的文件描述符上,其中 & 表示这是一个文件描述符,而不是一个文件名。

注意这里的 2>&1 放命令末尾.


ShellScript-技巧积累
http://example.com/2023/01/28/ShellScript-技巧积累/
作者
Jie
发布于
2023年1月28日
许可协议