
正则表达式的应用
在 grep 中的使用正则表达式
grep 命令在 Linux 系统中是一个用于查找文本内容的命令,其中为了更好更加方便地定位文本内容,grep 命令中支持正则表达式查找文本。
在正式讲正则表达式之前,我们先讲一下 grep 的进阶用法。
例如,我们需要查找在 /etc/passwd 文件中 shibo 用户所在哪一行。我们可以使用 -n 选项。
shibo@DESKTOP-U7FRP5M:~/spiit$ grep -n shibo /etc/passwd
27:shibo:x:1000:1000:,,,:/home/shibo:/bin/bash当使用 -n 选项后,输出的内容前会出现该行所在文档中所处的行数,这样我们就可以更加方便的定位数据。
有时候我们需要查看开机自检信息的内容,使用 dmesg 命令可以查看开机自检的内容。由于直接使用 dmesg 命令输出的内容太多,我们需要使用 grep 命令搜索我们需要的内容。
shibo@DESKTOP-U7FRP5M:~/spiit$ dmesg
[ 0.000000] Linux version 5.15.133.1-microsoft-standard-WSL2 (root@1c602f52c2e4) (gcc (GCC) 11.2.0, GNU ld (GNU Binutils) 2.37) #1 SMP Thu Oct 5 21:02:42 UTC 2023
[ 0.000000] Command line: initrd=\initrd.img WSL_ROOT_INIT=1 panic=-1 nr_cpus=8 bonding.max_bonds=0 dummy.numdummies=0 fb_tunnels=none console=hvc0 debug pty.legacy_count=0
[ 0.000000] KERNEL supported cpus:
[ 0.000000] Intel GenuineIntel
[ 0.000000] AMD AuthenticAMD
————————————以下内容省略————————————现在我们需要输出我们系统中 eth0 网卡的相关内容。
shibo@DESKTOP-U7FRP5M:~/spiit$ dmesg | grep -n eth0
390:[ 6.888925] IPv6: ADDRCONF(NETDEV_CHANGE): eth0: link becomes ready
443:[ 79.586230] TCP: eth0: Driver has suspect GRO implementation, TCP performance may be compromised.两条记录分别位于 390 行和 443 行中,为了更好的了解这两行的具体意思,我们需要输出包括这几行的前 4 行和后 4 行。
shibo@DESKTOP-U7FRP5M:~/spiit$ dmesg | grep -n eth0
390:[ 6.888925] IPv6: ADDRCONF(NETDEV_CHANGE): eth0: link becomes ready
443:[ 79.586230] TCP: eth0: Driver has suspect GRO implementation, TCP performance may be compromised.
shibo@DESKTOP-U7FRP5M:~/spiit$ dmesg | grep -n -A 3 -B 4 -i eth0
386-[ 0.587575] sd 0:0:0:1: [sdb] Mode Sense: 0f 00 00 00
387-[ 0.588423] sd 0:0:0:1: [sdb] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA
388-[ 0.591034] sd 0:0:0:1: [sdb] Attached SCSI disk
389-[ 0.627938] Adding 2097152k swap on /dev/sdb. Priority:-2 extents:1 across:2097152k
390:[ 6.888925] IPv6: ADDRCONF(NETDEV_CHANGE): eth0: link becomes ready
391-[ 7.023048] hv_pci 3c9ddda8-257a-4f62-ba26-16ecd5ddbe09: PCI VMBus probing: Using version 0x10004
392-[ 7.027081] hv_pci 3c9ddda8-257a-4f62-ba26-16ecd5ddbe09: PCI host bridge to bus 257a:00
393-[ 7.027851] pci_bus 257a:00: root bus resource [mem 0xc00000000-0xe00001fff window]
--
439-[ 71.788971] misc dxg: dxgk: dxgkio_query_adapter_info: Ioctl failed: -22
440-[ 71.794064] misc dxg: dxgk: dxgkio_query_adapter_info: Ioctl failed: -2
441-[ 72.622976] systemd-journald[45]: Received client request to flush runtime journal.
442-[ 78.604096] WSL (2): Creating login session for shibo
443:[ 79.586230] TCP: eth0: Driver has suspect GRO implementation, TCP performance may be compromised.
444-[ 4673.993794] hv_utils: TimeSync IC version 4.0
445-[ 5282.082469] scsi 0:0:0:3: Direct-Access Msft Virtual Disk 1.0 PQ: 0 ANSI: 5
446-[ 5282.090204] sd 0:0:0:3: Attached scsi generic sg3 type 0练习:
查看 grep 的 man 文档,在其中寻找 inver 字段,查看具体的选项和解释。
输入
df -h命令,将有关 none 的行去掉,用以查看我们需要的内容。CODEshibo@DESKTOP-U7FRP5M:~/spiit$ df -h Filesystem Size Used Avail Use% Mounted on none 3.9G 4.0K 3.9G 1% /mnt/wsl none 247G 207G 40G 84% /usr/lib/wsl/drivers /dev/sdc 251G 1.8G 237G 1% / none 3.9G 84K 3.9G 1% /mnt/wslg none 3.9G 0 3.9G 0% /usr/lib/wsl/lib rootfs 3.9G 2.1M 3.9G 1% /init none 3.9G 848K 3.9G 1% /run none 3.9G 0 3.9G 0% /run/lock none 3.9G 400M 3.5G 11% /run/shm tmpfs 4.0M 0 4.0M 0% /sys/fs/cgroup none 3.9G 76K 3.9G 1% /mnt/wslg/versions.txt none 3.9G 76K 3.9G 1% /mnt/wslg/doc C:\ 247G 207G 40G 84% /mnt/c D:\ 557G 253G 304G 46% /mnt/d snapfuse 128K 128K 0 100% /snap/bare/5 snapfuse 74M 74M 0 100% /snap/core22/864 snapfuse 75M 75M 0 100% /snap/core22/1380 snapfuse 41M 41M 0 100% /snap/snapd/20290 snapfuse 92M 92M 0 100% /snap/gtk-common-themes/1535 snapfuse 39M 39M 0 100% /snap/snapd/21759 snapfuse 132M 132M 0 100% /snap/ubuntu-desktop-installer/1276 snapfuse 132M 132M 0 100% /snap/ubuntu-desktop-installer/1286 shibo@DESKTOP-U7FRP5M:~/spiit$ df -h | grep -v none Filesystem Size Used Avail Use% Mounted on /dev/sdc 251G 1.8G 237G 1% / rootfs 3.9G 2.1M 3.9G 1% /init tmpfs 4.0M 0 4.0M 0% /sys/fs/cgroup C:\ 247G 207G 40G 84% /mnt/c D:\ 557G 253G 304G 46% /mnt/d snapfuse 128K 128K 0 100% /snap/bare/5 snapfuse 74M 74M 0 100% /snap/core22/864 snapfuse 75M 75M 0 100% /snap/core22/1380 snapfuse 41M 41M 0 100% /snap/snapd/20290 snapfuse 92M 92M 0 100% /snap/gtk-common-themes/1535 snapfuse 39M 39M 0 100% /snap/snapd/21759 snapfuse 132M 132M 0 100% /snap/ubuntu-desktop-installer/1276 snapfuse 132M 132M 0 100% /snap/ubuntu-desktop-installer/1286将该命令定义一个别名 df2,以后需要用到该功能时直接输入 df2 这个别名就行了。
CODEshibo@DESKTOP-U7FRP5M:~/spiit$ alias df2='df -h | grep -v none' shibo@DESKTOP-U7FRP5M:~/spiit$ df2 Filesystem Size Used Avail Use% Mounted on /dev/sdc 251G 1.8G 237G 1% / rootfs 3.9G 2.1M 3.9G 1% /init tmpfs 4.0M 0 4.0M 0% /sys/fs/cgroup C:\ 247G 207G 40G 84% /mnt/c D:\ 557G 253G 304G 46% /mnt/d snapfuse 128K 128K 0 100% /snap/bare/5 snapfuse 74M 74M 0 100% /snap/core22/864 snapfuse 75M 75M 0 100% /snap/core22/1380 snapfuse 41M 41M 0 100% /snap/snapd/20290 snapfuse 92M 92M 0 100% /snap/gtk-common-themes/1535 snapfuse 39M 39M 0 100% /snap/snapd/21759 snapfuse 132M 132M 0 100% /snap/ubuntu-desktop-installer/1276 snapfuse 132M 132M 0 100% /snap/ubuntu-desktop-installer/1286
正则表达式的符号意义
正则表达式的符号众多,这边先列出常用的的几个正则符号
| 符号 | 含义及示例 |
|---|---|
| ^word | 表示匹配字符串(word)在行首。示例 grep -n '^#' /etc/hosts |
| word$ | 表示匹配字符串(word)在行尾。示例 grep -n 's$' /etc/hosts |
| . | 表示匹配任意一个字符。示例 grep -n 'ip.' /etc/hosts |
| \ | 转意字符,将某些具有特定含义的符号转化为普通字符串,例如匹配字符 ’ grep -n \ /etc/hosts' |
| * | 匹配在该符号前零次到无穷多次字符,例如 grep -n 'ess*' /etc/hosts 匹配 es,ess,essss等字符 |
| [list] | 表示匹配字符合集中的任意一个字符,例如 grep -n 'a[fsy]g' /etc/hosts 表示匹配 afg,asg,ayg 字符。 |
| [n1-n2] | 表示匹配字符集合中n1到n2字符之间任意个字符(这个连续与 ascll 有关,因此如何在匹配时出现问题因该查看系统变量中的LANG变量),例如:grep -n 'a[b-g]' /etc/hosts 表示匹配 ab |
| [^list] | 表示不匹配字符集中的字符,例如:grep -n 'a[^bcd]g' /etc/hosts 表示匹配 afg,asg,aeg,aag字符。 |
| {n1,n2} | 表示匹配前一个字符n1到n2个连续的字符,例如:grep -n 'go\{1,3\}' test.txt 表示匹配 gooo 字符。 |
除了上面提到的符号,还有一些特殊字符。
| 特殊符号 | 代表意义 |
|---|---|
| [:alnum:] | 代表英文大小写字元及数字,亦即0-9, AZ, az |
| [:alpha:] | 代表任何英文大小写字元,亦即AZ, az |
| [:blank:] | 代表空白键与[Tab] 按键两者 |
| [:cntrl:] | 代表键盘上面的控制按键,亦即包括CR, LF, Tab, Del.. 等等 |
| [:digit:] | 代表数字而已,亦即0-9 |
| [:graph:] | 除了空白字元(空白键与[Tab] 按键) 外的其他所有按键 |
| [:lower:] | 代表小写字元,亦即az |
| [:print:] | 代表任何可以被列印出来的字元 |
| [:punct:] | 代表标点符号(punctuation symbol),亦即:" ’ ? ! ; : # $… |
| [:upper:] | 代表大写字元,亦即AZ |
| [:space:] | 任何会产生空白的字元,包括空白键, [Tab], CR 等等 |
| [:xdigit:] | 代表16 进位的数字类型,因此包括: 0-9, AF, af 的数字与字元 |
练习:
找出/etc/services 内含http 关键字的那几行
CODEshibo@DESKTOP-U7FRP5M:~$ grep -n 'http' /etc/services 3:# Updated from https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml . 39:http 80/tcp www # WorldWideWeb HTTP 83:https 443/tcp # http protocol over TLS/SSL 84:https 443/udp # HTTP/3 263:http-alt 8080/tcp webcache # WWW caching service承上,若仅须『开头含有http 』字样的那几行?
CODEshibo@DESKTOP-U7FRP5M:~$ grep -n '^http[^s-]' /etc/services 39:http 80/tcp www # WorldWideWeb HTTP承上,若仅须『开头含有http 或https 』字样的那几行?
CODEshibo@DESKTOP-U7FRP5M:~$ grep -n '^http[^-]' /etc/services 39:http 80/tcp www # WorldWideWeb HTTP 83:https 443/tcp # http protocol over TLS/SSL 84:https 443/udp # HTTP/3承上,若仅须『开头含有http 或https 』字样之外,且后面仅能接空白字元或[tab] 字元的那几行?
CODEshibo@DESKTOP-U7FRP5M:~$ grep -n '^http[^-][[:blank:]]' /etc/services 39:http 80/tcp www # WorldWideWeb HTTP 83:https 443/tcp # http protocol over TLS/SSL 84:https 443/udp # HTTP/3承上,若仅须『开头含有http 且后续接有80 』字样的那几行
CODEshibo@DESKTOP-U7FRP5M:~$ grep -n '^http[^-][[:blank:]]80' /etc/services 39:http 80/tcp www # WorldWideWeb HTTP找出/etc/services 内含有星号(*) 的那几行
找出/etc/services 内含有星号,且星号前为英文(不论大小写)的那几行
找出/etc/services 含有一个数字且后面紧邻一个大写字元的那几行
找出/etc/services 开头是一个数字紧邻一个大写字元的那几行
使用find /etc 找出档名,并找出结尾含有『 .conf 』的档名资料
CODEshibo@DESKTOP-U7FRP5M:~$ find /etc -name '*.conf' /etc/rsyslog.conf /etc/systemd/timesyncd.conf /etc/systemd/resolved.conf /etc/systemd/journald.conf /etc/systemd/system/systemd-binfmt.service.d/00-wsl.conf /etc/systemd/system/systemd-sysusers.service.d/00-wsl.conf /etc/systemd/system.conf /etc/systemd/logind.conf /etc/systemd/pstore.conf承上,且含有『大写字元或数字』档名在内的那几个
CODEshibo@DESKTOP-U7FRP5M:~$ find /etc -name '*.conf' | grep '[[:digit:],[:upper:]]' find: ‘/etc/polkit-1/localauthority’: Permission denied /etc/systemd/system/systemd-binfmt.service.d/00-wsl.conf /etc/systemd/system/systemd-sysusers.service.d/00-wsl.conf /etc/dbus-1/system.d/com.ubuntu.SoftwareProperties.conf /etc/dbus-1/system.d/org.freedesktop.PackageKit.conf /etc/polkit-1/localauthority.conf.d/50-localauthority.conf /etc/polkit-1/localauthority.conf.d/51-ubuntu-admin.conf find: ‘/etc/ssl/private’: Permission denied /etc/apt/apt.conf.d/20apt-esm-hook.conf /etc/apt/apt.conf.d/20snapd.conf /etc/rsyslog.d/20-ufw.conf /etc/rsyslog.d/50-default.conf /etc/sysctl.d/10-ptrace.conf
使用 sed 工具
使用 sed 工具实验信息选取。
sed 是一个流编辑器,它时文本处理中非常重要的工具,并且也支持完整的正则表达式,熟练使用 sed 命令可以极大提升工作效率。这里介绍一下该命令的基本用法,详细使用方法 点这里
sed 命令的格式如下:
sed ’s/旧文本内容/新文本内容/g‘ 文档路径旧文本内容必须指定,新本文内容如果忽略则表示替换为空。依据这个特性,我们用来代替 awk '{print $@}'命令。现在,我们使用这f命令来显示 eth0 网卡中的 IPv4 地址。
首先使用 ifconfig eth0 和 grep 命令提取出 IPv4 地址行。
shibo@DESKTOP-U7FRP5M:~$ ifconfig eth0 | grep 'inet[[:space:]]'
inet 172.27.254.212 netmask 255.255.240.0 broadcast 172.27.255.255然后,我们使用 sed 命令将 IPv4 地址前的 inet 字段去除。
shibo@DESKTOP-U7FRP5M:~$ ifconfig eth0 | grep 'inet[[:space:]]' | sed 's/^.*inet[[:space:]]//g' # 这里 sed 命令的意识是,将 inet 及其前面的空额替换为空。
172.27.254.212 netmask 255.255.240.0 broadcast 172.27.255.255现在,我们将 IPv4 地址后的 netmask 及其后面的字段去除。
shibo@DESKTOP-U7FRP5M:~$ ifconfig eth0 | grep 'inet[[:space:]]' | sed 's/^.*inet[[:space:]]//g' | sed 's/[[:space:]]netmask.*//g' # 最后一个 sed 的意思是,将 netmask 及其后面的所有字段替换为空
172.27.254.212现在提取出来的就是一个纯粹的 IPv4 地址。
使用 sed 命令替换文件中的内容。
使用 sed 命令,可以直接替换掉源文件中的文本内容。例如,现在将 test .txt.文档 “ess” 小写字母替换为大写字母。
shibo@DESKTOP-U7FRP5M:~$ cat test.txt
find: ‘/etc/polkit-1/localauthority’: Permission denied
/etc/shadow-
/etc/shadow
find: ‘/etc/ssl/private’: Permission denied
/etc/gshadow-
/etc/gshadow
gooooooooogle
gadbec def
essss
ess
shibo@DESKTOP-U7FRP5M:~$ sed 's/ess/ESS/g' test.txt
find: ‘/etc/polkit-1/localauthority’: Permission denied
/etc/shadow-
/etc/shadow
find: ‘/etc/ssl/private’: Permission denied
/etc/gshadow-
/etc/gshadow
gooooooooogle
gadbec def
ESSss
ESS注意:在不加任何选择时,我们修改的内容只会存储到缓冲区,并不会直接写入文件。如果你需要将替换的内容写入到文件内,那么我们可以使用 -i 选项。
shibo@DESKTOP-U7FRP5M:~$ sed -i 's/ess/ESS/g' test.txt
shibo@DESKTOP-U7FRP5M:~$ cat test.txt
find: ‘/etc/polkit-1/localauthority’: Permission denied
/etc/shadow-
/etc/shadow
find: ‘/etc/ssl/private’: Permission denied
/etc/gshadow-
/etc/gshadow
gooooooooogle
gadbec def
ESSss
ESS除了替换内容外,sed 命令还能指定输出行的范围。例如,现在要输出 /etc/passwd 文件中 10-15 行的内容。
shibo@DESKTOP-U7FRP5M:~$ cat -n /etc/passwd | sed -n '10,15p'
10 news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
11 uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
12 proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
13 www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
14 backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
15 list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin练习;
应该要用grep 去找出/etc/passwd 里面,结尾是bash 的那几行
CODEshibo@DESKTOP-U7FRP5M:~$ grep 'bash$' /etc/passwd root:x:0:0:root:/root:/bin/bash shibo:x:1000:1000:,,,:/home/shibo:/bin/bash承上,透过sed 将/bin/bash 改成/sbin/nologin 显示到萤幕上
CODEshibo@DESKTOP-U7FRP5M:~$ grep 'bash$' /etc/passwd | sed 's/\/bin\/bash/\/sbin\/nologin/g' root:x:0:0:root:/root:/sbin/nologin shibo:x:1000:1000:,,,:/home/shibo:/sbin/nologin承上,透过tr 这个指令,将全部的英文都变成大写字元
CODEshibo@DESKTOP-U7FRP5M:~$ grep 'bash$' /etc/passwd | sed 's/\/bin\/bash/\/sbin\/nologin/g' | tr [a-z] [A-Z] ROOT:X:0:0:ROOT:/ROOT:/SBIN/NOLOGIN SHIBO:X:1000:1000:,,,:/HOME/SHIBO:/SBIN/NOLOGIN
shell 脚本
shell 脚本相当于一组命令的集合,依据自己的需求将需要执行的命令和判断条件写进 shell 脚本中,可以实现一些复杂的功能。在实际项目中 shell 脚本还可以用来进行自动化。
shell 脚本编写基础
shell 脚本的特性与大部分编程语言类似,因此理解起来并不复杂。
- Shell 脚本执行是由上到下,由左到右执行的
- 脚本开头需要声明所使用的 shell 环境。如比常用的
#!/bin/bash。这表示使用 bash shell 环境。 - 命令之前的多余的空格和空行会被忽略。
- 当检测到 Enter (CR)换行符时,执行该行所出现的命令。
- 内容过多需要换行继续写时,可以使用
\[Enter]符号来表示延伸至下一行。 #在 shell 脚本中为注释的意思,注释不会被执行,只有说明或解释的作用。
执行 shell 脚本有两种方式,分别为:
- 直接命令下达,脚本必须具有 x 可执行权限。
- 绝对路径执行: 通过输入脚本的绝对路径来执行。例如:/home/shibo/myid.sh
- 相对路径执行: 当与脚本同目录时,可以通过 输入脚本的相对路径执行脚本。例如:./myid.sh
- 直接输入脚本名执行: 当脚本位于全局变量 【PATH】 中所指向的目录时,可以直接输入脚本名称来执行脚本。例如:myid.sh
- 通过 bash 程序执行:使用
bash <脚本名>或sh <脚本名>等命令来执行脚本。
现在,我们使用普通用户,在 /bing 目录创建 shell 脚本来进行练习。脚本要求如下:
- 输出用户的 id 信息
- 输出用户的家目录。
- 输出用户的历史命令记录条数。
- 列出当前用户下所有的别名命令。
依据上面的要求,我们的脚本大体上因该如下:
#!/bin/bash
# This script will use id, echo to show account's messages
# write by VBird 2016/04/27
echo "This script will show your accout messages."
echo "The 'id' command output is: "
id # 用以输出用户的 id 信息
echo "your user's home is: ${HOME}" # 显示用户的家目录
echo "your history record: ${HISTSIZE}" # 显示用户的历史命令记录条数
echo "your command aliases: "
alias # 显示当前用户下的别名命令。
echo "your home dir's filenames: "
ll ~ # 列出当前用户家目录下的文件。练习:
通过 bash 或 sh 命令来执行脚本。
CODEshibo@DESKTOP-U7FRP5M:~$ sh myid.sh This script will show your accout messages. The 'id' command output is: uid=1000(shibo) gid=1000(shibo) groups=1000(shibo),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),116(netdev) your user's home is: /home/shibo your history record: your command aliases: your home dir's filenames: myid.sh: 12: ll: not found承上,当需要在调试模式下执行脚本时,需要用哪一个选项。
CODEshibo@DESKTOP-U7FRP5M:~$ man sh ————————————省略———————————— -x xtrace Write each command to standard error (preceded by a ‘+ ’) before it is executed. Useful for debugging. ————————————省略———————————— shibo@DESKTOP-U7FRP5M:~$ sh -x myid.sh + echo This script will show your accout messages. This script will show your accout messages. + echo The 'id' command output is: The 'id' command output is: + id uid=1000(shibo) gid=1000(shibo) groups=1000(shibo),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),116(netdev) + echo your user's home is: /home/shibo your user's home is: /home/shibo + echo your history record: your history record: + echo your command aliases: your command aliases: + alias + echo your home dir's filenames: your home dir's filenames: + ll /home/shibo myid.sh: 12: ll: not found如何直接输入脚本名称执行脚本?
直接输入脚本名称执行脚本需要对脚本进行设置,确保脚本在当前用户下有 x 权限,且脚本所在的目录也存在 PATH 变量中
如何使用绝对路径执行脚本?
CODEshibo@DESKTOP-U7FRP5M:/$ /home/shibo/myid.sh当你在当前目录看到一个脚本,但是不清楚该目录是否在 PATH 变量中,因该如何执行脚本。
CODEshibo@DESKTOP-U7FRP5M:~$ ./myid.sh为什么
上面执行脚本后 HISTSIZE,alias,ll 等命令在脚本中不输出,如何解决?
当我们使用 bash 或 sh 来执行脚本时,系统会单独开启一个 “子bash” 来执行脚本,由于系统开启的 “子bash” 无法直接调用 “父bash” 中的变量和别名命令,所以在执行一些命令时会报错或不输出。这个时候,我们可以使用 source 命令来执行脚本
CODEshibo@DESKTOP-U7FRP5M:~$ source myid.sh This script will show your accout messages. The 'id' command output is: uid=1000(shibo) gid=1000(shibo) groups=1000(shibo),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),116(netdev) your user's home is: /home/shibo your history record: 10000 your command aliases: alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"' alias cp='cp -i' alias egrep='egrep --color=auto' alias fgrep='fgrep --color=auto' alias grep='grep --color=auto' alias l='ls -CF' alias la='ls -A' alias ll='ls -alF' alias ls='ls --color=auto' alias mv='mv -i' alias rm='rm -i' your home dir's filenames: total 205020 drwxr-x--- 7 shibo shibo 4096 Jun 21 20:59 ./ drwxr-xr-x 3 root root 4096 May 28 13:50 ../ -rw------- 1 shibo shibo 29002 Jun 19 17:46 .bash_history ————————以下省略————————
shell 脚本执行环境。
程序之间时有相关性的,我们可以使用 pstree 来查看。那 shell 脚本与 shell 之间有无相关性?我们用下面例子来看看:
shibo@DESKTOP-U7FRP5M:/tmp$ vim /bin/gototmp.sh
#!/bin/bash
cd /tmp
pwd
shibo@DESKTOP-U7FRP5M:/tmp$ sudo chmod a+x /bin/gototmp.sh 问:
在使用一般执行方式执行完脚本后,你的当前目录为:
CODEshibo@DESKTOP-U7FRP5M:/$ gototmp.sh /tmp shibo@DESKTOP-U7FRP5M:/$ pwd /因为通过 bash 或 sh 执行程序系统会创建一个新的子 bash,在这个子 bash 中执行脚本。因此脚本并不是没有起作用,而是另一个子 bash 中执行了。
如果是用 source 去执行,那么结果如何?
CODEshibo@DESKTOP-U7FRP5M:/bin$ source gototmp.sh /tmp shibo@DESKTOP-U7FRP5M:/tmp$ pwd /tmp使用 source 执行脚本后,系统并不会创建一个子 bash ,而是在当前 bash 中执行脚本。
使用 source 命令执行脚本。
依据上面所说的,执行脚本有两种形式。
- 产生一个 Bash 子程序,在其 bash 子程序中进行执行,例如使用 bash gototmp.sh 或 ./gototmp.sh
- 不产生新的 bash ,在当前 bash 中执行。例如使用 source gototmp.sh。
练习:
设计一个脚本,它具有以i下功能。
- 创建一个 MYIP 的变量,这个变量内存放着当前连网网卡的 IP 地址
- 创建一个 myWork 变量,该变量内存放着路径地址 /usr/local/lib 。
- 创建一个别名命令 megacil ,该别名是 /usr/bin/cat 命令。
- SHELL
#!/bin/bash MYIP=$(ifconfig eth0 | grep 'inet[[:space:]]' | sed 's/^.*[[:space:]]inet[[:space:]]//g' | sed 's/netmask[[:space:]].*//g') myWork="/usr/local/lib" alias megacli='/usr/bin/cat'
现在需要让 myenv.sh 这个脚本在当前 bash 中运行,如何做?
CODEshibo@DESKTOP-U7FRP5M:~$ source myenv.sh shibo@DESKTOP-U7FRP5M:~$ echo $MYIP 172.27.254.212 shibo@DESKTOP-U7FRP5M:~$ echo $myWork /usr/local/lib shibo@DESKTOP-U7FRP5M:~$ megacli fd fd te
以人机交互方式编写脚本
在编写脚本时,我们可以使用以下两种方式实现人机交互式脚本。
- 使用 read 命令实现读取用户输入的数据实现人机交互脚本。
- 使用 ${1} 等外部传值方式实现人机交互脚本。
使用 read 让用户输入数据
我们首先来了解一下 read 命令的用法,read 命令的用法和其他编程语言当中的标准输入命令差不多,它的基本格式如下:
read [option] <提示信息> <存入的变量>
# 常见选项
-a <arr_var>:将输入的数据存入到素组变量中。
-p <"test">:指定读取时的提示符
-t <number>:指定等待输入时间。
-d <"txt">:指定结束输入字符。
-n <number>:读取指定 number 的字符,不等回车读取。例如我们让用户输入一个数,然后让该数存入到变量 num 中。
read -p "place input a number:" num现在我们用这个命令,来实现用户输入数字,脚本输出特定的 pi 值小数位数。
#!/bin/bash
# 这是一个用于计算 pi 值的脚本。
echo -e "The program will calculate pi value \n" #这里的 -e 选项表示允许使用 \ 符号
echo -e "You should input a float number to calculate pi value \n"
read -p "place input a number:" num
echo -e "Starting calculate pi value Be patient"
time echo "scale=${num} ; 4*a(1)" | bc -lq #这里的 scala 变量就是指定输出的小数位长度(精度)。
# time 命令用于显示程序或命令运行使用的的时间和用户/系统使用情况。程序执行效果如下:
shibo@DESKTOP-U7FRP5M:~$ source myip.sh
The program will calculate pi value
-3 You should input a float number to calculate pi value \n
place input a number:10
Starting calculate pi value Be patient
3.1415926532
real 0m0.005s
user 0m0.003s
sys 0m0.002s练习:在 /usr/local/bin/listcmd.sh 编写脚本,该脚本能偶执行以下任务。
- 使用 # 注释表明该脚本的作用。
- 通过 read 命令读取用户输入的命令名称
- 通过 which 命令将用户输入的命令名称取缔其完整路径。
- 利用 ls -al 命令查看命令完整权限。
- 使用 getfacl 命令查看你命令的完整权限。
- 执行结束返回 0 值。
SHELL#!/bin/bash # 这个程序用于显示你输入的命令的路径详细信息和权限。 read -p "请输入命令:" cmd cmd_path=$(which $cmd) ls -al $cmd_path getfacl $cmd_path执行程序,它的执行效果如下:
CODEshibo@DESKTOP-U7FRP5M:~$ listcmd.sh 请输入命令:cat -rwxr-xr-x 1 root root 35280 Feb 8 2022 /usr/bin/cat getfacl: Removing leading '/' from absolute path names # file: usr/bin/cat # owner: root # group: root user::rwx group::r-x other::r-x
通过外带参数来执行程序
外带参数其实就是在执行命令的同时在命令后给出的参数,例如:
shibo@DESKTOP-U7FRP5M:~$ source mypi_case.sh 20
${0} ${1} 上面的脚本 mypi_case.sh 后的 20 就是该脚本的外带参数,通过将这个参数传入到脚本内并进行处理。如上所示,外带参数都有一个 【位置】 。及参数出现在脚本/命令后的顺序。如上面的 ${0} 的位置就表示传入程序的名称,${1} 表示传入脚本/命令后的的一个出现的参数,这里是 20。之后的参数以此类推。外带参数的具体用法如下:
${#}:这里的 “#” 表示参数出现的顺序/个数,上面演示中,“#” 为 1 表示传入 命令后出现的一个参数 20。
${@}:这里的 “@” 表示接受脚本/命令后所有的参数,如果使用该变量
${@}则输入的参数会被视为一个整体。如果在双引号中使用该变量,即"${@}"则输入的参数会以空格分隔作为独立的参数。创建一个 example.sh 脚本,先使用一般方式使用该变量。
SHELLf#!/bin/bash echo "all argument ${@}"现在我们执行脚本,并将在脚本后输入四个参数。
CODEshibo@DESKTOP-U7FRP5M:~/shell_script$ sh -x example.sh this is a argument + echo all argument this is a argument all argument this is a argument可以看到,这里把四个参数合并为了一个参数。现在我们改变一下脚本。
SHELL#!/bin/bash for arg in "${@}"; do echo "all argument $arg" done同样的,我们再执行脚本后输入四个参数。
CODEshibo@DESKTOP-U7FRP5M:~/shell_script$ sh example.sh this is a argument all argument this all argument is all argument a all argument argument被
""双引号括起来的${@}会将参数以空格隔开,保证每个参数的独立性。$*:这里的 * 表示接受所有参数,并将其输入的参数合并为一个处理。
同样是上面的脚本,我们将其中的
${@}改为$*。SHELL#!/bin/bash for arg in "$*"; do echo "all argument $arg" done执行脚本并再其脚本后输入 4 个参数。
CODEshibo@DESKTOP-U7FRP5M:~/shell_script$ sh example.sh arg1 arg2 arg3 arg4 all argument arg1 arg2 arg3 arg4可以看到,其参数被合并为了一个来处理。
现在我们使用外带参数的方式来修改之前我们写的计算 pi 值的程序中。
#!/bin/bash
# 这是一个用于计算 pi 值的脚本。
echo -e "The program will calculate pi value \n"
echo -e "You should input a float number to calculate pi value \n"
# read -p "place input a number:" num
num=${1}
time echo "scale=${num} ; 4*a(1)" | bc -lq现在,我们可以直接脚本后输入我们需要计算的 pi 值的长度啦。
shibo@DESKTOP-U7FRP5M:~$ source myip.sh 20
The program will calculate pi value
You should input a float number to calculate pi value
Starting calculate pi value Be patient
3.14159265358979323844
real 0m0.003s
user 0m0.000s
sys 0m0.000s练习:
修改之前编写的 listcmd.sh 脚本,将通过用户输入数据修改为通过外带参数输入数据。
CODE#!/bin/bash # 这个程序用于显示你输入的命令的路径详细信息和权限。 cmd=${1} # read -p "请输入命令:" cmd cmd_path=$(which $cmd) ls -al $cmd_path getfacl $cmd_path执行结果。
CODEshibo@DESKTOP-U7FRP5M:/usr/local/bin$ listcmd2.sh pwd -rwxr-xr-x 1 root root 35328 Feb 8 2022 /usr/bin/pwd getfacl: Removing leading '/' from absolute path names # file: usr/bin/pwd # owner: root # group: root user::rwx group::r-x other::r-x
通过 if … then 条件判断
有时候需要通过判断参数或功能是否符合程序要求,这个时候我们就可以使用 if 语句。再 shell 脚本中 if 语句的格式如下:
if [表达式]; then
# 条件成立,位于这里的语句就会被执行。
fi如果需要进行多个判断,格式如下:
if [表达式]; then
# 条件成立,执行这里的语句
else
# 条件不成立,执行这里的语句。
fi如果需要进行更加复杂的操作,那么可以使用下面的格式:
if [表达式1]; then
# 表达式1条件成立,执行这里的语句
elif [表达式2];then
# 表达式2条件成立,执行这里的语句
else
# 两个表达式条件都不成立,执行这里的语句。
fi 若需要两个或以上的表达式混合执行,可以使用 -a(and) 和 -o(or) 选项。
if [表达式1 -a 表达式2]; then
# 当表达1 与表达式2 都成立时,这里的语句执行。
fi
if [表达式1 -o 表达式2]; then
# 当表达式1或表达式2其中一条成立时,这里的语句执行。
fi或者,你也可以使用下面的格式。
if [表达式1] && [表达式2]; then
## 当表达式1与表达式2都成立,这里的语句执行
fi
if [表达式1] || [表达式2]; then
# 当表达式1或表达式2的其中任意一条成立,这里的语句执行。
fi 通过上面的方式,我们可以用以修改 mypi.sh 程序,实现防呆的效果。这里我们要求输入的数不能小于 10 ,因为精度太低没意义。同时输入的数也不能大于 100,精度太高会算的慢,会占用 cpu 资源。
#!/bin/bash
# 这是一个用于计算 pi 值的脚本。
echo -e "The program will calculate pi value \n"
echo -e "You should input a float number to calculate pi value \n"
# read -p "place input a number:" num
num=${1}
checking=$( echo ${num} | grep '[0-9]')
if [ ”${num}“ == ”“ -o "${checking}" != "" ];then
if [[ "${num}" -le 10 ]]; then
echo -e "数字过小\n"
elif [[ "${num}" -ge 100 ]]; then
echo -e "数字过大\n"
else
echo -e "Starting calculate pi value Be patient"
time echo "scale=${num} ; 4*a(1)" | bc -lq
fi
else
echo -e "非法字符,请输入数字【10~100】"
fi执行这个脚本,看看结果如何。
shibo@DESKTOP-U7FRP5M:~$ source myip.sh 5
The program will calculate pi value
You should input a float number to calculate pi value
数字过小
shibo@DESKTOP-U7FRP5M:~$ source myip.sh 14
The program will calculate pi value
You should input a float number to calculate pi value
Starting calculate pi value Be patient
3.14159265358976
real 0m0.001s
user 0m0.000s
sys 0m0.000s
shibo@DESKTOP-U7FRP5M:~$ source myip.sh 130
The program will calculate pi value
You should input a float number to calculate pi value
数字过大
shibo@DESKTOP-U7FRP5M:~$ source myip.sh hg
The program will calculate pi value
You should input a float number to calculate pi value
非法字符,请输入数字【10~100】以 case … esac 来判断。
这里的 case 与其他编程语言的类似语法一致,只是格式上有些差别。
case $变量名 in
"第一个变量内容")
# 匹配后执行的语句
;;
”第二个变量内容“)
# 匹配后执行的语句。
;;
"第三个变量内容")
# 匹配后执行的语句
;;
....
"*")
# 匹配后执行的语句
;;
esac继续用 mypi.sh 这个脚本举例,我们将 mypi.sh 通过 case 改写,用户只能输入 20、50、80 这几个数,如果输入的数不是这个几个,则会提示”请输入指定的数 20|50|80!“
#!/bin/bash
# 这是一个用于计算 pi 值的脚本。
echo -e "The program will calculate pi value \n"
echo -e "You should input a float number to calculate pi value \n"
# read -p "place input a number:" num
num=${1}
checking=$( echo ${num} | grep '[0-9]')
case $num in
"20")
echo "scale=$num ; 4*a(1)" | bc -lq
;;
"50")
echo "scale=$num ; 4*a(1) " | bc -lq
;;
"80")
echo "scale=$num ; 4*a(1) " | bc -lq
;;
*)
echo "请输入指定的数 20|50|80!"
;;
esac我们来看下执行效果。
The program will calculate pi value
You should input a float number to calculate pi value
请输入指定的数 20|50|80!
shibo@DESKTOP-U7FRP5M:~$ source mypi_case.sh 50
The program will calculate pi value
You should input a float number to calculate pi value
3.14159265358979323846264338327950288419716939937508衍生正则表达式字符
衍生正则表达式可以在原正则表达式的基础上简化命令。普通的 grep 只支持正则表达,如果需要使用衍生正则表达式,则需要使用 egrep 命令。举一个例子:在检索文件内容时去除空行和注释行可以减少干扰项,使用常规正则表达式进行匹配时,需要执行两次。
grep -v '^$' | grep -v '^#'而使用衍生正则表达式,只需要执行一次:
egrep -v '^$|^#'衍生正则表达式的特殊符号为:
| RE 字符 | 意义与范例 |
|---|---|
| + | 意义:重复『一个或一个以上』的前一个RE 字符 范例:搜寻(god) (good) (goood)… 等等的字串。 那个o+ 代表『一个以上的o 』所以,底下的执行成果会将第1, 9, 13 行列出来。egrep -n ‘go+d’ regular_express.txt |
| ? | 意义:『零个或一个』的前一个RE 字符 范例:搜寻(gd) (god) 这两个字串。 那个o? 代表『空的或1 个o 』所以,上面的执行成果会将第13, 14 行列出来。 有没有发现到,这两个案例( ‘go+d’ 与’go?d’ )的结果集合与’go*d’ 相同? 想想看,这是为什么喔! ^_^egrep -n ‘go?d’ regular_express.txt |
| | | 意义:用或( or )的方式找出数个字串 范例:搜寻gd 或good 这两个字串,注意,是『或』! 所以,第1,9,14 这三行都可以被列印出来喔!那如果还想要找出dog 呢?egrep -n ‘gd|good’ regular_express.txt egrep -n ‘gd|good|dog’ regular_express.txt |
| () | 意义:找出『群组』字串 范例:搜寻(glad) 或(good) 这两个字串,因为g 与d 是重复的,所以, 我就可以将la 与oo 列于( ) 当中,并以| 来分隔开来,就可以啦!egrep -n ‘g(la|oo)d’ regular_express.txt |
| ()+ | 意义:多个重复群组的判别 范例:将『AxyzxyzxyzxyzC』用echo 叫出,然后再使用如下的方法搜寻一下!echo ‘AxyzxyzxyzxyzC’ | egrep ‘A(xyz)+C’上面的例子意思是说,我要找开头是A 结尾是C ,中间有一个以上的"xyz" 字串的意思~ |
衍生正则表达式的内容就那么多。我们平时最常用的可能就一个 “()” 群组和 ”|“ 符号。另外,在正则表达式中 “!” 不是特殊字符,如果需要查找文件内容包含 ”!“ 符号的行,可以使用下面的方法:
shibo@DESKTOP-U7FRP5M:~$ egrep -n '[!]' mypi_case.sh
1:#!/bin/bash
19: echo "请输入指定的数 20|50|80!"作业/练习
正规表示法的应用-撷取重要的设定项目for /etc/man_db.conf
先确认/etc/man_db.conf 里面有几行?
再确认这个档案里面不是空白行也不是注解开头的,总共有几行?
最终只列出开头是『 MANPATH_MAP 』的那几行即可。
正规表示法的应用-撷取重要的设定项目for /etc/rsyslog.conf
根据前一题的作法,请将/etc/rsyslog.conf 当中,非空白行、非注解行取出(注意喔, # 在最前面才是全注解!)
承上,输出的资料中,还是含有# ,请将每一行的# 到最后面的资料,全部删除,只保留需要的设定部份。
从/etc/rsyslog.conf 当中,找出含有tcp 或udp 那一行,包括行号列出
根据上面的输出结果,你觉得rsyslog 这个服务,可能会启动那一个网路埠口号码?
撰写一只名为lm 的指令,这个指令会取得后面所有的参数( " {@} | more 』的样式, 进行一页一页翻动的功能。这个指令放在大家都能执行的/usr/local/bin 里面,请设定好权限之后, 以student 的身份执行看看喔!@ “),然后使用[ ls -al ]
执行效果:

![[番外]编译安装:apache 篇](/1788926622980-________________________-_____________hu_ebd767d9408462b1.webp)