第1章        初识Linux shell

内存存储单元按组划分成很多块,这些块称作页面(page)

Linux操作系统将运行中的程序称为进程。进程可以在前台运行,将输出显示在屏幕上,也
可以在后台运行,隐藏到幕后。内核控制着Linux系统如何管理运行在系统上的所有进程。

内核创建了第一个进程(称为init进程)来启动系统上所有其他进程。

Linux为系统上的每个设备都创建一种称为节点的特殊文件。与设备的所有通信都通过设备节点完成。每个节点都有唯一的数值对供Linux内核标识它。数值对包括一个主设备号和一个次设备号。类似的设备被划分到同样的主设备号下。次设备号用于标识主设备组下的某个特定设备。

Linux内核采用虚拟文件系统(Virtual File System,VFS)作为和每个文件系统交互的接口。这为Linux内核同任何类型文件系统通信提供了一个标准接口。当每个文件系统都被挂载和使用时,VFS将信息都缓存在内存中。

我们将完整的Linux系统包称为发行版。
不同的Linux发行版通常归类为3种:
 完整的核心Linux发行版
 特定用途的发行版
 LiveCD测试发行版

Linux内核是系统的核心,控制着内存、程序和硬件之间的交互。GNU工具也是Linux系统中的一个重要部分。本书关注的焦点Linux shell是GNU核心工具集中的一部分。本章还讨论了Linux系统中的最后一个组件:Linux桌面环境。

第2章        走进shell

表2-1 图形界面的组成
名 称 例 子 描 述
客户端 图形化终端仿真器,桌面环境,网络浏览器 请求图形化服务的应用
显示服务器 Mir,Wayland Compositor,Xserver 负责管理显示(屏幕)和输入设备(键盘、鼠标、触
摸屏)
窗口管理器 Compiz,Metacity,Kwin 为窗口加入边框,提供窗口移动和管理功能
部件库 Athenal(Xaw),X Intrinsics 为桌面环境中的客户端添加菜单以及外观项

要想在桌面中使用命令行,关键在于图形化终端仿真器。可以把图形化终端仿真器看作GUI
中(in the GUI)的CLI终端,将虚拟控制台终端看作GUI以外(outside the GUI)的CLI终端。

在大多数Linux发行版中,你可以使用简单的按键组合来访问某个Linux虚拟控制台。通常必
须按下Ctrl+Alt组合键,然后按功能键(F1~F7)进入要使用的虚拟控制台。功能键F1生成虚拟控
制台1,F2键生成虚拟控制台2,F3键生成虚拟控制台3,F4键生成虚拟控制台4,依次类推

第3章        基本的bash shell命令

[root@iZbp16mm3xbwen89azh9ffZ ~]# cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
polkitd:x:999:998:User for polkitd:/:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
chrony:x:998:996::/var/lib/chrony:/sbin/nologin
ntp:x:38:38::/etc/ntp:/sbin/nologin
tcpdump:x:72:72::/:/sbin/nologin
nscd:x:28:28:NSCD Daemon:/:/sbin/nologin
admin:x:1000:1000::/home/admin:/bin/bash
dongm:x:1001:1001::/home/dongm:/bin/bash
mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/false
nginx:x:997:995:Nginx web server:/var/lib/nginx:/sbin/nologin

当使用man命令查看命令手册页的时候,这些手册页是由分页程序(pager)来显示的。分页程序是一种实用工具,能够逐页显示文本

如果不记得命令名怎么办?可以使用关键字搜索手册页。语法是:man -k 关键字。例如,要查找与终端相关的命令,可以输入man -k terminal

Linux使用正斜线(/)而不是反斜线(\)在文件路径中划分目录。在Linux中,反斜线用来标识转义字符

用户可在虚拟目录中采用绝对文件路径引用目录名。绝对文件路径定义了在虚拟目录结构中该目录的确切位置,以虚拟目录的根目录开始,相当于目录的全名。

这个过滤器就是一个进行简单文本匹配的字符串。当用户指定特定文件的名称作为过滤器时,ls命令只会显示该文件的信息。有时你可能不知道要找的那个文件的确切名称。ls命令能够识别标准通配符,并在过滤器中用它们进行模式匹配:
 问号(?)代表一个字符;
 星号(*)代表零个或多个字符。

在过滤器中使用星号和问号被称为文件扩展匹配(file globbing),指的是使用通配符进行模式匹配的过程。通配符正式的名称叫作元字符通配符(metacharacter wildcards)。

cp -i test_one /home/christine/Documents/
$
$ ls -l /home/christine/Documents
total 0
-rw-rw-r-- 1 christine christine 0 May 21 15:25 test_one
$

新文件现就在目录Documents中了,和源文件同名。之前的例子在目标目录名尾部加上了一个正斜线(/),这表明Documents是目录而非文件。这有助于明确目的,而且在复制单个文件时非常重要。如果没有使用正斜线,子目录/home/christine/Documents又不存在,就会有麻烦。在这种情况下,试图将一个文件复制到Documents子目录反而会创建一个名为Documents的文件,连错误消息都不会显示!

##软链接
$ ls -l data_file
-rw-rw-r-- 1 christine christine 1092 May 21 17:27 data_file
$
$ ln -s data_file sl_data_file
$
$ ls -l *data_file
-rw-rw-r-- 1 christine christine 1092 May 21 17:27 data_file
lrwxrwxrwx 1 christine christine 9 May 21 17:29 sl_data_file -> data_file$ ls -i *data_file
296890 data_file 296891 sl_data_file
##硬链接
$ ls -l code_file
-rw-rw-r-- 1 christine christine 189 May 21 17:56 code_file
$
$ ln code_file hl_code_file
$
$ ls -li *code_file
296892 -rw-rw-r-- 2 christine christine 189 May 21 17:56 code_file
296892 -rw-rw-r-- 2 christine christine 189 May 21 17:56 hl_code_file
####说明 只能对处于同一存储媒体的文件创建硬链接。要想在不同存储媒体的文件之间创建链接,
只能使用符号链接。

cp file1 file2 
cp -r dir1 dir2     ## -r 递归,-i覆盖时提示
mv file1 file2 
mv dir1 dir2
rm -i file
mkdir -p dir1/dir2/dir3  ##mkdir命令的-p参数可以根据需要创建缺失的父目录
rm -ri dir ##使用-r选项使得命令可以向下进入目录,删除其中的文件,然后再删除目录本身
rm -rf dir

cat -n
cat -b file ##如果只想给有文本的行加上行号,可以用-b参数
tail -n file
tail -f log  ####-f参数是tail命令的一个突出特性。它允许你在其他进程使用该文件时查看文件的内容。tail命令会保持活动状态,并不断显示添加到文件中的内容。这是实时监测系统日志的绝妙方式。
head -n file

第4章        更多的bash shell命令

默认情况下,ps命令只会显示运行在当前控制台下的属于当前用户的进程。
Linux系统中使用的GNU ps命令支持3种不同类型的命令行参数:
 Unix风格的参数,前面加单破折线;
 BSD风格的参数,前面不加破折线;
 GNU风格的长参数,前面加双破折线。
ps -ef  ##-e参数指定显示所有运行在系统上的进程;-f参数则扩展了输出
ps aux  ##a 显示跟任意终端关联的所有进程,u 采用基于用户的格式显示,x 显示所有的进程,甚至包括未分配任何终端的进程

kill pid  ####kill命令可通过进程ID(PID)给进程发信号。默认情况下,kill命令会向命令行中列出的全部PID发送一个TERM信号。遗憾的是,你只能用进程的PID而不能用命令名
kill -s 信号名称 pid  ##针对不服管教的进程
killall http*   ##killall命令非常强大,它支持通过进程名而不是PID来结束进程。killall命令也支持通
配符,这在系统因负载过大而变得很慢时很有用。

w
pkill -u user        ##退出用户登录ps -ef|grep user|awk '{print $2}'|sudo xargs kill -9

mount
mount (-t type) devname mountpoint ##媒体设备挂载到了虚拟目录后,root用户就有了对该设备的所有访问权限,而其他用户的访问则会被限制。你可以通过目录权限(将在第7章中介绍)指定用户对设备的访问权限
mount -a ## -a 挂载/etc/fstab文件中指定的所有文件系统
umount [directory | device ]   ##卸载
lsof file   #####lsof命令获得使用file的进程信息,然后在应用中停止使用该设备或停止该进程    lsof -i:port     #####查看端口信息

df -h
du ##当前目录
du dir/    #显示dir目录的磁盘使用情况#-c:显示所有已列出文件总的大小,-h:按用户易读的格式输出大小,即用K替代千字节,用M替代兆字节,用G替代吉字节,-s:显示每个输出参数的总计

sort -t ':' -k 3 -n /etc/passwd   #。可以用-t参数来指定字段分隔符,然后用-k参数来指定排序的字段。-n参数按字符串数值来排序(并不转换为浮点数)
du -s *|sort -nr

grep option filename ##-v反向搜索,-n显示匹配行的行号,-c统计多少行含有匹配模式,-e指定多个模式,grep默认是匹配字符, -w 选项默认匹配一个单词
grep命令用基本的Unix风格正则表达式来匹配模式,egrep命令是grep的一个衍生,支持POSIX扩展正则表达式

###gzip
[root@iZbp16mm3xbwen89azh9ffZ dir]# ll
total 12
-rw-r--r-- 2 root root 115 Aug 15 17:06 1.txt
lrwxrwxrwx 1 root root   5 Aug 15 17:12 2.txt -> 1.txt
-rw-r--r-- 2 root root 115 Aug 15 17:06 5.txt
-rw-r--r-- 1 root root  53 Aug 16 11:10 6.txt
[root@iZbp16mm3xbwen89azh9ffZ dir]# gzip 2.txt   ##2.txt是软链接文件
gzip: 2.txt: Too many levels of symbolic links
[root@iZbp16mm3xbwen89azh9ffZ dir]# gzip 5.txt   ##5.txt是硬链接文件
gzip: 5.txt has 1 other link  -- unchanged
[root@iZbp16mm3xbwen89azh9ffZ dir]# gzip 6.txt   ##压缩
[root@iZbp16mm3xbwen89azh9ffZ dir]# ll
total 12
-rw-r--r-- 2 root root 115 Aug 15 17:06 1.txt
lrwxrwxrwx 1 root root   5 Aug 15 17:12 2.txt -> 1.txt
-rw-r--r-- 2 root root 115 Aug 15 17:06 5.txt
-rw-r--r-- 1 root root  59 Aug 16 11:10 6.txt.gz
[root@iZbp16mm3xbwen89azh9ffZ dir]# zcat 6.txt.gz  ##查看
this is other line
this is third line
this is a test
[root@iZbp16mm3xbwen89azh9ffZ dir]# gunzip 6.txt.gz  ##解压
[root@iZbp16mm3xbwen89azh9ffZ dir]# ll
total 12
-rw-r--r-- 2 root root 115 Aug 15 17:06 1.txt
lrwxrwxrwx 1 root root   5 Aug 15 17:12 2.txt -> 1.txt
-rw-r--r-- 2 root root 115 Aug 15 17:06 5.txt
-rw-r--r-- 1 root root  53 Aug 16 11:10 6.txt###zip
[root@iZbp16mm3xbwen89azh9ffZ dir]# yum install zip unzip -y
[root@iZbp16mm3xbwen89azh9ffZ dir]# zip 6.zip 6.txt    ##压缩adding: 6.txt (deflated 34%)
[root@iZbp16mm3xbwen89azh9ffZ dir]# ll
total 16
-rw-r--r-- 2 root root 115 Aug 15 17:06 1.txt
lrwxrwxrwx 1 root root   5 Aug 15 17:12 2.txt -> 1.txt
-rw-r--r-- 2 root root 115 Aug 15 17:06 5.txt
-rw-r--r-- 1 root root  53 Aug 16 11:10 6.txt
-rw-r--r-- 1 root root 195 Aug 16 11:20 6.zip
[root@iZbp16mm3xbwen89azh9ffZ dir]# unzip 6.zip     ##解压
Archive:  6.zip
replace 6.txt? [y]es, [n]o, [A]ll, [N]one, [r]ename: yinflating: 6.txt
[root@iZbp16mm3xbwen89azh9ffZ dir]# ll
total 16
-rw-r--r-- 2 root root 115 Aug 15 17:06 1.txt
lrwxrwxrwx 1 root root   5 Aug 15 17:12 2.txt -> 1.txt
-rw-r--r-- 2 root root 115 Aug 15 17:06 5.txt
-rw-r--r-- 1 root root  53 Aug 16 11:10 6.txt
-rw-r--r-- 1 root root 195 Aug 16 11:20 6.zip

tar cvf xxx.tar dir1/ dir2/
tar tf xxx.tar
tar xvf xxx.tar
tar zxf 1.tar.gz -C test/   #####解压到当前目录的test目录下

第5章        理解shell

默认的交互shell会在用户登录某个虚拟控制台终端或在GUI中运行终端仿真器时启动。不过还有另外一个默认shell是/bin/sh,它作为默认的系统shell,用于那些需要在启动时使用的系统shell脚本

[root@iZbp16mm3xbwen89azh9ffZ ~]# ps -f
UID        PID  PPID  C STIME TTY          TIME CMD
root     20200 20198  0 09:06 pts/0    00:00:00 -bash     #父shell
root     24427 20200  0 11:54 pts/0    00:00:00 ps -f
[root@iZbp16mm3xbwen89azh9ffZ ~]# bash          #创建子shell
[root@iZbp16mm3xbwen89azh9ffZ ~]# logout
bash: logout: not login shell: use `exit'
[root@iZbp16mm3xbwen89azh9ffZ ~]# bash -l       #以登录shell的形式启动创建子shell
[root@iZbp16mm3xbwen89azh9ffZ ~]# ps -f
UID        PID  PPID  C STIME TTY          TIME CMD
root     20200 20198  0 09:06 pts/0    00:00:00 -bash
root     24431 20200  0 11:54 pts/0    00:00:00 bash
root     24447 24431  0 11:54 pts/0    00:00:00 bash -l
root     24466 24447  0 11:55 pts/0    00:00:00 ps -f
[root@iZbp16mm3xbwen89azh9ffZ ~]# logout        #退出登录shell
[root@iZbp16mm3xbwen89azh9ffZ ~]# ps -f
UID        PID  PPID  C STIME TTY          TIME CMD
root     20200 20198  0 09:06 pts/0    00:00:00 -bash
root     24431 20200  0 11:54 pts/0    00:00:00 bash
root     24470 24431  0 11:55 pts/0    00:00:00 ps -f

$ pwd ; ls ; cd /etc ; pwd ; cd ; pwd ; ls  ##命令列表
### ;表示前一个命令结束接下一个命令                      \表示下一行接着写未结束的命令

在交互式的shell CLI中,进程列表、协程和管道都利用了子shell,一个高效的子shell用法就是使用后台模式

[dongm@localhost 5]$ ls
ceshi
[dongm@localhost 5]$ coproc ls>1.txt
[1] 6497
[1]+  Done                    coproc COPROC ls --color=auto > 1.txt
[dongm@localhost 5]$ cat 1.txt
1.txt
ceshi
[dongm@localhost 5]$ coproc (pwd;echo $BASH_SUBSHELL)>2.txt
[1] 6595
[1]+  Done                    coproc COPROC ( pwd; echo $BASH_SUBSHELL ) > 2.txt
[dongm@localhost 5]$ cat 2.txt
/home/dongm/fuxi3/5
2dongm@localhost 5]$ coproc sleep 10
[1] 7064
[dongm@localhost 5]$ ps -f
UID          PID    PPID  C STIME TTY          TIME CMD
dongm       3708    3702  0 21:44 pts/0    00:00:00 bash
dongm       7064    3708  0 22:34 pts/0    00:00:00 sleep 10
dongm       7071    3708  0 22:34 pts/0    00:00:00 ps -f
[dongm@localhost 5]$ coproc echo $BASH_SUBSHELL>3.txt
bash: warning: execute_coproc: coproc [7064:COPROC] still exists
[2] 7092
[1]   Done                    coproc COPROC sleep 10
[2]+  Done                    coproc COPROC echo $BASH_SUBSHELL > 3.txt
[dongm@localhost 5]$ cat 3.txt
1[root@iZbp16mm3xbwen89azh9ffZ dir]# coproc (echo $BASH_SUBSHELL;sleep 1000) >1.txt
[1] 32338
[root@iZbp16mm3xbwen89azh9ffZ dir]# cat 1.txt
2
[root@iZbp16mm3xbwen89azh9ffZ dir]# ps -f
UID        PID  PPID  C STIME TTY          TIME CMD
root     20200 20198  0 09:06 pts/0    00:00:00 -bash
root     32338 20200  0 14:34 pts/0    00:00:00 -bash
root     32339 32338  0 14:34 pts/0    00:00:00 sleep 1000
root     32348 20200  0 14:34 pts/0    00:00:00 ps -f

外部命令,有时候也被称为文件系统命令,是存在于bash shell之外的程序。它们并不是shell
程序的一部分。外部命令程序通常位于/bin、/usr/bin、/sbin或/usr/sbin中

[root@iZbp16mm3xbwen89azh9ffZ dir]# which cd
/usr/bin/cd
[root@iZbp16mm3xbwen89azh9ffZ dir]# type cd
cd is a shell builtin
[root@iZbp16mm3xbwen89azh9ffZ dir]# type -a cd
cd is a shell builtin
cd is /usr/bin/cd

history -a ##bash命令的历史记录是先存放在内存中,当shell退出时才被写入到历史文件中。可以在退出shell会话之前强制将命令历史记录写入.bash_history文件。要实现强制写入,需要使用history命令的-a选项
history -n  ##要想强制重新读取.bash_history文件,更新终端会话的历史记录,可以使用history -n命令

[root@iZbp16mm3xbwen89azh9ffZ dir]# alias li="ls -li"
[root@iZbp16mm3xbwen89azh9ffZ dir]# li
total 32
1055312 -rw-r--r-- 1 root root 10240 Aug 16 14:16 1.tar
1055748 -rw-r--r-- 1 root root   150 Aug 16 11:34 1.tar.gz
1053036 -rw-r--r-- 2 root root     2 Aug 16 14:34 1.txt
1055714 lrwxrwxrwx 1 root root     5 Aug 15 17:12 2.txt -> 1.txt
1053036 -rw-r--r-- 2 root root     2 Aug 16 14:34 5.txt
1055734 -rw-r--r-- 1 root root    53 Aug 16 11:10 6.txt
1055715 -rw-r--r-- 1 root root   195 Aug 16 11:20 6.zip
[root@iZbp16mm3xbwen89azh9ffZ dir]# bash
[root@iZbp16mm3xbwen89azh9ffZ dir]# li
bash: li: command not found
##在定义好别名之后,你随时都可以在shell中使用它。要注意,
因为命令别名属于内部命令,一个别名仅在它所被定义的shell进程中才有效。

第6章        使用Linux环境变量

要查看全局变量,可以使用env或printenv命令
set命令会显示为某个特定进程设置的所有环境变量,包括局部变量、全局变量以及用户定义变量。

[root@iZbp16mm3xbwen89azh9ffZ dongm]# which pwd
/usr/bin/pwd
[root@iZbp16mm3xbwen89azh9ffZ dongm]# `which pwd`
/home/dongm
[root@iZbp16mm3xbwen89azh9ffZ dongm]# $(which pwd)
/home/dongm
[root@iZbp16mm3xbwen89azh9ffZ dongm]# value1=`which pwd`
[root@iZbp16mm3xbwen89azh9ffZ dongm]# $value1
/home/dongm
[root@iZbp16mm3xbwen89azh9ffZ dongm]# value2=2
[root@iZbp16mm3xbwen89azh9ffZ dongm]# $value2
-bash: 2: command not found
[root@iZbp16mm3xbwen89azh9ffZ dongm]# echo $value2
2#####变量名由字母、数字和下划线组成,但是不能以数字开头
[dongm@www ~]$ variable='hello word'
[dongm@www ~]$ echo $variable
hello word
[dongm@www ~]$ bash
[dongm@www ~]$ bash
[dongm@www ~]$ echo $variable
hello word
[dongm@www ~]$ variable=null
[dongm@www ~]$ echo $variable
null
[dongm@www ~]$ unset variable
[dongm@www ~]$ echo $variable
[dongm@www ~]$ exit
exit
[dongm@www ~]$ ps -f
UID        PID  PPID  C STIME TTY          TIME CMD
dongm     6517  6507  0 22:03 pts/0    00:00:00 bash
dongm     7174  6517  0 22:10 pts/0    00:00:00 bash
dongm     7297  7174  0 22:11 pts/0    00:00:00 ps -f
[dongm@www ~]$ echo $variable
hello word######在设定全局环境变量的进程所创建的子进程中,该变量都是可见的。export设置全局变量
[root@iZbp16mm3xbwen89azh9ffZ dongm]# value3=100
[root@iZbp16mm3xbwen89azh9ffZ dongm]# export value3 ###相当于export value3=100
######和修改变量一样,在子shell中删除全局变量后,你无法将效果反映到父shell中##当你在shell命令行界面中输入一个外部命令时(参见第5章),shell必须搜索系统来找到对应
的程序。PATH环境变量定义了用于进行命令和程序查找的目录
[root@iZbp16mm3xbwen89azh9ffZ dir]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
[root@iZbp16mm3xbwen89azh9ffZ dir]# cat 1.sh
echo 1
[root@iZbp16mm3xbwen89azh9ffZ dir]# 1.sh
-bash: 1.sh: command not found
[root@iZbp16mm3xbwen89azh9ffZ dir]# PATH=$PATH:.    ###PATH=$PATH:绝对路径
[root@iZbp16mm3xbwen89azh9ffZ dir]# 1.sh
1
[root@iZbp16mm3xbwen89azh9ffZ dir]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:.

在你登入Linux系统启动一个bash shell时,默认情况下bash会在几个文件中查找命令。这些文件叫作启动文件或环境文件。bash检查的启动文件取决于你启动bash shell的方式。启动bash shell有3种方式:
 登录时作为默认登录shell
 作为非登录shell的交互式shell
 作为运行脚本的非交互shell

当你登录Linux系统时,bash shell会作为登录shell启动。登录shell会从5个不同的启动文件里
读取命令:
 /etc/profile        #/etc/profile文件是系统上默认的bash shell的主启动文件。系统上的每个用户登录时都会执行这个启动文件,对全局环境变量来说(Linux系统中所有用户都需要使用的变量),最好是在/etc/profile.d目录中创建一个以.sh结尾的文件。把所有新的或修改过的全局环境变量设置放在这个文件中
$HOME目录下的启动文件:提供一个用户专属的启动文件来定义该用户所用到的环境变量
 $HOME/.bash_profile
 $HOME/.bashrc     ###在大多数发行版中,存储个人用户永久性bash shell变量的地方是$HOME/.bashrc文件
 $HOME/.bash_login
 $HOME/.profile

###添加全局变量
[root@iZbp16mm3xbwen89azh9ffZ dongm]# cd /etc/profile.d/
[root@iZbp16mm3xbwen89azh9ffZ profile.d]# echo $value
[root@iZbp16mm3xbwen89azh9ffZ profile.d]# echo "value=100" >value.sh
[root@iZbp16mm3xbwen89azh9ffZ profile.d]# source value.sh
[root@iZbp16mm3xbwen89azh9ffZ profile.d]# echo $value
100
[root@iZbp16mm3xbwen89azh9ffZ profile.d]# bash
[root@iZbp16mm3xbwen89azh9ffZ profile.d]# bash
[root@iZbp16mm3xbwen89azh9ffZ profile.d]# echo $value
100###添加用户变量
[root@iZbp16mm3xbwen89azh9ffZ ~]# cd
[root@iZbp16mm3xbwen89azh9ffZ ~]# sed -i '$a\value2=1000' .bashrc
[root@iZbp16mm3xbwen89azh9ffZ ~]# sed -i '$a\echo 10000' .bashrc
[root@iZbp16mm3xbwen89azh9ffZ ~]# bash
10000
[root@iZbp16mm3xbwen89azh9ffZ ~]# bash
10000
[root@iZbp16mm3xbwen89azh9ffZ ~]# echo $value2
1000
##数组变量
[root@iZbp16mm3xbwen89azh9ffZ ~]# mytest=(1 2 3 4 5)
[root@iZbp16mm3xbwen89azh9ffZ ~]# echo ${mytest[*]}
1 2 3 4 5
[root@iZbp16mm3xbwen89azh9ffZ ~]# echo ${mytest[2]}
3
[root@iZbp16mm3xbwen89azh9ffZ ~]# unset mytest[2]
[root@iZbp16mm3xbwen89azh9ffZ ~]# echo ${mytest[*]}
1 2 4 5
[root@iZbp16mm3xbwen89azh9ffZ ~]# echo ${mytest[2]}     ##值删除后,索引依然在[root@iZbp16mm3xbwen89azh9ffZ ~]# echo ${mytest[1]}
2
[root@iZbp16mm3xbwen89azh9ffZ ~]# unset mytest
[root@iZbp16mm3xbwen89azh9ffZ ~]# echo ${mytest[*]}[root@iZbp16mm3xbwen89azh9ffZ ~]# value[1]=5
[root@iZbp16mm3xbwen89azh9ffZ ~]# value[2]=6
[root@iZbp16mm3xbwen89azh9ffZ ~]# value[0]=1000
[root@iZbp16mm3xbwen89azh9ffZ ~]# echo ${value[*]}
1000 5 6

第7章        理解Linux文件权限

用户权限是通过创建用户时分配的用户ID(User ID,通常缩写为UID)来跟踪的
系统账户,是系统上运行的各种服务进程访问资源用的特殊账户。所有运行在后台的服务都需要用一个系统用户账户登录到Linux系统上。

目录的rwx分别r权限代表能不能读取目录内的文件信息(Is), w权限代表能不能在目录内删除或者创建以及修改文件(touch, rm, mv), x权限代表能不能进入目录(cd)
目录内的文件具体能不能可读可写可执行要看文件的具体权限
目录内的文件在使用touch, nv, rm命令的时候要看文件的上一级目录权限

[root@iZbp16mm3xbwen89azh9ffZ skel]# useradd -D #系统默认值被设置在/etc/default/useradd文件中
GROUP=100
HOME=/home
INACTIVE=-1
EXPIRE=
SHELL=/bin/bash
SKEL=/etc/skel
CREATE_MAIL_SPOOL=yes[root@iZbp16mm3xbwen89azh9ffZ skel]# useradd -D -s /usr/bin/sh
[root@iZbp16mm3xbwen89azh9ffZ skel]# useradd -D
GROUP=100
HOME=/home
INACTIVE=-1
EXPIRE=
SHELL=/usr/bin/sh
SKEL=/etc/skel
CREATE_MAIL_SPOOL=yes
[root@iZbp16mm3xbwen89azh9ffZ skel]# useradd -m test
[root@iZbp16mm3xbwen89azh9ffZ skel]# echo "123456"|passwd --stdin test  ##设置用户密码
Changing password for user test.
passwd: all authentication tokens updated successfully.
[root@iZbp16mm3xbwen89azh9ffZ home]# tail -1 /etc/passwd
test:x:1002:1002::/home/test:/usr/bin/sh

chpasswd命令能从标准输入自动读取登录名和密码对(由冒号分割)列表,给密码加密,然后为用户账户设置。你也可以用重定向命令来将含有userid:passwd对的文件重定向给该命令。

# chpasswd < users.txt
dongm@ubuntu:/home/maxm$ su
Password:
Error: cannot canonicalize mount point: No such file or directory
root@ubuntu:/home/maxm# chpasswd
dongm:1(ctrl+d保存)
root@ubuntu:/home/maxm# exit
exit
maxm@ubuntu:~$ su dongm
Password:           (输入密码1)
dongm@ubuntu:/home/maxm$ grep -i umask /etc/login.defs     ### -i 不区分大小写root@ubuntu:/home/dongm/7# ls -l
total 4
-rw-r----- 1 dongm dongm    0  1月 18 14:05 1
drwxr-x--x 2 dongm dongm 4096  1月 18 14:05 2
root@ubuntu:/home/dongm/7# chown sharing. 1
chown: invalid user: `sharing.'
root@ubuntu:/home/dongm/7# chown maxm.sharing 1
root@ubuntu:/home/dongm/7# ls -l
total 4
-rw-r----- 1 maxm  sharing    0  1月 18 14:05 1
drwxr-x--x 2 dongm dongm   4096  1月 18 14:05 2
root@ubuntu:/home/dongm/7# chown dongm. 1
root@ubuntu:/home/dongm/7# ls -l
total 4
-rw-r----- 1 dongm dongm    0  1月 18 14:05 1
drwxr-x--x 2 dongm dongm 4096  1月 18 14:05 2-h选项可以改变该文件的所有符号链接文件的所属关系。
root@ubuntu:/home/dongm/7# ln -s 1 3
root@ubuntu:/home/dongm/7# ls -l
total 4
-rw-r----- 1 dongm dongm    0  1月 18 14:05 1
drwxr-x--x 2 dongm dongm 4096  1月 18 14:05 2
lrwxrwxrwx 1 root  root     1  1月 18 14:18 3 -> 1
root@ubuntu:/home/dongm/7# chown dongm. 3
root@ubuntu:/home/dongm/7# ls -l
total 4
-rw-r----- 1 dongm dongm    0  1月 18 14:05 1
drwxr-x--x 2 dongm dongm 4096  1月 18 14:05 2
lrwxrwxrwx 1 root  root     1  1月 18 14:18 3 -> 1
root@ubuntu:/home/dongm/7# chown -h dongm. 3
root@ubuntu:/home/dongm/7# ls -l
total 4
-rw-r----- 1 dongm dongm    0  1月 18 14:05 1
drwxr-x--x 2 dongm dongm 4096  1月 18 14:05 2
lrwxrwxrwx 1 dongm dongm    1  1月 18 14:18 3 -> 1usermod -G groupname usernamegroupmod -n newname oldname

当一个用户在/etc/passwd文件中指定某个组作为默认组时,用户账户不会作为该组成员再出现在/etc/group文件中。

[root@iZbp16mm3xbwen89azh9ffZ home]# groupadd sharing
[root@iZbp16mm3xbwen89azh9ffZ home]# usermod -G sharing test
[root@iZbp16mm3xbwen89azh9ffZ home]# tail -2 /etc/group
test:x:1002:
sharing:x:1003:test
##说明 如果更改了已登录系统账户所属的用户组,该用户必须登出系统后再登录,组关系的更
改才能生效。
为用户账户分配组时要格外小心。如果加了-g选项,指定的组名会替换掉该账户的默认
组。-G选项则将该组添加到用户的属组的列表里,不会影响默认组。
##修改组
[root@iZbp16mm3xbwen89azh9ffZ home]# groupmod -n shared sharing
[root@iZbp16mm3xbwen89azh9ffZ home]# tail -1 /etc/group
shared:x:1003:test
##修改组名时,GID和组成员不会变,只有组名改变。由于所有的安全权限都是基于GID的,
你可以随意改变组名而不会影响文件的安全性grep -i umask /etc/profile    ###-i 不区分大小写

改变权限---chmod命令用来改变文件和目录的安全性设置。该命令的格式如下:
chmod options mode file
mode参数可以使用八进制模式或符号模式进行安全性设置。八进制模式设置非常直观,直
接用期望赋予文件的标准3位八进制权限码即可。
符号模式下指定权限的格式:[ugoa…][[+-=][rwxXstugo…]
options为chmod命令提供了另外一些功能。-R选项可以让权限的改变递归地作用到文件和子目录。你可以使用通配符指定多个文件,然后利用一条命令将权限更改应用到这些文件上。

改变所属关系---chown命令用来改变文件的属主,chgrp命令用来改变文件的默认属组。
chown命令的格式如下。
chown options owner[.group] file
-R选项配合通配符可以递归地改变子目录和文件的所属关系。-h选项可以改变该文件的所有符号链接文件的所属关系

[root@iZbp16mm3xbwen89azh9ffZ test]# ll
total 0
-rw-r--r-- 1 root root 0 Aug 17 18:13 1.txt
lrwxrwxrwx 1 test test 5 Aug 18 09:25 2.txt -> 1.txt
[root@iZbp16mm3xbwen89azh9ffZ test]# chown -h dongm. 2.txt
[root@iZbp16mm3xbwen89azh9ffZ test]# ll
total 0
-rw-r--r-- 1 root  root  0 Aug 17 18:13 1.txt
lrwxrwxrwx 1 dongm dongm 5 Aug 18 09:25 2.txt -> 1.txt

chgrp group file     chgrp -R group dir/
###用户账户必须是这个文件的属主,除了能够更换属组之外,还得是新组的成员。

###共享文件
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ mkdir share
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ ll
total 4
drwxrwxr-x 2 dongm dongm 4096 Aug 18 10:05 share
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ chmod g+s share/
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ tail -2 /etc/group
shared:x:1003:test,dongm
dongm:x:1004:
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ chgrp shared share/
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ ll
total 4
drwxrwsr-x 2 dongm shared 4096 Aug 18 10:05 share
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ cd share/
[dongm@iZbp16mm3xbwen89azh9ffZ share]$ umask 002
[dongm@iZbp16mm3xbwen89azh9ffZ share]$ touch 1.txt
[dongm@iZbp16mm3xbwen89azh9ffZ share]$ ll
total 0
-rw-rw-r-- 1 dongm shared 0 Aug 18 10:06 1.txt
###通过chgrp命令将目录的默认属组改为包
含所有需要共享文件的用户的组(你必须是该组的成员)。最后,将目录的SGID位置位,以保证
目录中新建文件都用shared作为默认属组。

第8章        管理文件系统

Linux操作系统中引入的最早的文件系统叫作扩展文件系统(extended filesystem,简记为ext)
Linux通过唯一的数值(称作索引节点号)来引用索引节点表中的每个索引节点,这个值是创建文件时由文件系统分配的。文件系统通过索引节点号而不是文件全名及路径来标识文件

ext3文件系统为日志文件系统
日志文件系统先将文件的更改写入到临时文件(称作日志,journal)中。在数据成功写到存储设备和索引节点表之后,再删除对应的日志条目。
数据模式日志方法是所有写到存储设备上的数据都必须写两次:第一次写入日志,第二次写入真正的存储设备

主分区(包括扩展分区)的总个数不能超过四个;硬盘总容量=主分区(包括扩展分区)总容量;扩展分区容量=逻辑分区总容量

文件系统的检查与修复:
fsck options filesystem
你可以在命令行上列出多个要检查的文件系统。文件系统可以通过设备名、在虚拟目录中的挂载点以及分配给文件系统的唯一UUID值来引用。

############################操作文件系统###############################
[root@localhost ~]# fdisk /dev/sda         #创建分区Welcome to fdisk (util-linux 2.32.1).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.Command (m for help): p
Disk /dev/sda: 100 GiB, 107374182400 bytes, 209715200 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x3d3f9d05Device     Boot     Start       End   Sectors  Size Id Type
/dev/sda1  *         2048    616447    614400  300M 83 Linux
/dev/sda2          616448   8902655   8286208    4G 82 Linux swap / Solaris
/dev/sda3         8902656 104857599  95954944 45.8G 83 Linux
/dev/sda4       104857600 209715199 104857600   50G  5 Extended
/dev/sda5       104859648 125831167  20971520   10G 83 Linux
/dev/sda6       125833216 136318975  10485760    5G 83 Linux
/dev/sda7       136321024 157292543  20971520   10G 83 Linux
/dev/sda8       157294592 176168959  18874368    9G 8e Linux LVM
/dev/sda9       176171008 199239679  23068672   11G 8e Linux LVM
/dev/sda10      199241728 203436031   4194304    2G 8e Linux LVMCommand (m for help): n
All primary partitions are in use.
Adding logical partition 11
First sector (203438080-209715199, default 203438080):
Last sector, +sectors or +size{K,M,G,T,P} (203438080-209715199, default 209715199): +2.5GCreated a new partition 11 of type 'Linux' and of size 2.5 GiB.Command (m for help): p
Disk /dev/sda: 100 GiB, 107374182400 bytes, 209715200 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x3d3f9d05Device     Boot     Start       End   Sectors  Size Id Type
/dev/sda1  *         2048    616447    614400  300M 83 Linux
/dev/sda2          616448   8902655   8286208    4G 82 Linux swap / Solaris
/dev/sda3         8902656 104857599  95954944 45.8G 83 Linux
/dev/sda4       104857600 209715199 104857600   50G  5 Extended
/dev/sda5       104859648 125831167  20971520   10G 83 Linux
/dev/sda6       125833216 136318975  10485760    5G 83 Linux
/dev/sda7       136321024 157292543  20971520   10G 83 Linux
/dev/sda8       157294592 176168959  18874368    9G 8e Linux LVM
/dev/sda9       176171008 199239679  23068672   11G 8e Linux LVM
/dev/sda10      199241728 203436031   4194304    2G 8e Linux LVM
/dev/sda11      203438080 208656383   5218304  2.5G 83 LinuxCommand (m for help): w
The partition table has been altered.
Syncing disks.[root@localhost ~]# blkid           ###查看分区和文件系统的信息
/dev/sda1: UUID="34c40ae0-1885-4113-bf12-0217b18f1825" BLOCK_SIZE="1024" TYPE="ext4" PARTUUID="3d3f9d05-01"
/dev/sda2: UUID="ea68b98f-6ae1-4ef3-b253-4ce2bb1dba6d" TYPE="swap" PARTUUID="3d3f9d05-02"
/dev/sda3: UUID="f323d384-661e-4a08-956a-b7c740920e76" BLOCK_SIZE="512" TYPE="xfs" PARTUUID="3d3f9d05-03"
/dev/sda5: UUID="2482ae10-f90f-41fe-82ff-0e91ac25435d" BLOCK_SIZE="4096" TYPE="ext4" PARTUUID="3d3f9d05-05"
/dev/sda6: UUID="b1f09ead-6eab-4811-a9f6-ebb603a0b103" SEC_TYPE="ext2" BLOCK_SIZE="4096" TYPE="ext3" PARTUUID="3d3f9d05-06"
/dev/sda7: UUID="924c7db6-cfa9-4527-964c-5d159f4f94fe" BLOCK_SIZE="4096" TYPE="ext4" PARTUUID="3d3f9d05-07"
/dev/sda8: UUID="uDZ342-bNXr-PgpJ-douS-nro0-CZjg-EE73Pk" TYPE="LVM2_member" PARTUUID="3d3f9d05-08"
/dev/sda9: UUID="Txs2e3-LTZf-wHz5-V4au-kGVj-5YXN-UODcBQ" TYPE="LVM2_member" PARTUUID="3d3f9d05-09"
/dev/sda10: UUID="gJ29f1-ZXbY-wxPV-OG0d-bFaV-NTHJ-0ogUWI" TYPE="LVM2_member" PARTUUID="3d3f9d05-0a"
/dev/mapper/vol1-lvtest1: UUID="855159b5-aee9-4a03-bd9b-c81f750730a0" BLOCK_SIZE="4096" TYPE="ext4"
/dev/mapper/vol1-lvtest2: UUID="3e3001b2-e82a-4f58-a1c0-43cce7e4c062" BLOCK_SIZE="4096" TYPE="ext4"
/dev/sda11: PARTUUID="3d3f9d05-0b"       ##此时/dev/sda11为物理分区,未格式化文件系统
[root@localhost ~]# mkfs.ext4 /dev/sda11  ##格式化文件系统
mke2fs 1.45.6 (20-Mar-2020)
Creating filesystem with 652288 4k blocks and 163200 inodes
Filesystem UUID: 7aac5008-31fe-414c-ba81-fe4e7d4a8e84
Superblock backups stored on blocks: 32768, 98304, 163840, 229376, 294912Allocating group tables: done
Writing inode tables: done
Creating journal (16384 blocks): done      ##创建日志
Writing superblocks and filesystem accounting information: done [root@localhost ~]# blkid
/dev/sda1: UUID="34c40ae0-1885-4113-bf12-0217b18f1825" BLOCK_SIZE="1024" TYPE="ext4" PARTUUID="3d3f9d05-01"
/dev/sda2: UUID="ea68b98f-6ae1-4ef3-b253-4ce2bb1dba6d" TYPE="swap" PARTUUID="3d3f9d05-02"
/dev/sda3: UUID="f323d384-661e-4a08-956a-b7c740920e76" BLOCK_SIZE="512" TYPE="xfs" PARTUUID="3d3f9d05-03"
/dev/sda5: UUID="2482ae10-f90f-41fe-82ff-0e91ac25435d" BLOCK_SIZE="4096" TYPE="ext4" PARTUUID="3d3f9d05-05"
/dev/sda6: UUID="b1f09ead-6eab-4811-a9f6-ebb603a0b103" SEC_TYPE="ext2" BLOCK_SIZE="4096" TYPE="ext3" PARTUUID="3d3f9d05-06"
/dev/sda7: UUID="924c7db6-cfa9-4527-964c-5d159f4f94fe" BLOCK_SIZE="4096" TYPE="ext4" PARTUUID="3d3f9d05-07"
/dev/sda8: UUID="uDZ342-bNXr-PgpJ-douS-nro0-CZjg-EE73Pk" TYPE="LVM2_member" PARTUUID="3d3f9d05-08"
/dev/sda9: UUID="Txs2e3-LTZf-wHz5-V4au-kGVj-5YXN-UODcBQ" TYPE="LVM2_member" PARTUUID="3d3f9d05-09"
/dev/sda10: UUID="gJ29f1-ZXbY-wxPV-OG0d-bFaV-NTHJ-0ogUWI" TYPE="LVM2_member" PARTUUID="3d3f9d05-0a"
/dev/mapper/vol1-lvtest1: UUID="855159b5-aee9-4a03-bd9b-c81f750730a0" BLOCK_SIZE="4096" TYPE="ext4"
/dev/mapper/vol1-lvtest2: UUID="3e3001b2-e82a-4f58-a1c0-43cce7e4c062" BLOCK_SIZE="4096" TYPE="ext4"
/dev/sda11: UUID="7aac5008-31fe-414c-ba81-fe4e7d4a8e84" BLOCK_SIZE="4096" TYPE="ext4" PARTUUID="3d3f9d05-0b"    ###已被ext4文件系统格式化
[root@localhost ~]# ll /mnt
total 9
dr-xr-xr-x. 1 root root 4192 Aug 18 13:55 hgfs
drwxr-xr-x. 2 root root    6 May 10  2021 lvtest1
drwxr-xr-x. 3 root root 4096 May 10  2021 mypartition
drwxr-xr-x. 2 root root    6 May 10  2021 partition1
drwxr-xr-x. 2 root root    6 May 10  2021 partition2
drwxrwxrwx. 2 root root    6 Nov 16  2021 partition3
drwxrwxrwx. 2 root root    6 Nov 16  2021 partition4
[root@localhost ~]# cd /mnt
[root@localhost mnt]# ll
total 9
dr-xr-xr-x. 1 root root 4192 Aug 18 13:55 hgfs  ##vmware宿主机与虚拟机共享文件的目录
drwxr-xr-x. 2 root root    6 May 10  2021 lvtest1
drwxr-xr-x. 3 root root 4096 May 10  2021 mypartition
drwxr-xr-x. 2 root root    6 May 10  2021 partition1
drwxr-xr-x. 2 root root    6 May 10  2021 partition2
drwxrwxrwx. 2 root root    6 Nov 16  2021 partition3
drwxrwxrwx. 2 root root    6 Nov 16  2021 partition4
[root@localhost mnt]# cd partition2/
[root@localhost partition2]# ll
total 0
[root@localhost partition2]# mount /dev/sda11 /mnt/partition2/   ##这种挂载文件系统的方法只能临时挂载文件系统。当重启Linux系统时,文件系统并不会自动挂载。要强制Linux在启动时自动挂载新的文件系统,可以将其添加到/etc/fstab文件
[root@localhost partition2]# ll /mnt/partition2/
total 16
drwx------. 2 root root 16384 Aug 18 13:55 lost+found  ###文件系统已挂在到目录
[root@localhost partition2]# lsblk   ##查看磁盘结构
NAME             MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
sda                8:0    0  100G  0 disk
├─sda1             8:1    0  300M  0 part /boot
├─sda2             8:2    0    4G  0 part [SWAP]
├─sda3             8:3    0 45.8G  0 part /
├─sda4             8:4    0    1K  0 part
├─sda5             8:5    0   10G  0 part /mnt/mypartition
├─sda6             8:6    0    5G  0 part
├─sda7             8:7    0   10G  0 part
├─sda8             8:8    0    9G  0 part
│ ├─vol1-lvtest1 253:0    0    2G  0 lvm
│ └─vol1-lvtest2 253:1    0   15G  0 lvm
├─sda9             8:9    0   11G  0 part
│ └─vol1-lvtest2 253:1    0   15G  0 lvm
├─sda10            8:10   0    2G  0 part
└─sda11            8:11   0  2.5G  0 part /mnt/partition2
sr0               11:0    1 1024M  0 rom
sr1               11:1    1 1024M  0 rom
[root@localhost partition2]# umount /mnt/partition2
[root@localhost partition2]# fsck -a /dev/sda11   ###卸载后可以对文件系统进行检查和修复
fsck from util-linux 2.32.1
/dev/sda11: clean, 11/163200 files, 28550/652288 blocks
[root@localhost partition2]# df -h
Filesystem      Size  Used Avail Use% Mounted on
devtmpfs        881M     0  881M   0% /dev
tmpfs           897M     0  897M   0% /dev/shm
tmpfs           897M  9.5M  887M   2% /run
tmpfs           897M     0  897M   0% /sys/fs/cgroup
/dev/sda3        46G   19G   28G  41% /
/dev/sda5       9.8G   37M  9.3G   1% /mnt/mypartition
/dev/sda1       283M  198M   67M  75% /boot
vmhgfs-fuse     239G  135G  104G  57% /mnt/hgfs
tmpfs           180M  1.2M  179M   1% /run/user/42
tmpfs           180M     0  180M   0% /run/user/0
[root@localhost partition2]# mount /dev/sda11 /mnt/partition2/
[root@localhost partition2]# df -h
Filesystem      Size  Used Avail Use% Mounted on
devtmpfs        881M     0  881M   0% /dev
tmpfs           897M     0  897M   0% /dev/shm
tmpfs           897M  9.5M  887M   2% /run
tmpfs           897M     0  897M   0% /sys/fs/cgroup
/dev/sda3        46G   19G   28G  41% /
/dev/sda5       9.8G   37M  9.3G   1% /mnt/mypartition
/dev/sda1       283M  198M   67M  75% /boot
vmhgfs-fuse     239G  135G  104G  57% /mnt/hgfs
tmpfs           180M  1.2M  179M   1% /run/user/42
tmpfs           180M     0  180M   0% /run/user/0
/dev/sda11      2.4G  7.5M  2.3G   1% /mnt/partition2   ##已挂载
###################永久挂载---/etc/fstab文件内挂载文件系统步骤#####################
1.物理分区基础上创建物理卷---2.创建卷组---3.创建逻辑卷---4.用文件系统格式化逻辑卷
vi /etc/fstab
#
# /etc/fstab
# Created by anaconda on Wed Apr 14 05:01:17 2021
#
# Accessible filesystems, by reference, are maintained under '/dev/disk/'.
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info.
#
# After editing this file, run 'systemctl daemon-reload' to update systemd
# units generated from this file.
#
UUID=f323d384-661e-4a08-956a-b7c740920e76 /            xfs     defaults        0 0
UUID=34c40ae0-1885-4113-bf12-0217b18f1825 /boot        ext4    defaults        1 2
UUID=ea68b98f-6ae1-4ef3-b253-4ce2bb1dba6d swap         swap    defaults        0 0
/dev/sda5  /mnt/mypartition/                           ext4    defaults        0 0
/dev/vol1/lvtest2  /mnt/partition3/                ext4    defaults        0 0  #添加此行
####保存后mount -a,/etc/fstab配置文件即可生效[root@localhost mnt]# df -h
Filesystem                Size  Used Avail Use% Mounted on
devtmpfs                  881M     0  881M   0% /dev
tmpfs                     897M     0  897M   0% /dev/shm
tmpfs                     897M   18M  879M   2% /run
tmpfs                     897M     0  897M   0% /sys/fs/cgroup
/dev/sda3                  46G   19G   28G  40% /
/dev/sda5                 9.8G   37M  9.3G   1% /mnt/mypartition
/dev/sda1                 283M  198M   67M  75% /boot
vmhgfs-fuse               473G  129G  344G  28% /mnt/hgfs
tmpfs                     180M  1.2M  179M   1% /run/user/42
tmpfs                     180M  5.7M  174M   4% /run/user/1002
/dev/mapper/vol1-lvtest1  4.9G   20M  4.6G   1% /mnt/partition2
[root@localhost mnt]# mount -a
[root@localhost mnt]# df -h
Filesystem                Size  Used Avail Use% Mounted on
devtmpfs                  881M     0  881M   0% /dev
tmpfs                     897M     0  897M   0% /dev/shm
tmpfs                     897M   18M  879M   2% /run
tmpfs                     897M     0  897M   0% /sys/fs/cgroup
/dev/sda3                  46G   19G   28G  40% /
/dev/sda5                 9.8G   37M  9.3G   1% /mnt/mypartition
/dev/sda1                 283M  198M   67M  75% /boot
vmhgfs-fuse               473G  129G  344G  28% /mnt/hgfs
tmpfs                     180M  1.2M  179M   1% /run/user/42
tmpfs                     180M  5.7M  174M   4% /run/user/1002
/dev/mapper/vol1-lvtest1  4.9G   20M  4.6G   1% /mnt/partition2
/dev/mapper/vol1-lvtest2   15G   41M   14G   1% /mnt/partition3如需卸载,1.umount /dev/vol1/lvtest2  (umount 设备 目录或者uuid都可)2./etc/fstab文件注释掉此行#/dev/vol1/lvtest2  /mnt/partition3/         ext4    defaults        0 0保存后mount -a即可。
#############################逻辑卷#########################
[root@localhost ~]# fdisk /dev/sda          ##物理分区Welcome to fdisk (util-linux 2.32.1).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.Command (m for help): p
Disk /dev/sda: 100 GiB, 107374182400 bytes, 209715200 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x3d3f9d05Device     Boot     Start       End   Sectors  Size Id Type
/dev/sda1  *         2048    616447    614400  300M 83 Linux
/dev/sda2          616448   8902655   8286208    4G 82 Linux swap / Solaris
/dev/sda3         8902656 104857599  95954944 45.8G 83 Linux
/dev/sda4       104857600 209715199 104857600   50G  5 Extended
/dev/sda5       104859648 125831167  20971520   10G 83 Linux
/dev/sda6       125833216 136318975  10485760    5G 83 Linux
/dev/sda7       136321024 157292543  20971520   10G 83 LinuxCommand (m for help): n
All primary partitions are in use.
Adding logical partition 8
First sector (157294592-209715199, default 157294592):
Last sector, +sectors or +size{K,M,G,T,P} (157294592-209715199, default 209715199): +10GCreated a new partition 8 of type 'Linux' and of size 10 GiB.Command (m for help): n
All primary partitions are in use.
Adding logical partition 9
First sector (178268160-209715199, default 178268160):
Last sector, +sectors or +size{K,M,G,T,P} (178268160-209715199, default 209715199): +8GCreated a new partition 9 of type 'Linux' and of size 8 GiB.Command (m for help): p
Disk /dev/sda: 100 GiB, 107374182400 bytes, 209715200 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x3d3f9d05Device     Boot     Start       End   Sectors  Size Id Type
/dev/sda1  *         2048    616447    614400  300M 83 Linux
/dev/sda2          616448   8902655   8286208    4G 82 Linux swap / Solaris
/dev/sda3         8902656 104857599  95954944 45.8G 83 Linux
/dev/sda4       104857600 209715199 104857600   50G  5 Extended
/dev/sda5       104859648 125831167  20971520   10G 83 Linux
/dev/sda6       125833216 136318975  10485760    5G 83 Linux
/dev/sda7       136321024 157292543  20971520   10G 83 Linux
/dev/sda8       157294592 178266111  20971520   10G 83 Linux
/dev/sda9       178268160 195045375  16777216    8G 83 LinuxCommand (m for help): t            ##修改分区类型
Partition number (1-9, default 9):
Hex code (type L to list all codes): l0  Empty           24  NEC DOS         81  Minix / old Lin bf  Solaris        1  FAT12           27  Hidden NTFS Win 82  Linux swap / So c1  DRDOS/sec (FAT-2  XENIX root      39  Plan 9          83  Linux           c4  DRDOS/sec (FAT-3  XENIX usr       3c  PartitionMagic  84  OS/2 hidden or  c6  DRDOS/sec (FAT-4  FAT16 <32M      40  Venix 80286     85  Linux extended  c7  Syrinx         5  Extended        41  PPC PReP Boot   86  NTFS volume set da  Non-FS data    6  FAT16           42  SFS             87  NTFS volume set db  CP/M / CTOS / .7  HPFS/NTFS/exFAT 4d  QNX4.x          88  Linux plaintext de  Dell Utility   8  AIX             4e  QNX4.x 2nd part 8e  Linux LVM       df  BootIt         9  AIX bootable    4f  QNX4.x 3rd part 93  Amoeba          e1  DOS access     a  OS/2 Boot Manag 50  OnTrack DM      94  Amoeba BBT      e3  DOS R/O        b  W95 FAT32       51  OnTrack DM6 Aux 9f  BSD/OS          e4  SpeedStor      c  W95 FAT32 (LBA) 52  CP/M            a0  IBM Thinkpad hi ea  Rufus alignmente  W95 FAT16 (LBA) 53  OnTrack DM6 Aux a5  FreeBSD         eb  BeOS fs        f  W95 Ext'd (LBA) 54  OnTrackDM6      a6  OpenBSD         ee  GPT
10  OPUS            55  EZ-Drive        a7  NeXTSTEP        ef  EFI (FAT-12/16/
11  Hidden FAT12    56  Golden Bow      a8  Darwin UFS      f0  Linux/PA-RISC b
12  Compaq diagnost 5c  Priam Edisk     a9  NetBSD          f1  SpeedStor
14  Hidden FAT16 <3 61  SpeedStor       ab  Darwin boot     f4  SpeedStor
16  Hidden FAT16    63  GNU HURD or Sys af  HFS / HFS+      f2  DOS secondary
17  Hidden HPFS/NTF 64  Novell Netware  b7  BSDI fs         fb  VMware VMFS
18  AST SmartSleep  65  Novell Netware  b8  BSDI swap       fc  VMware VMKCORE
1b  Hidden W95 FAT3 70  DiskSecure Mult bb  Boot Wizard hid fd  Linux raid auto
1c  Hidden W95 FAT3 75  PC/IX           bc  Acronis FAT32 L fe  LANstep
1e  Hidden W95 FAT1 80  Old Minix       be  Solaris boot    ff  BBT
Hex code (type L to list all codes): 8e    ##8e表示这个分区将会被用作Linux LVM系统的一部分Changed type of partition 'Linux' to 'Linux LVM'.Command (m for help): t
Partition number (1-9, default 9): 8
Hex code (type L to list all codes): l0  Empty           24  NEC DOS         81  Minix / old Lin bf  Solaris        1  FAT12           27  Hidden NTFS Win 82  Linux swap / So c1  DRDOS/sec (FAT-2  XENIX root      39  Plan 9          83  Linux           c4  DRDOS/sec (FAT-3  XENIX usr       3c  PartitionMagic  84  OS/2 hidden or  c6  DRDOS/sec (FAT-4  FAT16 <32M      40  Venix 80286     85  Linux extended  c7  Syrinx         5  Extended        41  PPC PReP Boot   86  NTFS volume set da  Non-FS data    6  FAT16           42  SFS             87  NTFS volume set db  CP/M / CTOS / .7  HPFS/NTFS/exFAT 4d  QNX4.x          88  Linux plaintext de  Dell Utility   8  AIX             4e  QNX4.x 2nd part 8e  Linux LVM       df  BootIt         9  AIX bootable    4f  QNX4.x 3rd part 93  Amoeba          e1  DOS access     a  OS/2 Boot Manag 50  OnTrack DM      94  Amoeba BBT      e3  DOS R/O        b  W95 FAT32       51  OnTrack DM6 Aux 9f  BSD/OS          e4  SpeedStor      c  W95 FAT32 (LBA) 52  CP/M            a0  IBM Thinkpad hi ea  Rufus alignmente  W95 FAT16 (LBA) 53  OnTrack DM6 Aux a5  FreeBSD         eb  BeOS fs        f  W95 Ext'd (LBA) 54  OnTrackDM6      a6  OpenBSD         ee  GPT
10  OPUS            55  EZ-Drive        a7  NeXTSTEP        ef  EFI (FAT-12/16/
11  Hidden FAT12    56  Golden Bow      a8  Darwin UFS      f0  Linux/PA-RISC b
12  Compaq diagnost 5c  Priam Edisk     a9  NetBSD          f1  SpeedStor
14  Hidden FAT16 <3 61  SpeedStor       ab  Darwin boot     f4  SpeedStor
16  Hidden FAT16    63  GNU HURD or Sys af  HFS / HFS+      f2  DOS secondary
17  Hidden HPFS/NTF 64  Novell Netware  b7  BSDI fs         fb  VMware VMFS
18  AST SmartSleep  65  Novell Netware  b8  BSDI swap       fc  VMware VMKCORE
1b  Hidden W95 FAT3 70  DiskSecure Mult bb  Boot Wizard hid fd  Linux raid auto
1c  Hidden W95 FAT3 75  PC/IX           bc  Acronis FAT32 L fe  LANstep
1e  Hidden W95 FAT1 80  Old Minix       be  Solaris boot    ff  BBT
Hex code (type L to list all codes): 8eChanged type of partition 'Linux' to 'Linux LVM'.Command (m for help): p
Disk /dev/sda: 100 GiB, 107374182400 bytes, 209715200 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x3d3f9d05Device     Boot     Start       End   Sectors  Size Id Type
/dev/sda1  *         2048    616447    614400  300M 83 Linux
/dev/sda2          616448   8902655   8286208    4G 82 Linux swap / Solaris
/dev/sda3         8902656 104857599  95954944 45.8G 83 Linux
/dev/sda4       104857600 209715199 104857600   50G  5 Extended
/dev/sda5       104859648 125831167  20971520   10G 83 Linux
/dev/sda6       125833216 136318975  10485760    5G 83 Linux
/dev/sda7       136321024 157292543  20971520   10G 83 Linux
/dev/sda8       157294592 178266111  20971520   10G 8e Linux LVM
/dev/sda9       178268160 195045375  16777216    8G 8e Linux LVMCommand (m for help): w
The partition table has been altered.
Syncing disks.[root@localhost ~]# pvcreate /dev/sda8      ##创建物理卷Physical volume "/dev/sda8" successfully created.
[root@localhost ~]# pvcreate /dev/sda9Physical volume "/dev/sda9" successfully created.
[root@localhost ~]# pvs                      ##相当于pvdisplay命令PV         VG Fmt  Attr PSize  PFree /dev/sda8     lvm2 ---  10.00g 10.00g/dev/sda9     lvm2 ---   8.00g  8.00g
[root@localhost ~]# vgcreate vol1 /dev/sda8      ##创建卷组Volume group "vol1" successfully created
[root@localhost ~]# vgextend vol1 /dev/sda9      ##扩容卷组Volume group "vol1" successfully extended
[root@localhost partition1]# vgdisplay          ##相当于vgs命令--- Volume group ---VG Name               vol1System ID             Format                lvm2Metadata Areas        2Metadata Sequence No  5VG Access             read/writeVG Status             resizableMAX LV                0Cur LV                1Open LV               1Max PV                0Cur PV                2Act PV                2VG Size               17.99 GiBPE Size               4.00 MiBTotal PE              4606Alloc PE / Size       3840 / 15.00 GiBFree  PE / Size       766 / 2.99 GiBVG UUID               4PwAJq-vv0h-sPnM-sZJv-uCuA-drhe-LpsJUo
[root@localhost ~]# lvcreate -L 15G -n lvtest vol1     ##创建逻辑卷Logical volume "lvtest" created.
[root@localhost ~]# lvs                          ##相当于lvdisplayLV     VG   Attr       LSize  Pool Origin Data%  Meta%  Move Log Cpy%Sync Convertlvtest vol1 -wi-a----- 15.00g
[root@localhost ~]# lvdisplay--- Logical volume ---LV Path                /dev/vol1/lvtestLV Name                lvtestVG Name                vol1LV UUID                NZgXci-OYk3-eA2t-gzCz-40kt-l8Pf-3uFM6SLV Write Access        read/writeLV Creation host, time localhost.localdomain, 2022-08-18 15:27:43 +0800LV Status              available# open                 0LV Size                15.00 GiBCurrent LE             3840Segments               2Allocation             inheritRead ahead sectors     auto- currently set to     8192Block device           253:0[root@localhost ~]# mkfs.ext4 /dev/vol1/lvtest     ###文件系统格式化逻辑卷
mke2fs 1.45.6 (20-Mar-2020)
Creating filesystem with 3932160 4k blocks and 983040 inodes
Filesystem UUID: c21f5551-fb59-4ca3-af73-455d26c7bedd
Superblock backups stored on blocks: 32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208Allocating group tables: done
Writing inode tables: done
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: done   [root@localhost ~]# ls -l /mnt/partition1/
total 0
[root@localhost ~]# mount /dev/vol1/lvtest /mnt/partition1/  ###临时挂载
[root@localhost ~]# df -h
Filesystem               Size  Used Avail Use% Mounted on
devtmpfs                 881M     0  881M   0% /dev
tmpfs                    897M     0  897M   0% /dev/shm
tmpfs                    897M  9.5M  887M   2% /run
tmpfs                    897M     0  897M   0% /sys/fs/cgroup
/dev/sda3                 46G   19G   28G  41% /
/dev/sda5                9.8G   37M  9.3G   1% /mnt/mypartition
/dev/sda1                283M  198M   67M  75% /boot
vmhgfs-fuse              239G  135G  104G  57% /mnt/hgfs
tmpfs                    180M  1.2M  179M   1% /run/user/42
tmpfs                    180M     0  180M   0% /run/user/0
/dev/mapper/vol1-lvtest   15G   41M   14G   1% /mnt/partition1
[root@localhost ~]# cd /mnt/partition1
[root@localhost partition1]# touch 1.txt
[root@localhost partition1]# ll
total 16
-rw-r--r--. 1 root root     0 Aug 18 15:29 1.txt
drwx------. 2 root root 16384 Aug 18 15:28 lost+found

修改lvm

Linux LVM命令
命 令 功 能
vgchange 激活和禁用卷组
vgremove 删除卷组
vgextend 将物理卷加到卷组中
vgreduce 从卷组中删除物理卷
lvextend 增加逻辑卷的大小
lvreduce 减小逻辑卷的大小
lvremove 删除逻辑卷
#############################逻辑卷的创建、扩容、缩容#########################
二、逻辑卷的扩容1、将逻辑卷扩容1个G空间
pvcreate /dev/sda13
vgextend vol2 /dev/sda13
//lvextend -L +2G /dev/vol2/lvtest3
2、查看扩容后详细信息
//lvs
df -h
3、可以查看到LVM卷的大小变成了9 GB,但是挂载信息中没有发生变化,这时系统还识别不了新添加的磁盘文件系统,所以还需要对文件系统进行扩容
// resize2fs /dev/vol2/lvtest3逻辑卷lv的扩大
1】 卸载
umount /dev/myvg/mylv
2】强制检测
e2fsck -f /devmyvg/mylv
3】 扩大LV
lvextend -L +5000M /dev/myvg/mylv (在原来基础上增加5000M)
4】 扩大文件系统
resize2fs -f /dev/myvg/mylv +5000M
5】 挂载
mount /dev/myvg/mylv /share
6】 查看
df -h /share三、逻辑卷的缩容
1、首先将逻辑卷卸载挂载
//umount /mnt/partition4
2、进行磁盘检测
// e2fsck -f /dev/vol2/lvtest3
3、首先将文件系统先缩容至2G
// resize2fs /dev/vol2/lvtest3 2G
4、缩容逻辑卷
// lvresize -L 2G /dev/vol2/lvtest3
5、重新挂载、查看
//mount /dev/vol2/lvtest3 /mnt/partition4
// df -h

第9章        安装软件程序

aptitude默认的软件仓库位置是在安装Linux发行版时设置的。具体位置存储在文件
/etc/apt/sources. list中,如果需要为你的PMS添加一些额外的软件仓库,就在这个文件中设置吧

yum list installed    ##相当于rpm -qa     ##要找出系统上已安装的包
yum provides file_name   ##找出系统上的某个特定文件属于哪个软件包
yum list updates   ##列出所有已安装包的可用更新
yum update package_name  ##某个特定软件包需要更新
yum update     ##对更新列表中的所有包进行更新
yum install package_name -y    ##安装软件包
yum remove package_name    ##只删除软件包而保留配置文件和数据文件
yum erase package_name     ##要删除软件和它所有的文件

######################处理损坏的包依赖关系################################
有时在安装多个软件包时,某个包的软件依赖关系可能会被另一个包的安装覆盖掉。这叫作
损坏的包依赖关系(broken dependency)。
如果系统出现了这个问题,先试试下面的命令:
yum clean all
然后试着用yum命令的update选项。有时,只要清理了放错位置的文件就可以了。
如果这还解决不了问题,试试下面的命令:
yum deplist package_name
这个命令显示了所有包的库依赖关系以及什么软件可以提供这些库依赖关系。一旦知道某个
包需要的库,你就能安装它们了。
如果这样仍未解决问题,还有最后一招:
yum update --skip-broken
--skip-broken选项允许你忽略依赖关系损坏的那个包,继续去更新其他软件包。这可能
救不了损坏的包,但至少可以更新系统上的其他包。
########################################################################

要想知道你现在正从哪些仓库中获取软件,输入如下命令:yum repolist
yum的仓库定义文件位于/etc/yum.repos.d。有时这些仓库网站会提供一个可下载的rpm文件,可以用yum localinstall命令进行安装。这个rpm文件在安装过程会为你完成所有的仓库设置工作。

##############################CentOS 7.9配置yum源##############################
# 配置官方源更新地址:
[root@oldboy ~]# curl -s -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo
​
# 配置第三方epel源更新地址:
[root@oldboy ~]# curl -s -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo

rpm命令与源码安装见:
rpm包名软件包名程序名和rpm包源码包的说明_weixin_47680367的博客-CSDN博客Linux_RPM包详解目录什么是RPM包RPM包的特点RPM包的命名规则RPM包的安装RPM常用命令作者:@桃花换了酒钱本文为作者原创,转载请注明出处:Linux_RPM包详解 - yatolovefantasy - 博客园原文:Linux系统-RPM包详解 - luodenglin - 博客园Top什么是RPM包RPM(RedHat Package Manager)一种通过资料库管理的方式将所需要的软件安装到主机上的管理程序.TopRPM包的.https://blog.csdn.net/weixin_47680367/article/details/122345250?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22122345250%22%2C%22source%22%3A%22weixin_47680367%22%7D

[root@iZbp16mm3xbwen89azh9ffZ test]# rpm -qf `which mkfs.ext4`
e2fsprogs-1.42.9-13.el7.x86_64
[root@iZbp16mm3xbwen89azh9ffZ test]# rpm -q e2fsprogs   ##查看软件包是否安装
e2fsprogs-1.42.9-13.el7.x86_64
[root@iZbp16mm3xbwen89azh9ffZ test]# rpm -q e2fsprogs1
package e2fsprogs1 is not installed

第10章      使用编辑器

连续使用ls –l命令来查找一系列链接文件的最终目标,只需要使用readlink –f命令就可以了。它能够立刻找出链接文件的最后一环

GNOME是一套纯粹自由的计算机软件,运行在操作系统上,提供图形桌面环境。

vim编辑器,在普通模式下:
 G:移到缓冲区的最后一行。
 num G:移动到缓冲区中的第num行。
 gg:移到缓冲区的第一行
o:光标所在当前行新增下一行,并进入插入模式
O:光标所在当前行新增上一行,并进入插入模式
D:删除光标所在行所在位置后到行尾的内容
vim编辑器在普通模式下有个特别的功能叫命令行模式。在命令行模式下有几个命令可以将缓冲区的数据保存到文件中并退出vim。
 q:如果未修改缓冲区数据,退出。
 q!:取消所有对缓冲区数据的修改并退出。
 w filename:将文件保存到另一个文件中。
 wq:将缓冲区数据保存到文件中并退出。

复制和粘贴:v(进入可视模式)→y(复制)→p(粘贴)

查找和替换:
可以使用vim查找命令来轻松查找缓冲区中的数据。要输入一个查找字符串,就按下斜线(/)
键。光标会跑到消息行,然后vim会显示出斜线。在输入你要查找的文本后,按下回车键。vim
编辑器会采用以下三种回应中的一种。
 如果要查找的文本出现在光标当前位置之后,则光标会跳到该文本出现的第一个位置。
 如果要查找的文本未在光标当前位置之后出现,则光标会绕过文件末尾,出现在该文本
所在的第一个位置(并用一条消息指明)。
 输出一条错误消息,说明在文件中没有找到要查找的文本。
要继续查找同一个单词,按下斜线键,然后按回车键。或者使用n键,表示下一个(next)。
替换命令允许你快速用另一个单词来替换文本中的某个单词。必须进入命令行模式才能使用
替换命令。替换命令的格式是:
:s/old/new/
vim编辑器会跳到old第一次出现的地方,并用new来替换。可以对替换命令作一些修改来替
换多处文本。
 :s/old/new/g:一行命令替换所有old。
 :n,ms/old/new/g:替换行号n和m之间所有old。
 :%s/old/new/g:替换整个文件中的所有old。
 :%s/old/new/gc:替换整个文件中的所有old,但在每次出现时提示。

第11章        构建基本脚本

通过${variable}形式引用的变量。变量名两侧额外的花括号通常用来帮助识别美元符后的变量名。
用户变量可以是任何由字母、数字或下划线组成的文本字符串,长度不超过20个。用户变量区分大小写,所以变量Var1和变量var1是不同的。

命令替换会创建一个子shell来运行对应的命令。子shell(subshell)是由运行该脚本的shell
所创建出来的一个独立的子shell(child shell)。正因如此,由该子shell所执行命令是无法
使用脚本中所创建的变量的。在命令行提示符下使用路径./运行命令的话,也会创建出子shell,要是运行命令的时候不加入路径,就不会创建子shell。如果你使用的是内建的shell命令,并不会涉及子shell。在命令行提示符下运行脚本时一定要留心!

[root@iZbp16mm3xbwen89azh9ffZ test]# echo "time is `date`"
time is Fri Aug 19 10:23:51 CST 2022[dongm@localhost ~]$ echo `echo $BASH_SUBSHELL`
1
##命令替换会创建一个子shell来运行对应的命令。子shell(subshell)是由运行该脚本的shell
所创建出来的一个独立的子shell(child shell)。正因如此,由该子shell所执行命令是无法
使用脚本中所创建的变量的。[root@iZbp16mm3xbwen89azh9ffZ test]# today=`date +%y%m%d`
[root@iZbp16mm3xbwen89azh9ffZ test]# echo $today
220819
[root@iZbp16mm3xbwen89azh9ffZ test]# ll >log.$today
[root@iZbp16mm3xbwen89azh9ffZ test]# ll
total 28
-rw-r--r-- 1 root  root  12660 Aug 19 09:10 1.txt
lrwxrwxrwx 1 dongm dongm     5 Aug 18 09:25 2.txt -> 1.txt
-rw-r--r-- 1 root  root     38 Aug 19 10:12 3.txt
-rw-r--r-- 1 root  root    272 Aug 19 10:27 log.220819
drwxrwxr-x 2 test  dongm  4096 Aug 18 09:52 test
[root@iZbp16mm3xbwen89azh9ffZ test]# cat log.220819
total 24
-rw-r--r-- 1 root  root  12660 Aug 19 09:10 1.txt
lrwxrwxrwx 1 dongm dongm     5 Aug 18 09:25 2.txt -> 1.txt
-rw-r--r-- 1 root  root     38 Aug 19 10:12 3.txt
-rw-r--r-- 1 root  root      0 Aug 19 10:27 log.220819
drwxrwxr-x 2 test  dongm  4096 Aug 18 09:52 test############################脚本中使用bc计算器################################
[root@iZbp16mm3xbwen89azh9ffZ test]# cat bc.sh
#!/bin/bash
var1=1.55
var2=3.66
var3=10
var4=2.12
var5=$(bc<<eof
scale=4
a=($var1 * $var2)   ##bash计算器实际上是一种编程语言,有自己的运算表达式,bc引用bc程序
b=($var3 / $var4)   外脚本内的变量时需要用$符号引用变量
a+b
eof
)
echo "the final result is $var5"
[root@iZbp16mm3xbwen89azh9ffZ test]# sh bc.sh
the final result is 10.3899
########在bash计算器中创建的变量只在bash计算器中有效,不能在shell脚本中使用。

第12章        使用结构化命令

if condition;then
command
else
command
fi                     ###第一种if condition;then
command
elif condition;then
command
elif condition;then
command
else
command
fi                ###第二种case variable in
pattern1 | pattern2) commands1;;
pattern3) commands2;;
*) default commands
esac

test命令:方括号是与test命令同义的特殊bash命令

test命令的数值比较功能
比 较 描 述
n1 -eq n2 检查n1是否与n2相等
n1 -ge n2 检查n1是否大于或等于n2
n1 -gt n2 检查n1是否大于n2
n1 -le n2 检查n1是否小于或等于n2
n1 -lt n2 检查n1是否小于n2
n1 -ne n2 检查n1是否不等于n2
字符串比较测试
比 较 描 述
str1 = str2 检查str1是否和str2相同
str1 != str2 检查str1是否和str2不同
str1 < str2 检查str1是否比str2小
str1 > str2 检查str1是否比str2大
-n str1 检查str1的长度是否非0
-z str1 检查str1的长度是否为0

在比较字符串的相等性时,比较测试会将所有的标点和大小写情况都考虑在内
 大于号和小于号必须转义,否则shell会把它们当作重定向符号,把字符串值当作文件
名;
 大于和小于顺序和sort命令所采用的不同。
test命令和测试表达式使用标准的数学比较符号来表示字符串比较,而用文本代码来表
示数值比较。这个细微的特性被很多程序员理解反了。如果你对数值使用了数学运算符
号,shell会将它们当成字符串值,可能无法得到正确的结果。

[root@iZbp16mm3xbwen89azh9ffZ dongm]# echo $value[root@iZbp16mm3xbwen89azh9ffZ dongm]# if [ -n $value ];then
> echo 0
> else
> echo 1
> fi
0
[root@iZbp16mm3xbwen89azh9ffZ dongm]#
[root@iZbp16mm3xbwen89azh9ffZ dongm]# if [ -n "$value" ];then
> echo 0
> else
> echo 1
> fi
1
-n和-z判断字符串长度的时候需要给判断的变量加上"",否则会出现错误的结果
test命令的文件比较功能
比 较 描 述
-d file 检查file是否存在并是一个目录
-e file 检查file是否存在
-f file 检查file是否存在并是一个文件
-r file 检查file是否存在并可读
-s file 检查file是否存在并非空
-w file 检查file是否存在并可写
-x file 检查file是否存在并可执行
-O file 检查file是否存在并属当前用户所有
-G file 检查file是否存在并且默认组与当前用户相同
file1 -nt file2 检查file1是否比file2新
file1 -ot file2 检查file1是否比file2旧

-nt与-ot比较文件之前先测试文件是否存在,若文件不存在,比较会返回一个错误的结果

[root@iZbp16mm3xbwen89azh9ffZ dongm]# cat if4.sh
#!/bin/bash
item=/etc/shadow
if [ -f $item ];then
echo "$item is a file"
echo "test if you can read it"
if [ -r $item ];then
echo "you can read $item"
tail -2  $item
else
echo "you can not read it"
fi
else
echo "$item is not a file"
fi
[root@iZbp16mm3xbwen89azh9ffZ dongm]# sh if4.sh
/etc/shadow is a file
test if you can read it
you can read /etc/shadow
test:$6$AY48Q/GU$FC8FD1d85sqSCWxDW.fwbfuaBr5lPiwP4Iuf2up8cTOqjT7emvmT0Wn5oPIkEDXpuQBKZor3.U5UOTwDittQI/:19221:0:99999:7:::
dongm:$6$ojQAuKHT$uo0EXmDfQVRuHW/vND7kgrIwzURXEYYNwpHsV/QrhwCuOVmhVn1rBGhNPr8LoD7mHUH4nc6xS5blhYQzMBBU40:19222:0:99999:7:::
[root@iZbp16mm3xbwen89azh9ffZ dongm]# su - dongm        ###root切换为其他用户
Last login: Fri Aug 19 16:16:40 CST 2022 on pts/0
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ sh if4.sh
/etc/shadow is a file
test if you can read it
you can not read it########### -O 测试
[root@iZbp16mm3xbwen89azh9ffZ dongm]# cat if9.sh
#!/bin/bash
if [ -O /etc/shadow ];then
echo 0
else
echo 1
fi
[root@iZbp16mm3xbwen89azh9ffZ dongm]# sh if9.sh
0
[root@iZbp16mm3xbwen89azh9ffZ dongm]# su - dongm
Last login: Fri Aug 19 16:18:19 CST 2022 on pts/0
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ sh if9.sh
1

复合条件测试:&&与||
双括号特性:
1.双括号命令允许你在比较过程中使用高级数学表达式(可以在if语句中用双括号命令,也可以在脚本中的普通命令里使用来赋值。)
2.不需要将双括号中表达式里的大于号转义

##双括号命令允许你在比较过程中使用高级数学表达式,双括号命令的格式如下:
(( expression ))
expression可以是任意的数学赋值或比较表达式
dongm@localhost 12]$ cat test17.sh
#!/bin/bash
#using double parenthesis
val1=10
if (( $val1 ** 2 > 90));then
((val2=$val1**2))
#val2=$[$val1**2]
echo "the square of $val1 is $val2"
fi
[dongm@localhost 12]$ sh test17.sh
the square of 10 is 100

双方括号里的expression使用了test命令中采用的标准字符串比较。但它提供了test命令未提供的另一个特性——模式匹配(pattern matching)。在模式匹配中,可以定义一个正则表达式来匹配字符串值。双方括号在bash shell中工作良好。不是所有的shell都支持双方括号。
例如: [[ $USER == r* ]]

[dongm@localhost 12]$ if ((5>2));then
> echo "5 is greater than 2"
> else
> echo "5 is smaller than 2"
> fi
5 is greater than 2
##(( expression )),expression可以是任意的数学赋值或比较表达式,不需要将双括号中表达式里的大于号转义。这是双括号命令提供的另一个高级特性[dongm@localhost 12]$ if [[ $USER == d* ]];then echo "welcome $USER"; else  echo "i am not d*"; fi
welcome dongm
##双等号将右边的字符串(r*)视为一个模式,并应用模式匹配规则。

第13章        更多的结构化命令

每个循环都必须改变在测试条件中用到的值,否则循环就会无止尽进行下去
############for循环
for var in list
do
commands
done##如何正确的执行数学运算
$ cat for.sh
#!/bin/bash
#
total=0
for value in `seq 10`
do
total+=$value
done
echo "total is $total"
echo ------------------------------------------
total=0
for value in `seq 10`
do
((total+=$value))
done
echo "total is $total"
$ sh for.sh
total is 012345678910
------------------------------------------
total is 55##用通配符读取目录,可以用for命令来自动遍历目录中的文件。进行此操作时,
必须在文件名或路径名中使用通配符。它会强制shell使用文件扩展匹配。
文件扩展匹配是生成匹配指定通配符的文件名或路径名的过程。
[root@localhost dongm]# for file in /home/dongm/* /home/badfile
> do
> if [ -f "$file" ];then
> echo "$file is a file"
> elif [ -d "$file" ];then
> echo "$file is a dir"
> else
> echo "$file does not exist"
> fi
> done
/home/dongm/1.txt is a file
/home/dongm/2 is a dir
/home/dongm/bashrc is a file
/home/dongm/for1.sh is a file
/home/badfile does not exist
[root@localhost dongm]# for file in /home/dongm/.b* /home/badfile
> do
> if [ -f "$file" ];then
> echo "$file is a file"
> elif [ -d "$file" ];then
> echo "$file is a dir"
> else
> echo "$file does not exist"
> fi
> done
/home/dongm/.bash_history is a file
/home/dongm/.bash_logout is a file
/home/dongm/.bash_profile is a file
/home/dongm/.bashrc is a file
/home/badfile does not exist#############################C风格的for循环#####################################
for (i = 0; i < 10; i++)
{
printf("The next number is %d\n", i);
}
这段代码产生了一个简单的迭代循环,其中变量i作为计数器。第一部分将一个默认值赋给
该变量。中间的部分定义了循环重复的条件。当定义的条件不成立时,for循环就停止迭代。最
后一部分定义了迭代的过程。在每次迭代之后,最后一部分中定义的表达式会被执行。在本例中,
i变量会在每次迭代后增一。[dongm@localhost 13]$ cat for2.sh
#!/bin/bash
for ((i=1;i<=5;i++));doecho "the next number is $i"
done
echo "the final number is $i"
[dongm@localhost 13]$ sh for2.sh
the next number is 1
the next number is 2
the next number is 3
the next number is 4
the next number is 5
the final number is 6
for循环通过定义好的变量(本例中是变量i)来迭代执行这些命令。在每次迭代中,$i变
量包含了for循环中赋予的值。在每次迭代后,循环的迭代过程会作用在变量上,在本例中,变
量增一。#####while命令
while命令的格式是:
while test command
do
other commands
done
while命令中定义的test command和if-then语句中的格式一模一样。可
以使用任何普通的bash shell命令,或者用test命令进行条件测试,比如测试变量值。
while命令允许你在while语句行定义多个测试命令。只有最后一个测试命令的退出状态码
会被用来决定什么时候结束循环。每个测试命令都出现在单独的一行上。
[root@localhost dongm]# echo $var1
5
[root@localhost dongm]# while [ $var1 -ge 0 ];do
> echo $var1
> ((var1-=1))
> done
5
4
3
2
1
0###until命令
until命令的格式是:
until test commands
do
other commands
done
until命令要求你指定一个通常返回非零退出状态码的测试命令。只有测试命令的退出状态码不为0,
bash shell才会执行循环中列出的命令。一旦测试命令返回了退出状态码0,循环就结束了。
你可以在until命令语句中放入多个测试命令。只有最后一个命令的退出状态码决定了
bash shell是否执行已定义的other commands。
[root@localhost dongm]# var2=100
[root@localhost dongm]# until echo $var2
> [ $var2 -eq 0 ]
> do
> echo inside loop:$var2
> var2=$[ $var2 - 25 ]     ##((var1=var1-25))  双括号高级特性引用var1变量前没有加$
> done
100
inside loop:100
75
inside loop:75
50
inside loop:50
25
inside loop:25
0[dongm@localhost ~]$ echo "scale=4;1/5"|bc
.2000
[dongm@localhost ~]$ bc<<eof
> scale=4
> 1/5
> eof
.2000##实例
[root@localhost dongm]# cat for3.sh
#!/bin/bash
IFS=$'\n'
for entry in `tail -2 /etc/passwd`
do
echo "values in $entry -"
IFS=:
for value in $entry
do
echo $value
done
done
[root@localhost dongm]# sh for3.sh
values in dongm:x:1116:1000::/home/dongm:/bin/bash -
dongm
x
1116
1000/home/dongm
/bin/bash
values in tcpdump:x:72:72::/:/sbin/nologin -
tcpdump
x
72
72/
/sbin/nologin####break n
[root@localhost dongm]# for ((a=1;a<=5;a++));do
> echo outer loop:$a
> var100=1
> while [ $var100 -le 5 ];do
> echo inside loop:$var100
> ((var100+=1))
> if [ $var100 -eq 3 ];then
> break 2
> fi
> done
> done
outer loop:1
inside loop:1
inside loop:2###continue与continue n
$ cat test21
#!/bin/bash
# using the continue command
for (( var1 = 1; var1 < 8; var1++ ))
do
if [ $var1 -gt 2 ] && [ $var1 -lt 6 ]
then
continue
fi
echo "Iteration number: $var1"
done
$ ./test21
Iteration number: 1
Iteration number: 2
Iteration number: 6
Iteration number: 7
####当if-then语句的条件被满足时(值大于2且小于6),shell会执行continue命令,跳过此
次循环中剩余的命令,但整个循环还会继续。当if-then的条件不再被满足时,一切又回到正轨。
记住,当shell执行continue命令时,它会跳过剩余的命令。
如果你在其中某个条件里对测试条件变量进行增值,问题就会出现######continue n  其中n定义了循环层级
[root@localhost dongm]# cat continue.sh
#!/bin/bash
#using continue command
for ((a=1;a<3;a++));doecho "outer loop:$a"for ((b=1;b<5;b++));do
if [ $b -gt 1 ]&&[ $b -lt 4 ];then
continue fiecho "  inner loop:$b"done
done
echo -------------------------------------
for ((a=1;a<3;a++));doecho "outer loop:$a"for ((b=1;b<5;b++));do
if [ $b -gt 1 ]&&[ $b -lt 4 ];then
continue 2fiecho " inner loop:$b"done
done
[root@localhost dongm]# sh continue.sh
outer loop:1inner loop:1inner loop:4
outer loop:2inner loop:1inner loop:4
-------------------------------------
outer loop:1inner loop:1
outer loop:2inner loop:1####处理循环的输出
在shell脚本中,你可以对循环的输出使用管道或进行重定向。这可以通过在done命令
之后添加一个处理命令来实现。
[root@localhost dongm]# for ((a=1;a<=5;a++));do
> echo $a
> done > output.txt
[root@localhost dongm]# cat output.txt
1
2
3
4
5###查找可执行文件
[root@localhost dongm]# cat path.sh
#!/bin/bash
#iterate PTAH
IFS=:
for folder in $PATH
do
echo "$folder:"
for file in $folder/*
do
if [ -x "$file" ]
then
echo " $file"
fi
done
done
[root@localhost dongm]# sh path.sh |less
/usr/local/sbin:
/usr/local/bin:
/usr/sbin:/usr/sbin/abrt-auto-reporting/usr/sbin/abrt-configuration/usr/sbin/abrtd/usr/sbin/abrt-dbus/usr/sbin/abrt-harvest-pstoreoops/usr/sbin/abrt-harvest-vmcore/usr/sbin/abrt-install-ccpp-hook/usr/sbin/abrt-server/usr/sbin/accessdb.....................####创建多个用户账户
[root@localhost dongm]# cat users.csv
uu,bbbb
dd,dddd
cc,ffff
[root@localhost dongm]# cat useradd.sh
#!/bin/bash
#process new user accounts
input="users.csv"
while IFS=, read -r userid name  ##可以用while IFS=, read userid name
do
echo "adding $userid"
useradd -c "$name" -m $userid
echo "123456"|passwd --stdin $userid   ##给用户设置密码
done <$input
#######read -r参数说明
-r :在参数输入中,我们可以使用’/’表示没有输入完,换行继续输入,
如果我们需要行最后的’/’作为有效的字符,可以通过-r来进行。
此外在输入字符中,我们希望/n这类特殊字符生效,也应采用-r选项。#####################看一看下面1个字段和2个字段的区别##################
[root@localhost 13]# cat account
rich,Richard Blum
christine,Christine Bresnahan
barbara,Barbara Blum
tim,Timothy Bresnahan
[root@localhost 13]# cat account |while IFS=, read username remark;do
> echo $username
> done
rich
christine
barbara
tim
[root@localhost 13]# cat account |while IFS=, read username;do
> echo $username
> done
rich,Richard Blum
christine,Christine Bresnahan
barbara,Barbara Blum
tim,Timothy Bresnahan
#######################################################################

小结:
循环是编程的一部分。bash shell提供了三种可用于脚本中的循环命令。
for命令允许你遍历一系列的值,不管是在命令行里提供好的、包含在变量中的还是通过文
件扩展匹配获得的文件名和目录名。
while命令使用普通命令或测试命令提供了基于命令条件的循环。只有在命令(或条件)产
生退出状态码0时,while循环才会继续迭代指定的一组命令。
until命令也提供了迭代命令的一种方法,但它的迭代是建立在命令(或条件)产生非零退
出状态码的基础上。这个特性允许你设置一个迭代结束前都必须满足的条件。
可以在shell脚本中对循环进行组合,生成多层循环。bash shell提供了continue和break命
令,允许你根据循环内的不同值改变循环的正常流程。
bash shell还允许使用标准的命令重定向和管道来改变循环的输出。你可以使用重定向来将循
环的输出重定向到一个文件或是另一个命令。这就为控制shell脚本执行提供了丰富的功能。

第14章        处理用户输入

bash shell提供了一些不同的方法来从用户处获得数据,包括命令行参数(添加在命令后的数据)、命令行选项(可修改命令行为的单个字母)以及直接从键盘读取输入的能力

bash shell会将一些称为位置参数(positional parameter)的特殊变量分配给输入到命令行中的
所有参数。位置参数变量是标准的数字:$0是程序名,$1是第一个参数,$2是第二个参数,依次类推,在第9个变量之后,你必须在变量数字周围加上花括号,比如${10}。

特殊变量$#含有脚本运行时携带的命令行参数的个数,那么变量${!#}就代表了最后一个命令行参数变量

[root@iZbp16mm3xbwen89azh9ffZ dongm]# cat parameter4.sh
#!/bin/bash
param=$#
echo "there were $param parameters supplied"
echo "the last parameter is ${!#}"
[root@iZbp16mm3xbwen89azh9ffZ dongm]# sh parameter4.sh
there were 0 parameters supplied
the last parameter is
[root@iZbp16mm3xbwen89azh9ffZ dongm]# bash parameter4.sh
there were 0 parameters supplied
the last parameter is parameter4.sh
[root@iZbp16mm3xbwen89azh9ffZ dongm]# bash parameter4.sh 1 2 3 4 5 8
there were 6 parameters supplied
the last parameter is 8
#当脚本文件使用特殊变量${!#}时,不能用sh scriptname执行脚本,
可以使用bash scriptname或者./scriptname执行脚本,$#为0时,${!#}返回脚本名###  $*变量会将命令行上提供的所有参数当作一个单词保存。
基本上$*变量会将这些参数视为一个整体,而不是多个个体。
$@变量会将命令行上提供的所有参数当作同一字符串中的多个独立的单词
[root@iZbp16mm3xbwen89azh9ffZ dongm]# cat parameter5.sh
#!/bin/bash
count=1
for param in $*;doecho "\$* parameter #$count:$param"((count+=1))
done
echo ---------------------------------------------
count=1
for param in "$*";doecho "\$* parameter #$count:$param"((count+=1))
done
echo ---------------------------------------------
count=1
for param in "$@";doecho "\$@ parameter #$count:$param"((count+=1))
done
[root@iZbp16mm3xbwen89azh9ffZ dongm]# sh parameter5.sh 2 4 6 8 9
$* parameter #1:2
$* parameter #2:4
$* parameter #3:6
$* parameter #4:8
$* parameter #5:9
---------------------------------------------
$* parameter #1:2 4 6 8 9
---------------------------------------------
$@ parameter #1:2
$@ parameter #2:4
$@ parameter #3:6
$@ parameter #4:8
$@ parameter #5:9##shift n
使用shift命令的时候要小心。如果某个参数被移出,它的值就被丢弃了,无法再恢复
[root@iZbp16mm3xbwen89azh9ffZ dongm]# cat shift.sh
#!/bin/bash
count=1
while [ -n "$1" ]
do
echo "Parameter #$count = $1"
((count+=1))
shift
done
[root@iZbp16mm3xbwen89azh9ffZ dongm]# sh shift.sh 2 4  6 8
Parameter #1 = 2
Parameter #2 = 4
Parameter #3 = 6
Parameter #4 = 8
#####处理命令行选项与参数,这种方法多个选项放进一个参数中时,它就不能工作了
[root@iZbp16mm3xbwen89azh9ffZ dongm]# cat shift2.sh
#!/bin/bash
while [ -n "$1" ]
do
case "$1" in
-a) echo "found -a opiton";;
-b) param=$2
echo "found -b option,with parameter $param"
shift;;
-c) echo "found -c option";;
--) shift
break;;
*) echo "$1 is not an option"
esac
shift
done
count=1
for value in "$@"
do
echo "#$count parameter:$value"
((count+=1))
done
[root@iZbp16mm3xbwen89azh9ffZ dongm]# sh shift2.sh -a -b test -c -d -e -- 1 2 3 4 5
found -a opiton
found -b option,with parameter test
found -c option
-d is not an option
-e is not an option
#1 parameter:1
#2 parameter:2
#3 parameter:3
#4 parameter:4
#5 parameter:5###getopt处理命令行选项与参数,但是getopt命令并不擅长处理带空格和引号的参数值。它会将空格当作参数分隔符,而不是根据双引号将二者当作一个参数。
[root@iZbp16mm3xbwen89azh9ffZ dongm]# cat shift3.sh
#!/bin/bash
set -- $(getopt -q ab:cd "$@")
while [ -n "$1" ]
do
case "$1" in
-a) echo "found -a option";;
-b) param=$2
echo "found -b option,with parameter $param"
shift;;
-c) echo "found -c option";;
-d) echo "found -d option";;
--) shift
break;;
*) echo "$1 is not an option"
esac
shift
done
count=1
for value in "$@"
do
echo "Parameter #$count:$value"
((count+=1))
done
[root@iZbp16mm3xbwen89azh9ffZ dongm]# sh shift3.sh -a -btest -cde  test1 test2 test3
found -a option
found -b option,with parameter 'test'
found -c option
found -d option
Parameter #1:'test1'
Parameter #2:'test2'
Parameter #3:'test3'####getopts处理选项和参数
[root@iZbp16mm3xbwen89azh9ffZ dongm]# cat shift4.sh
#!/bin/bash
while getopts :ab:cd value     #a前面的冒号表示忽略错误,b后面的冒号表示b选项带参数
do
case "$value" in
a) echo "found -a option";;
b) echo "found -b option,with parameter $OPTARG";;
c) echo "found -c option";;
d) echo "fount -d option";;
*) echo "unkown option:$value"
esac
done
shift $[ $OPTIND - 1 ]
count=1
for param in "$@"
do
echo "Paremeter #$count:$param"
((count+=1))
done
[root@iZbp16mm3xbwen89azh9ffZ dongm]# sh shift4.sh -abtest -cde test1 test2 test3
found -a option
found -b option,with parameter test
found -c option
fount -d option
unkown option:?
Paremeter #1:test1
Paremeter #2:test2
Paremeter #3:test3
###每次调用optgets时,它一次只处理命令行上检测到的一个参数。处理完所有的参数后,它会退出
并返回一个大于0的退出状态码。getopts命令将当前参数保存在命令行中定义的variable中。
getopts命令会用到两个环境变量。如果选项需要跟一个参数值,OPTARG环境变量就会保
存这个值。OPTIND环境变量保存了参数列表中getopts正在处理的参数位置。
read命令            ##选项有-t,-n,-s
[dongm@localhost fuxi3]$ read value
1111
[dongm@localhost fuxi3]$ echo $value
1111#-t选项指定了read命令等待输入的秒数。当计时器过期后,read命令会返回一个非零退出状态码。
[dongm@localhost fuxi3]$ if read -t 5 -p "enter your value: " value; then  echo "ok"; else echo "\nbye"; fi
enter your value: \nbye
[dongm@localhost fuxi3]$ if read -t 5 -p "enter your value: " value; then  echo "ok"; else echo -e  "\nbye"; fi
enter your value:
bye
[dongm@localhost fuxi3]$ if read -t 5 -p "enter your value: " value; then  echo "ok"; else echo  "bye"; fi
enter your value: bye###从文件中读取
[root@iZbp16mm3xbwen89azh9ffZ dongm]# cat test
The quick brown dog jumps over the lazy fox.
This is a test, this is only a test.
O Romeo, Romeo! Wherefore art thou Romeo?
[root@iZbp16mm3xbwen89azh9ffZ dongm]# cat read4.sh
#!/bin/bash
count=1
cat test|while read line
do
echo "Line #$count:$line"
((count+=1))
done
echo "Finished processing the file"
[root@iZbp16mm3xbwen89azh9ffZ dongm]# sh read4.sh
Line #1:The quick brown dog jumps over the lazy fox.
Line #2:This is a test, this is only a test.
Line #3:O Romeo, Romeo! Wherefore art thou Romeo?
Finished processing the file

第15章        呈现数据

Linux系统将每个对象当作文件处理。这包括输入和输出进程。Linux用文件描述符(file
descriptor)来标识每个文件对象。

###标准输出文件描述符为1,标准错误文件描述符为2
[root@iZbp16mm3xbwen89azh9ffZ dir.xrbsFt]# echo 1 8>test
1
[root@iZbp16mm3xbwen89azh9ffZ dir.xrbsFt]# ll
total 0
-rw-r--r-- 1 root root 0 Aug 22 17:05 test
[root@iZbp16mm3xbwen89azh9ffZ dir.xrbsFt]# cat test[root@localhost 15]# ls
1.txt  testfile
[root@localhost 15]# ls -l >2.txt    ----重定向符号会先创建文件
[root@localhost 15]# cat 2.txt
total 8
-rw-r--r--. 1 root root 15 Nov 25 10:17 1.txt
-rw-r--r--. 1 root root  0 Nov 25 10:17 2.txt
-rw-r--r--. 1 root root 43 Nov 25 10:14  testfile###这个方法非常适合在脚本中生成错误消息。如果有人用了你的脚本,
他们可以像上面的例子中那样轻松地通过STDERR文件描述符重定向错误消息
$ cat test8
#!/bin/bash
# testing STDERR messages
echo "This is an error" >&2
echo "This is normal output"
$ ./test8 2> test9
This is normal output
$ cat test9
This is an error
[dongm@localhost 15]$ ls
1.sh  test12.sh  test2  testfile
[dongm@localhost 15]$ date >>1.txt    ##  >>也会生成文件
[dongm@localhost 15]$ ls
1.sh  1.txt  test12.sh  test2  testfile##exec命令会启动一个新shell并将文件描述符重定向到文件
[root@iZbp16mm3xbwen89azh9ffZ dongm]# cat test.sh
#!/bin/bash
exec 2>testerror
echo "this is the normal line."
exec 1>testfile
echo "this should go to the testfile"
echo "this should go to the testerror" >&2
[root@iZbp16mm3xbwen89azh9ffZ dongm]# sh test.sh
this is the normal line.
[root@iZbp16mm3xbwen89azh9ffZ dongm]# cat testfile
this should go to the testfile
[root@iZbp16mm3xbwen89azh9ffZ dongm]# cat testerror
this should go to the testerror##用exec命令在脚本中重定向输入
[root@iZbp16mm3xbwen89azh9ffZ dongm]# cat test1.sh
#!/bin/bash
exec 0<test1file
count=1
while read line
do
echo "Line #$count:$line"
((count+=1))
done
[root@iZbp16mm3xbwen89azh9ffZ dongm]# sh test1.sh
Line #1:this is the first line
Line #2:this is the second line
Line #3:this is the third line###创建输出文件描述符
exec 3>testout    ###exec 3>>testout
###重定向文件描述符
重定向输出:exec 3>&1  exec 1>testout    恢复:exec 1>&3
重定向输入:exec 6<&0  exec 0<testfile    恢复:exec 0<&6
[root@iZbp16mm3xbwen89azh9ffZ dongm]# cat test3.sh ##重定向输出
#!/bin/bash
exec 5>&1
exec 1>testout
echo "this is a test"
exec 1>&5
echo "this is a normal line"
[root@iZbp16mm3xbwen89azh9ffZ dongm]# sh test3.sh
this is a normal line
[root@iZbp16mm3xbwen89azh9ffZ dongm]# cat testout
this is a test[root@iZbp16mm3xbwen89azh9ffZ dongm]# cat test4.sh ##重定向输入
#!/bin/bash
exec 6<&0
exec <testfile
read line
echo $line
exec 0<&6
read -p "enter a value: " value
echo $value
[root@iZbp16mm3xbwen89azh9ffZ dongm]# cat testfile
this is the first line
this is the second line
[root@iZbp16mm3xbwen89azh9ffZ dongm]# sh test4.sh
this is the first line
enter a value: 1111111111
1111111111####关闭文件描述符
####如果你创建了新的输入或输出文件描述符,shell会在脚本退出时自动关闭它们。
[root@iZbp16mm3xbwen89azh9ffZ dongm]# cat test.sh
#!/bin/bash
# testing closing file descriptors
exec 3>test1file      ##打开文件描述符
echo 111111111 >&3
exec 3>&-             ##关闭文件描述符
echo 222222222 >&3
cat  test1file
exec 3>test1file      ##重新打开文件描述符,重定向符号>会重新创建新文件
echo "this is the last line" >&3
[root@iZbp16mm3xbwen89azh9ffZ dongm]# sh test.sh
test.sh: line 6: 3: Bad file descriptor
111111111
[root@iZbp16mm3xbwen89azh9ffZ dongm]# cat test1file
this is the last line####列出打开的文件描述符
[root@iZbp16mm3xbwen89azh9ffZ dongm]# cat test6.sh
#!/bin/bash
##testing lsof with file descriptors
exec 3>test3file
exec 5>test5file
exec 6<test6file
lsof -a -p $$ -d 0,1,2,3,4,5,6,7,8,9 #-a选项用来对其他两个选项的结果执行布尔AND运算
[root@iZbp16mm3xbwen89azh9ffZ dongm]# bash test6.sh
COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF    NODE NAME
bash    13797 root    0u   CHR  136,0      0t0       3 /dev/pts/0
bash    13797 root    1u   CHR  136,0      0t0       3 /dev/pts/0
bash    13797 root    2u   CHR  136,0      0t0       3 /dev/pts/0
bash    13797 root    3w   REG  253,1        0 1055099 /home/dongm/test3file
bash    13797 root    5w   REG  253,1        0 1055512 /home/dongm/test5file
bash    13797 root    6r   REG  253,1        0 1055513 /home/dongm/test6file##清空文件数据
cat /dev/null >file##### mktemp命令
当前目录下创建文件:mktemp test.XXXXXX
当前目录下创建目录:mktemp -d test.XXXXXX
/tmp目录下创建文件:mktemp -t test.XXXXXX
/tmp目录下创建目录:mktemp -td test.XXXXXX## tee命令
[root@iZbp16mm3xbwen89azh9ffZ ~]# date|tee testfile
Mon Aug 22 17:20:34 CST 2022
[root@iZbp16mm3xbwen89azh9ffZ ~]# cat testfile
Mon Aug 22 17:20:34 CST 2022
[root@iZbp16mm3xbwen89azh9ffZ ~]# who|tee -a testfile #-a选项表示追加数据到文件中
root     pts/0        2022-08-22 08:49 (36.7.136.111)
root     pts/1        2022-08-22 16:26 (36.7.136.111)
[root@iZbp16mm3xbwen89azh9ffZ ~]# cat testfile
Mon Aug 22 17:20:34 CST 2022
root     pts/0        2022-08-22 08:49 (36.7.136.111)
root     pts/1        2022-08-22 16:26 (36.7.136.111)##实例
[root@iZbp16mm3xbwen89azh9ffZ ~]# cat members.csv
Blum,Richard,123 Main St.,Chicago,IL,60601
Blum,Barbara,123 Main St.,Chicago,IL,60601
Bresnahan,Christine,456 Oak Ave.,Columbus,OH,43201
Bresnahan,Timothy,456 Oak Ave.,Columbus,OH,43201
[root@iZbp16mm3xbwen89azh9ffZ ~]# cat test.sh
#!/bin/bash
#read file and create insert statements for mysql
outfile="members.sql"
IFS=,
while read lname fname address city state zip
do
cat>>$outfile<<eof
insert into members (lname,fname,address,city,state,zip)
values ('$lname','$fname','$address','$city','$state',$zip);
eof
done < $1
[root@iZbp16mm3xbwen89azh9ffZ ~]# sh test.sh members.csv
[root@iZbp16mm3xbwen89azh9ffZ ~]# cat members.sql #数据库内用source命令操作sql文件
insert into members (lname,fname,address,city,state,zip)
values ('Blum','Richard','123 Main St.','Chicago','IL',60601);
insert into members (lname,fname,address,city,state,zip)
values ('Blum','Barbara','123 Main St.','Chicago','IL',60601);
insert into members (lname,fname,address,city,state,zip)
values ('Bresnahan','Christine','456 Oak Ave.','Columbus','OH',43201);
insert into members (lname,fname,address,city,state,zip)
values ('Bresnahan','Timothy','456 Oak Ave.','Columbus','OH',43201);
#####cat >> $outfile << EOF
这条语句中包含一个输出追加重定向(双大于号)和一个输入追加重定向(双小于号)。输
出重定向将cat命令的输出追加到由$outfile变量指定的文件中。cat命令的输入不再取自标准
输入,而是被重定向到脚本中存储的数据。EOF符号标记了追加到文件中的数据的起止。
INSERT INTO members (lname,fname,address,city,state,zip) VALUES
('$lname', '$fname', '$address', '$city', '$state', '$zip');
上面的文本生成了一个标准的SQL INSERT语句。注意,其中的数据会由变量来替换,变量
中内容则是由read语句存入的。

bash shell允许在脚本中创建自己的文件描述符。你可以创建文件描述符3~9,并将它们分配给要用到的任何输出文件。一旦创建了文件描述符,你就可以利用标准的重定向符号将任意命令的输出重定向到那里

exec 5>testfile
commands >&5##xargs命令
xargs命令接收STDIN的输入,这样你可以直接将数据通过管道输入xargs命令处理。
[root@iZbp16mm3xbwen89azh9ffZ 1]# touch {1..5}.txt
[root@iZbp16mm3xbwen89azh9ffZ 1]# ll
total 0
-rw-r--r-- 1 root root 0 Aug 26 09:45 1.txt
-rw-r--r-- 1 root root 0 Aug 26 09:45 2.txt
-rw-r--r-- 1 root root 0 Aug 26 09:45 3.txt
-rw-r--r-- 1 root root 0 Aug 26 09:45 4.txt
-rw-r--r-- 1 root root 0 Aug 26 09:45 5.txt
[root@iZbp16mm3xbwen89azh9ffZ 1]# ls 1.txt 6.txt #ls接收参数,不接受STDIN输入
ls: cannot access 6.txt: No such file or directory
1.txt
[root@iZbp16mm3xbwen89azh9ffZ 1]# xargs ls #可以用xargs命令将STDIN输入传递给ls命令,作为ls参数,ctrl+D进行保存(End-of-File字符),Ctrl+D组合键会在bash中产生一个EOF字符。
1111
1.txt
6.txt
2.txt
ls: cannot access 1111: No such file or directory
ls: cannot access 6.txt: No such file or directory
1.txt  2.txt
[root@iZbp16mm3xbwen89azh9ffZ 1]# echo "1.txt 6.txt"|xargs ls #可以直接将数据通过管道输入xargs命令处理
ls: cannot access 6.txt: No such file or directory
1.txt

第16章        控制脚本

Linux信号
信 号 描 述
1 SIGHUP 挂起进程
2 SIGINT 终止进程
3 SIGQUIT 停止进程
9 SIGKILL 无条件终止进程
15 SIGTERM 尽可能终止进程
17 SIGSTOP 无条件停止进程,但不是终止进程
18 SIGTSTP 停止或暂停进程,但不终止进程
19 SIGCONT 继续运行停止的进程

默认情况下,bash shell会忽略收到的任何SIGQUIT (3)和SIGTERM (15)信号(正因为这样,
交互式shell才不会被意外终止)。但是bash shell会处理收到的SIGHUP (1)和SIGINT (2)信号

1. 中断进程
Ctrl+C组合键会生成SIGINT信号
2. 暂停进程
Ctrl+Z组合键会生成一个SIGTSTP信号,停止进程会让程序继续保留在内存

###进程间的父子关系
dongm       2870       1  0 15:55 ?        00:00:00 /usr/lib/systemd/systemd --user
dongm       3555    2870  0 15:55 ?        00:00:00 /usr/libexec/gnome-terminal-server
dongm       3570    3555  0 15:55 pts/0    00:00:00 bash
dongm      10943    3570  0 17:16 pts/0    00:00:00 sleep 2000
dongm      11440    3570  0 17:21 pts/0    00:00:00 ps -f##捕获信号
如果脚本收到了trap命令中列出的信号,该信号不再由shell处理,而是交由本地处理。
trap命令的格式是:
trap commands signals[root@iZbp16mm3xbwen89azh9ffZ dongm]# cat trap1.sh
#!/bin/bash
##testing signal trapping
trap "echo 'sorry,i have trapped ctrl-c'" SIGINT #捕获SIGINT信号
echo "this is a test script"
count=1
while [ $count -le 5 ]
do
echo "Loop:#$count"
sleep 1
((count+=1))
done
##
echo "moving the trap"
trap - SIGINT                                   #移除SIGINT信号捕获
count=1
while [ $count -le 5 ]
do
echo "Loop:$count"
sleep 1
((count+=1))
done
echo "this is the end of the script"
[root@iZbp16mm3xbwen89azh9ffZ dongm]# sh trap1.sh
this is a test script
Loop:#1
Loop:#2
^Csorry,i have trapped ctrl-c
Loop:#3
Loop:#4
Loop:#5
moving the trap
Loop:1
^C###在非控制台下运行脚本
nohup命令运行了另外一个命令来阻断所有发送给该进程的SIGHUP信号。这会在退出终端会
话时阻止进程退出。
当你使用nohup命令时,如果关闭该会话,脚本会忽略终端会话发过来的SIGHUP信号。
由于nohup命令会解除终端与进程的关联,进程也就不再同STDOUT和STDERR联系在一起。
为了保存该命令产生的输出,nohup命令会自动将STDOUT和STDERR的消息重定向到一个名为
nohup.out的文件中。
nohup命令的格式如下:
$ nohup ./test1.sh &
[1] 3856
$ nohup: ignoring input and appending output to 'nohup.out'
$##作业控制
启动、停止、终止以及恢复作业的这些功能统称为作业控制。
默认进程(带有加号的那个)是最后启动的那个进程
前台启动作业  fg n  ##n为作业号
后台启动作业  bg n  ##调度优先级(scheduling priority)是内核分配给进程的CPU时间(相对于其他进程)-20到+19
最低值-20是最高优先级,而最高值19是最低优先级,越是“好”或高的值,获得CPU时间的机会越低。
##nice命令    允许你设置命令启动时的调度优先级
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ nice -n 10 sh test1.sh >test1out &
[2] 16198
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ ps -p 16198 -o pid,ppid,ni,cmdPID  PPID  NI CMD
16198 16161  10 sh test1.sh[dongm@iZbp16mm ~]$ nice -n -10 sh test1.sh >test1out &
[1] 16191
[dongm@iZbp16mm ~]$ nice: cannot set niceness: Permission denied #nice命令阻止普通系统用户
来提高命令的优先级。指定的作业的确运行了,但是试图使用nice命令提高其优先级的操作却失败了
[dongm@iZbp16mm ~]$ ps -p 16191 -o pid,ppid,ni,cmdPID  PPID  NI CMD
16191 16161   0 sh test1.sh
[dongm@iZbp16mm ~]$ renice -n 10 -p 16191   #renice命令:改变系统上已运行命令的优先级
16191 (process ID) old priority 0, new priority 10
[dongm@iZbp16mm ~]$ ps -p 16191 -o pid,ppid,ni,cmdPID  PPID  NI CMD
16191 16161  10 sh test1.sh
renice命令会自动更新当前运行进程的调度优先级。和nice命令一样,renice命令也有一
些限制:
1 只能对属于你的进程执行renice;
2 只能通过renice降低进程的优先级;
3 root用户可以通过renice来任意调整进程的优先级。
定时运行作业:at命令和cron表
###at命令
at的守护进程atd会以后台模式运行,检查作业队列来运行作业。大多数Linux发行
版会在启动时运行此守护进程。
atd守护进程会检查系统上的一个特殊目录(通常位于/var/spool/at)来获取用at命令提交的
作业
at命令的基本格式非常简单:
at [-f filename] time
at命令会显示分配给作业的作业号以及为作业安排的运行时间
[root@iZbp16mm3xbwen89azh9ffZ dongm]# at -M -f test.sh tomorrow
job 10 at Wed Aug 24 18:24:00 2022  #如果不想在at命令中使用邮件或重定向,最好加上-M选项来屏蔽作业产生的输出信息
[root@iZbp16mm3xbwen89azh9ffZ dongm]# at -M -f test.sh teatime
job 11 at Wed Aug 24 16:00:00 2022
[root@iZbp16mm3xbwen89azh9ffZ dongm]# at -M -f test.sh 13:30
job 12 at Wed Aug 24 13:30:00 2022
[root@iZbp16mm3xbwen89azh9ffZ dongm]# at -M -f test.sh now + 100min
job 13 at Tue Aug 23 20:04:00 2022
[root@iZbp16mm3xbwen89azh9ffZ dongm]# atq    ##atq查看系统中有哪些作业在等待
11  Wed Aug 24 16:00:00 2022 a root
10  Wed Aug 24 18:24:00 2022 a root
13  Tue Aug 23 20:04:00 2022 a root
12  Wed Aug 24 13:30:00 2022 a root
[root@iZbp16mm3xbwen89azh9ffZ dongm]# atrm 11
[root@iZbp16mm3xbwen89azh9ffZ dongm]# atrm 10      ##atrm删除作业
[root@iZbp16mm3xbwen89azh9ffZ dongm]# atq
13  Tue Aug 23 20:04:00 2022 a root
12  Wed Aug 24 13:30:00 2022 a root##cron程序
Linux系统使用cron程序来安排要定期执行的作业。cron程序会在后台运行并检查一个特殊的
表(被称作cron时间表),以获知已安排执行的作业
cron时间表采用一种特别的格式来指定作业何时运行。其格式如下:
min hour dayofmonth month dayofweek command
可以用三字符的文本值(mon、tue、wed、thu、fri、sat、sun)或数值(0为周日,6为周六)
来指定dayofweek表项。
这里还有另外一个例子:在每个月的第一天中午12点执行命令。可以用下面的格式:
00 12 1 * * command
dayofmonth表项指定月份中的日期值(1~31)
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ crontab -l    ##列出当前用户的cron表(计划任务)
00 12 * * * bash /home/dongm/test.sh >/home/dongm/1.txt
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ cat /home/dongm/test.sh
#!/bin/bash
if [ `date +%d -d tomorrow` = 01 ]  #每天12点运行脚本,判断是否月的最后一天,如果是就执行then部分命令
then
echo 1
elif [ `date +%d -d tomorrow` = 25 ]
then
echo 2
else
echo
echo 3
fi
crontab -e  #为cron时间表添加条目,文件位于/var/spool/cron/目录,cron程序会用提交作业的用户账户运行该脚本。
如果你创建的脚本对精确的执行时间要求不高,用预配置的cron脚本目录会更方便。有4个
基本目录:hourly、daily、monthly和weekly。位于/etc/下
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ ls /etc/cron*ly
/etc/cron.daily:
logrotate  man-db.cron/etc/cron.hourly:
0anacron/etc/cron.monthly:/etc/cron.weekly:###anacron程序
anacron程序只会处理位于cron目录的程序,比如/etc/cron.monthly。它用时间戳来决定作业
是否在正确的计划间隔内运行了。每个cron目录都有个时间戳文件,该文件位于/var/spool/
anacron
[root@iZbp16mm3xbwen89azh9ffZ anacron]# cat cron.weekly
20220818
[root@iZbp16mm3xbwen89azh9ffZ anacron]# cat /etc/anacrontab
#period in days   delay in minutes   job-identifier   command
1   5   cron.daily      nice run-parts /etc/cron.daily
7   25  cron.weekly     nice run-parts /etc/cron.weekly
@monthly 45    cron.monthly        nice run-parts /etc/cron.monthly
period条目定义了作业多久运行一次,以天为单位。anacron程序用此条目来检查作业的时间
戳文件。delay条目会指定系统启动后anacron程序需要等待多少分钟再开始运行错过的脚本。
command条目包含了run-parts程序和一个cron脚本目录名。run-parts程序负责运行目录中传给它的
任何脚本。##脚本用的shell
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ cat test.sh
echo $BASH
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ sh test.sh
/bin/sh
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ ll /bin/sh
lrwxrwxrwx. 1 root root 4 Jul 11  2019 /bin/sh -> bash##Linux系统提供了脚本文件,可以让你的脚本在用户启动一个新的bash shell时运行。
位于每个用户主目录中的启动文件(如.bashrc)提供了一个位置来存放新shell启动时需要运行的脚本和命令
[dongm@localhost 16]$ pwd
/home/dongm/16
[dongm@localhost 16]$ ls
1111.txt  1.sh  2.sh  ceshi.sh  nohup.out  priority.sh  scheduling.sh  testa.out  testa.sh  testb.out  test.txt  trap1.sh  trap2.sh
[dongm@localhost 16]$ cat ceshi.sh
#!/bin/bash
echo "this is a test"[dongm@localhost 16]$ cat $HOME/.bashrc
# .bashrc# Source global definitions
if [ -f /etc/bashrc ]; then. /etc/bashrc
fi# Uncomment the following line if you don't like systemctl's auto-paging feature:
# export SYSTEMD_PAGER=# User specific aliases and functions
/home/dongm/16/ceshi.sh
[dongm@localhost 16]$ bash
this is a test

第17章        创建函数

创建自己的shell脚本函数
将shell脚本代码放进函数中封装起来,这样就能在脚本中的任何地方多次使用它了。
函数是一个脚本代码块,你可以为其命名并在代码中任何位置重用。要在脚本中使用该代码块时,只要使用所起的函数名就行了(这个过程称为调用函数),函数名必须是唯一的
创建函数
function name {                                               name() {
commands                                                       commands       
}                                                                        }
bash shell会把函数当作一个小型脚本,运行结束时会返回一个退出状态码(参见第11章),使用return命令来退出函数并返回特定的退出状态码。

#你会注意到func1函数实际上输出了两条消息。read命令输出了一条简短的消息来向用户询问输入值。bash shell脚本并不将其作为STDOUT输出的一部分,并且忽略掉它。如果你用echo语句生成这条消息来向用户查询,那么它会与输出值一起被读进shell变量中。
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ cat function2.sh
#!/bin/bash
#using the echo to return a value
db1() {
read -p "enter a value: " value
echo $[ $value * 2 ]
}
echo "the result is `db1`"
echo -----------------------
db1() {
echo -n "enter a value: "
read value
echo $[ $value * 2 ]
}
echo "the result is `db1`"
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ sh function2.sh
enter a value: 20
the result is 40
-----------------------
20
the result is enter a value: 40##示例2
[dongm@localhost 17]$ cat test4.sh
#!/bin/bash
db1() {read -p "enter a value: " valueecho  $[ $value * 2 ]
}
result=`db1`
echo "the new value is $result"
db1() {read -p "enter a value: " valueecho "double the value"echo $[ $value * 2 ]
}
result=`db1`
echo "the new value is $result"
db1() {read -p "enter a value: " value$[ $value * 2 ]
}
result=`db1`
echo "the new value is $result"
[dongm@localhost 17]$ sh test4.sh
enter a value: 100
the new value is 200
enter a value: 100
the new value is double the value
200
enter a value: 100
test4.sh: line 17: 200: command not found
the new value is 
##向函数传递参数
函数可以使用标准的参数环境变量来表示命令行上传给函数的参数。例如,函数名会在$0
变量中定义,函数命令行上的任何参数都会通过$1、$2等定义。也可以用特殊变量$#来判断传
给函数的参数数目。在脚本中指定函数时,必须将参数和函数放在同一行,像这样:
func1 $value1 10
然后函数可以用参数环境变量来获得参数值
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ cat function3.sh
#!/bin/bash
#passing parameters to a function
db1() {
if [ $# -eq 0 ]||[ $# -gt 2 ]
then
echo -1
elif [ $# -eq 1 ]
then
echo $(($1 + $1))
else
echo $[ $1 + $2 ]
fi
}
echo -n "adding 10 and 20: "
echo "the result is `db1 10 20`"
echo -n "adding 10: "
echo "the result is `db1 10`"
echo -n "adding 10 20 30: "
echo "the result is `db1 10 20 30`"
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ sh function3.sh
adding 10 and 20: the result is 30
adding 10: the result is 20
adding 10 20 30: the result is -1
###由于函数使用特殊参数环境变量作为自己的参数值,因此它无法直接获取脚本在命令行中的参数值。##在函数中处理变量
作用域是变量可见的区域。函数中定义的变量与普通变量的作用域不同。
1. 全局变量
全局变量是在shell脚本中任何地方都有效的变量。如果你在脚本的主体部分定义了一个全局
变量,那么可以在函数内读取它的值。类似地,如果你在函数内定义了一个全局变量,可以在脚
本的主体部分读取它的值
2.局部变量
函数内部使用的任何变量都可以被声明成局部变量,只要在变量声明的前面加上local关键字就可以了
local variablename
也可以在变量赋值语句中使用local关键字:
local temp=$[ $value + 5 ]
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ cat function4.sh
#!/bin/bash
#demonstrating the local keyword
db1() {
temp=$[ $value + 5 ]
result=$[ $temp * 2 ]
}
temp=4
value=6
db1
echo the temp is $temp
echo the result is $result
echo -----------------------------
db1() {
local temp=$[ $value +5 ]
result=$[ $temp * 2 ]
}
temp=4
value=6
db1
echo the temp is $temp
echo the result is $result
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ sh function4.sh
the temp is 11
the result is 22
-----------------------------
the temp is 4
the result is 22

shell函数的作用域和环境变量一样,shell函数仅在定义它的shell会话内有效

###数组变量和函数
向函数传数组参数
[dongm@localhost 17]$ cat test8.sh
#!/bin/bash
#trying to pass a array variable
myarray=(1 2 3 4 5)
testit() {
echo "the parameters are:$@"
local thisarray=$@
echo "the received array1 is $thisarray"
echo "the received array2 is ${thisarray[*]}"
local secondarray=(`echo $@`)
echo "the received array3 is ${secondarray[*]}"
local value
local sum=0
for value in ${secondarray[*]}
do
sum=$[ $sum + $value ]
done
echo "the result is $sum"
}
testit ${myarray[*]}
[dongm@localhost 17]$ sh test8.sh
the parameters are:1 2 3 4 5
the received array1 is 1 2 3 4 5
the received array2 is 1 2 3 4 5
the received array3 is 1 2 3 4 5
the result is 15从函数返回数组
[dongm@localhost 17]$ array[0]=2
[dongm@localhost 17]$ array[1]=5
[dongm@localhost 17]$ array[2]=8
[dongm@localhost 17]$ echo ${array[*]}
2 5 8
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ cat array1.sh
#!/bin/bash
#returning a array value
testit() {
local i
local element=$[ $# - 1 ]
for ((i=0;i<=$element;i++))
do
local newarray[i]=$[ ${array[i]} * 2 ]     ##使用shell脚本内,函数外的array数组变量
done
echo ${newarray[*]}
}
array=(1 2 3 4 5)
echo "the original array is ${array[*]}"
arg1=`echo ${array[*]}`
result=(`testit $arg1`)     #组建新数组result
echo "The new array is: ${result[*]}"
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ sh array1.sh
the original array is 1 2 3 4 5
The new array is: 2 4 6 8 10##函数递归
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ cat recursion.sh
#!/bin/bash
#using recursion
db1() {
if [ $1 -eq 1 ]
then
echo 1
else
local temp=$[ $1 - 1 ]
local result=`db1 $temp`
echo $[ $result * $1 ]
fi
}
read -p "enter a value: " value
echo "the factorial of $value is `db1 $value`"
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ sh recursion.sh
enter a value: 5
the factorial of 5 is 120##创建库
bash shell允许创建函数库文件,然后在多个脚本中引用该库文件
shell函数的作用域。和环境变量一样,shell函数仅在定义它的shell会话内有效。
如果你在shell命令行界面的提示符下运行myfuncs shell脚本,shell会创建一个新的shell并在其中
运行这个脚本。它会为那个新shell定义这三个函数,但当你运行另外一个要用到这些函数的脚本
时,它们是无法使用的。
这同样适用于脚本。如果你尝试像普通脚本文件那样运行库文件,函数并不会出现在脚本中
使用函数库的关键在于source命令。source命令会在当前shell上下文中执行命令,而不是
创建一个新shell。可以用source命令来在shell脚本中运行库文件脚本。这样脚本就可以使用库
中的函数了。
source命令有个快捷的别名,称作点操作符(dot operator)。要在shell脚本中运行myfuncs
库文件,只需添加下面这行:
. ./myfuncs
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ cat myfuncs
#my script functions
addem() {
echo $[ $1 + $2 ]
}
multem() {
echo $[ $1 * $2 ]
}
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ cat function5.sh
#!/bin/bash
#using function lib
. /home/dongm/myfuncs            ##source命令
value1=10
value2=5
result1=`addem $value1 $value2`
result2=`multem $value1 $value2`
echo "The result of adding them is: $result1"
echo "The result of multiplying them is: $result2"
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ sh function5.sh
The result of adding them is: 15
The result of multiplying them is: 50###在命令行上创建函数时要特别小心,如果你给函数起了个跟内建命令或另一个命令相同
的名字,函数将会覆盖原来的命令。
###在.bashrc 文件中定义函数
[dongm@localhost ~]$ tail -5 $HOME/.bashrc
# . /home/dongm/17/17.7.2.1
function divem1 {echo $[ $1 * $2 ]
}
. /home/dongm/fuxi3/17/myfuncs
[dongm@localhost ~]$ cat /home/dongm/fuxi3/17/myfuncs
addem() {
echo $[ $1 + $2 ]
}
multem() {
echo $[ $1 * $2 ]
}
[dongm@localhost ~]$ addem 10 20
30
[dongm@localhost 17]$ divem1 10 5
50
[dongm@localhost 17]$ cat /home/dongm/fuxi3/17/test14.sh
#!/bin/bash
divem1 10 5
[dongm@localhost 17]$ sh /home/dongm/fuxi3/17/test14.sh
/home/dongm/fuxi3/17/test14.sh: line 2: divem1: command not found    #运行脚本的shell没有读取.bashrc文件###shtool函数库文件
下载:cd /usr/local/usr/;wget ftp://ftp.gnu.org/gnu/shtool/shtool-2.0.8.tar.gz; tar zxf shtool-2.0.8.tar.gz;cd shtool-2.0.8/
配置安装:./configure --prefix=/usr/local/shtool
make
make test&&make install

第18章        图形化桌面环境中的脚本编程

##创建文本菜单
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ cat menu.sh
#!/bin/bash
# simple script menu
function diskspace {
clear
df -k
}
function whoseon {
clear
who
}
function memusage {
clear
cat /proc/meminfo
}
function menu {
clear
echo
echo -e "\t\t\tSys Admin Menu\n"
echo -e "\t1. Display disk space"
echo -e "\t2. Display logged on users"
echo -e "\t3. Display memory usage"
echo -e "\t0. Exit program\n\n"
echo -en "\t\tEnter option: "
read -n 1 option
}
while [ 1 ]
do
menu
case $option in
0)
break ;;
1)
diskspace ;;
2)
whoseon ;;
3)
memusage ;;
*)
clear
echo "Sorry, wrong selection";;
esac
echo -en "\n\n\t\t\tHit any key to continue: "
read -n 1 line
done
clear##使用select命令
select命令的格式如下
select variable in list
do
commands
done
list参数是由空格分隔的文本选项列表,这些列表构成了整个菜单。select命令会将每个
列表项显示成一个带编号的选项,然后为选项显示一个由PS3环境变量定义的特殊提示符
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ cat select.sh
#!/bin/bash
# using select in the menu
function diskspace {
clear
df -k
}
function whoseon {
clear
who
}
function memusage {
clear
cat /proc/meminfo
}
PS3="Enter option: "
select value in "Display disk space" "Display logged on users" "Display memory usage" "Exit program"
do
case $value in
"Exit program")
break ;;
"Display disk space")
diskspace ;;
"Display logged on users")
whoseon ;;
"Display memory usage")
memusage ;;
*)
clear
echo "Sorry, wrong selection";;
esac
done
clear###脚本中使用dialog命令
[root@iZbp16mm3xbwen89azh9ffZ dongm]# cat dialog.sh
#!/bin/bash
# using dialog to create a menu
temp=$(mktemp -t test.XXXXXX)
temp2=$(mktemp -t test2.XXXXXX)
function diskspace {
df -k > $temp
dialog --textbox $temp 20 60
}
function whoseon {
who > $temp
dialog --textbox $temp 20 50
}
function memusage {
cat /proc/meminfo > $temp
dialog --textbox $temp 20 50
}
while [ 1 ]
do
dialog --menu "Sys Admin Menu" 20 30 10 1 "Display disk space" 2 "Display users" 3 "Display memory usage" 0 "Exit" 2> $temp2    #重定向STDERR来获得输出值
if [ $? -eq 1 ]
then
break
fi
selection=$(cat $temp2)
case $selection in
1)
diskspace ;;
2)
whoseon ;;
3)
memusage ;;
0)
break ;;
*)
dialog --msgbox "Sorry, invalid selection" 10 30
esac
done
rm -f $temp 2> /dev/null
rm -f $temp2 2> /dev/null
clear##kdialog命令
kdialog窗口部件用STDOUT来输出值,而不是STDERR。

第19章        初识sed和gawk

shell脚本最常见的一个用途就是处理文本文件。检查日志文件、读取配置文件、处理数据元素,shell脚本可以帮助我们将文本文件中各种数据的日常处理任务自动化。

像cat,sed这类可以从STDIN获取输入的命令,它们也可以通过echo管接或者文本获取输入!!

###sed命令                     sed -r    -r即--regexp-extended使用扩展的正则表达式
默认情况下,sed编辑器会将指定的命令应用到STDIN输入流上。这样你可以直接将数据通过管道输入sed编辑器处理
sed编辑器并不会修改文本文件的数据。它只会将修改后的数据发送到STDOUT。如果你查看原来的文本文件,它仍然保留着原始数据
要在sed命令行上执行多个命令时,只要用-e选项就可以了
$ sed -e 's/brown/green/; s/dog/cat/' data1.txt

[root@m03 ~]# sed -ri '/^(user|group)/s#apache#nginx#g' /etc/php-fpm.d/www.conf-f文件
[root@iZbp16mm3xbwen89azh9ffZ 1]# cat data1.txt
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
[root@iZbp16mm3xbwen89azh9ffZ 1]# cat >1.sed<<eof
> s/fox/cat/
> s/dog/fish/
> s/lazy/active/
> eof
[root@iZbp16mm3xbwen89azh9ffZ 1]# cat 1.sed
s/fox/cat/
s/dog/fish/
s/lazy/active/
[root@iZbp16mm3xbwen89azh9ffZ 1]# sed -f 1.sed data1.txt
The quick brown cat jumps over the active fish.
The quick brown cat jumps over the active fish.
The quick brown cat jumps over the active fish.
The quick brown cat jumps over the active fish.#sed编辑器基础
1.替换标记
s/pattern/replacement/flags
有4种可用的替换标记:
 数字,表明新文本将替换第几处模式匹配的地方;
 g,表明新文本将会替换所有匹配的文本;
 p,表明原先行的内容要打印出来;
 w file,将替换的结果写到文件中。
[root@iZbp16mm3xbwen89azh9ffZ 1]# cat data5.txt
This is a test line.
This is a different line.
[root@iZbp16mm3xbwen89azh9ffZ 1]# sed 's/test/trail/' data5.txt
This is a trail line.
This is a different line.
[root@iZbp16mm3xbwen89azh9ffZ 1]# sed 's/test/trail/p' data5.txt
This is a trail line.
This is a trail line.
This is a different line.
[root@iZbp16mm3xbwen89azh9ffZ 1]# sed -n 's/test/trail/p' data5.txt  #-n选项将禁止sed编辑器输出。但p替换标记会输出修改过的行。将二者配合使用的效果就是只输出被替换命令修改过的行
This is a trail line.
[root@iZbp16mm3xbwen89azh9ffZ 1]# sed 's/test/trail/w testout' data5.txt  #sed编辑器的正常输出是在STDOUT中,而只有那些包含匹配模式的行才会保存在指定的输出文件中
This is a trail line.
This is a different line.
[root@iZbp16mm3xbwen89azh9ffZ 1]# cat testout
This is a trail line.2.替换字符
$ sed 's/\/bin\/bash/\/bin\/csh/' /etc/passwd
由于正斜线通常用作字符串分隔符,因而如果它出现在了模式文本中的话,必须用反斜线来转义。
要解决这个问题,sed编辑器允许选择其他字符来作为替换命令中的字符串分隔符:
$ sed 's#/bin/bash#/bin/csh#' /etc/passwd
#被用作字符串分隔符,这样路径名就更容易阅读和理解了
[root@iZbp16mm3xbwen89azh9ffZ 1]# sed 's#/bin/bash#/bin/sh#' /etc/passwd
root:x:0:0:root:/root:/bin/sh
test:x:1002:1002::/home/test:/bin/sh
dongm:x:1003:1004::/home/dongm:/bin/sh3.使用地址
在sed编辑器中有两种形式的行寻址:
 以数字形式表示行区间
 用文本模式来过滤出行
两种形式都使用相同的格式来指定地址:
[address]command
也可以将特定地址的多个命令分组:
address {
command1
command2
command3
}[root@iZbp16mm3xbwen89azh9ffZ 1]# cat data1.txt
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
[root@iZbp16mm3xbwen89azh9ffZ 1]# sed '2,${     #如果需要在单行上执行多条命令,可以用花括号将多条命令组合在一起。sed编辑器会处理地址行处列出的每条命令。
> s/fox/cat/
> s/dog/fish/
> }' data1.txt
The quick brown fox jumps over the lazy dog.
The quick brown cat jumps over the lazy fish.
The quick brown cat jumps over the lazy fish.
The quick brown cat jumps over the lazy fish.4.删除行
d命令5.插入和附加文本
 插入(insert)命令(i)会在指定行前增加一个新行;
 附加(append)命令(a)会在指定行后增加一个新行。
格式如下:
sed '[address]command\
new line'
[root@iZbp16mm3xbwen89azh9ffZ 1]# echo "test line 2"|sed 'i\test line 1'
test line 1
test line 2
[root@iZbp16mm3xbwen89azh9ffZ 1]# cat data6.txt
This is line number 1.
This is line number 2.
This is line number 3.
This is line number 4.
[dongm@localhost 19]$ sed '3i\
> this is a inserted line'    #输入了结尾的单引号,需要在此行结束命令行,bash shell就会执行该命令
data6.txt          ##sed编辑器把这一行的data6.txt当作STDIN
data6.txt
[root@iZbp16mm3xbwen89azh9ffZ 1]# sed '3i\
> this is a insert line 1\
> this is a insert line 2' data6.txt    #要插入或附加多行文本,就必须对要插入或附加的新文本中的每一行使用反斜线,直到最后一行
This is line number 1.
This is line number 2.
this is a insert line 1
this is a insert line 2
This is line number 3.
This is line number 4.6.修改行
c命令
[root@iZbp16mm3xbwen89azh9ffZ 1]# sed '3c\
> this is a changed line' data6.txt
This is line number 1.
This is line number 2.
this is a changed line
This is line number 4.7.转换命令
y命令8.打印
 p命令用来打印文本行; #跟替换命令中的p标记类似,p命令可以打印sed编辑器输出中的行
 等号(=)命令用来打印行号;
 l(小写的L)命令用来列出行。 #列出(list)命令(l)可以打印数据流中的文本和不可打印的ASCII字符
[root@iZbp16mm3xbwen89azh9ffZ 1]# cat data6.txt
This is line number 1.
This is line number 2.
This is line number 3.
This is line number 4.
[root@iZbp16mm3xbwen89azh9ffZ 1]# sed -n '3{
> p
> s/line/test/p}' data6.txt
This is line number 3.
This is test number 3.
[root@iZbp16mm3xbwen89azh9ffZ 1]# grep -n 2 data6.txt
2:This is line number 2.
[root@iZbp16mm3xbwen89azh9ffZ 1]# sed -n '/number 4/{   ##等号命令会打印行在数据流中的当前行号。行号由数据流中的换行符决定
> =
> p
> }' data6.txt
4
This is line number 4.9.数据写入文件和从文件读取数据
w命令用来向文件写入行。该命令的格式如下:
[address]w filename   #filename可以使用相对路径或绝对路径,运行sed编辑器的用户必须有文
件的写权限
读取(read)命令(r)允许你将一个独立文件中的数据插入到数据流中。读取命令的格式如下:
[address]r filename
[root@iZbp16mm3xbwen89azh9ffZ 1]# sed '2,3w test.txt' data6.txt
This is line number 1.
This is line number 2.
This is line number 3.
This is line number 4.
[root@iZbp16mm3xbwen89azh9ffZ 1]# cat test.txt
This is line number 2.
This is line number 3.
[root@iZbp16mm3xbwen89azh9ffZ 1]# cat data11.txt
Blum, R Browncoat
McGuiness, A Alliance
Bresnahan, C Browncoat
Harken, C Alliance
[root@iZbp16mm3xbwen89azh9ffZ 1]# sed -n '/Browncoat/w Browncoats.txt' data11.txt
[root@iZbp16mm3xbwen89azh9ffZ 1]# cat Browncoats.txt
Blum, R Browncoat
Bresnahan, C Browncoat
[root@iZbp16mm3xbwen89azh9ffZ 1]# cat notice.std
Would the following people:
LIST
please report to the ship's captain.
[root@iZbp16mm3xbwen89azh9ffZ 1]# sed '/LIST/{
> r data11.txt
> d
> }' notice.std
Would the following people:
Blum, R Browncoat
McGuiness, A Alliance
Bresnahan, C Browncoat
Harken, C Alliance
please report to the ship's captain.

###gawk命令
yum list installed|grep gawk
gawk程序的报告生成能力通常用来从大文本文件中提取数据元素,并将它们格式化成可读的报告。
gawk程序脚本用一对花括号来定义。你必须将脚本命令放到两个花括号({})中,由于gawk命令行假定脚本是单个文本字符串,你还必须将脚本放到单引号中
数据记录:一条数据记录由记录分隔符分隔          
字段分隔符:一个数据字段由字段分隔符分隔       ###数据字段=数据元素

[root@iZbp16mm3xbwen89azh9ffZ 1]# awk -F: '{print $1}' /etc/passwd
root
bin
daemon
adm
lp#要在命令行上的程序脚本中使用多条命令,只要在命令之间放个分号即可。
$ echo "My name is Rich" | gawk '{$4="Christine"; print $0}' #第一条命令会给字段变量$4赋值。第二条命令会打印整个数据字段。
My name is Christine##-f选项
[root@iZbp16mm3xbwen89azh9ffZ 1]# cat 1.gawk
{print $1 "'s home directory is " $6}  #'s此处有单引号,程序脚本放在命令行会误认为是结束的文本字符串
[root@iZbp16mm3xbwen89azh9ffZ 1]# gawk -F: -f 1.gawk /etc/passwd
root's home directory is /root
admin's home directory is /home/admin
mysql's home directory is /var/lib/mysql
nginx's home directory is /var/lib/nginx
test's home directory is /home/test
dongm's home directory is /home/dongm
[root@iZbp16mm3xbwen89azh9ffZ 1]# cat 2.gawk
{
text="'s home directory is "  #注意,gawk程序在引用变量值时并未像shell脚本一样使用美元符
print $1 text $6
}##BEGIN和END关键字
[root@iZbp16mm3xbwen89azh9ffZ 1]# cat data3.txt
Line 1
Line 2
Line 3
[root@iZbp16mm3xbwen89azh9ffZ 1]# awk 'BEGIN{print "start of file"}{print $0}END{print "end of file"}' data3.txt
start of file
Line 1
Line 2
Line 3
end of file
[root@iZbp16mm3xbwen89azh9ffZ 1]# cat 3.gawk
BEGIN{
print "    title"
print "userid \t shell"
print "----------------"
FS=":"  #FS的特殊变量。这是定义字段分隔符的另一种方法。
}
{print $1 "\t" $7}
END{print "This concludes the listing"}
[root@iZbp16mm3xbwen89azh9ffZ 1]# awk -f 3.gawk /etc/passwdtitle
userid   shell
----------------
root    /bin/bash
admin   /bin/bash
mysql   /bin/false
nginx   /sbin/nologin
test    /bin/bash
dongm   /bin/bash
This concludes the listing

第20章        正则表达式

正则表达式是你所定义的模式模板(pattern template),Linux工具可以用它来过滤文本。Linux
工具(比如sed编辑器或gawk程序)能够在处理数据时使用正则表达式对数据进行模式匹配。
正则表达式模式利用通配符来描述数据流中的一个或多个字符
正则表达式通配符模式的工作原理与之类似。正则表达式模式含有文本或特殊字符,为sed
编辑器和gawk程序定义了一个匹配数据时采用的模板。
正则表达式是通过正则表达式引擎(regular expression engine)实现的。正则表达式引擎是
一套底层软件,负责解释正则表达式模式并使用这些模式进行文本匹配。
在Linux中,有两种流行的正则表达式引擎:
 POSIX基础正则表达式(basic regular expression,BRE)引擎
 POSIX扩展正则表达式(extended regular expression,ERE)引擎
Linux中的不同应用程序可能会用不同类型的正则表达式。这其中包括编程语言(Java、Perl和Python)、Linux实用工具(比如sed编辑器、gawk程序和grep工具)以及主流应用(比如MySQL和PostgreSQL数据库服务器)。
大多数Linux工具都至少符合POSIX BRE引擎规范,能够识别该规范定义的所有模式符号。
有些工具(比如sed编辑器)只符合了BRE引擎规范的子集。POSIX BRE引擎通常出现在依赖正则表达式进行文本过滤的编程语言中。gawk程序用ERE引擎来处理它的正则表达式模式。

BRE模式特殊字符
需转义字符 \(转义字符)        /
锚字符 ^$
点号字符 .
字符组 [ ]
排除型字符组 [^ ]
区间 [ - ]
星号 *

正则表达式模式都区分大小写,一旦正则表达式匹配了文本字符串中任意位置上的模式,它就会将该字符串传回Linux工具。在正则表达式中,你不用写出整个单词。只要定义的文本出现在数据流中,正则表达式就能够匹配,可以在正则表达式中使用空格和数字,空格和其他的字符并没有什么区别。
如果要用某个特殊字符作为文本字符,就必须转义。在转义特殊字符时,你需要在它前面加一个特殊字符来告诉正则表达式引擎应该将接下来的字符当作普通的文本字符。这个特殊字符就是反斜线(\)。

#转义字符\
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ echo "this cost is \$4.00"
this cost is $4.00
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ echo "this cost is $4.00"
this cost is .00#单词间有两个空格的行匹配正则表达式模式。
$ cat data1
This is a normal line of text.
This is     a line with too many spaces.
$ sed -n '/     /p' data1
This is     a line with too many spaces.#由于脱字符出现在正则表达式模式的尾部,sed编辑器会将它当作普通字符来匹配。
$ echo "This ^ is a test" | sed -n '/s ^/p'
This ^ is a test
#如果你在模式中先指定了脱字符,随后还有其他一些文本,那么你必须在脱字符前用转义字符。
$ echo "this ^ is a test"|sed -n '/\^ is/p'
this ^ is a test#特殊字符点号用来匹配除换行符之外的任意单个字符。它必须匹配一个字符
$ cat data6
This is a test of a line.
The cat is sleeping.
That is a very nice hat.
This test is at line four.
at ten o'clock we'll go home.
$ sed -n '/.at/p' data6   #在正则表达式中,空格也是字符,因此at前面的空格刚好匹配了该模式
The cat is sleeping.
That is a very nice hat.
This test is at line four.
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ cat data6
This is a test of a line.
The cat is sleeping.
That is a very nice hat.
This test is at line four.
at ten o'clock we'll go home.
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ sed -n '/[^ch]at/p' data6  #即使是排除,字符组仍然必须匹配一个字符
This test is at line four.
$ sed -n '/[a-ch-m]at/p' data6  #该字符组允许区间a~c、h~m中的字母出现在at文本前,但不允许出现d~g的字母
The cat is sleeping.
That is a very nice hat.#星号表明该字符必须在匹配模式的文本中出现0次或多次。
$ echo "ik" | sed -n '/ie*k/p'
$ echo "ieeeek" | sed -n '/ie*k/p'
##另一个特性是将点号特殊字符和星号特殊字符组合起来。这个组合能够匹配任意数量
的任意字符。它通常用在数据流中两个可能相邻或不相邻的文本字符串之间。可以使用这个模式轻松查找可能出现在数据流中文本行内任意位置的多个单词。
$ echo "this is a regular pattern expression" | sed -n '
> /regular.*expression/p'
this is a regular pattern expression
#星号还能用在字符组上。它允许指定可能在文本中出现多次的字符组或字符区间。
$ echo "bt" | sed -n '/b[ae]*t/p'
bt
$ echo "baeeaeeat" | sed -n '/b[ae]*t/p'
baeeaeeat
$ echo "baakeeet" | sed -n '/b[ae]*t/p'
$
只要a和e字符以任何组合形式出现在b和t字符之间(就算完全不出现也行),模式就能够匹配。如果出现了字符组之外的字符,该模式匹配就会不成立
BRE特殊字符组
描 述
[[:alpha:]] 匹配任意字母字符,不管是大写还是小写
[[:alnum:]] 匹配任意字母数字字符0~9、A~Z或a~z
[[:blank:]] 匹配空格或制表符
[[:digit:]] 匹配0~9之间的数字
[[:lower:]] 匹配小写字母字符a~z
[[:print:]] 匹配任意可打印字符
[[:punct:]] 匹配标点符号
[[:space:]] 匹配任意空白字符:空格、制表符、NL、FF、VT和CR
[[:upper:]] 匹配任意大写字母字符A~Z

POSIX ERE模式包括了一些可供Linux应用和工具使用的额外符号。gawk程序能够识别ERE
模式,但sed编辑器不能。

ERE模式符号
问号
加号 +
花括号 { }
管道符号 |
表达式分组 ( )
##问号类似于星号,不过有点细微的不同。问号表明前面的字符可以出现0次或1次
与星号一样,你可以将问号和字符组一起使用。
$ echo "bt" | gawk '/b[ae]?t/{print $0}'
bt
$ echo "bat" | gawk '/b[ae]?t/{print $0}'
bat
$ echo "bot" | gawk '/b[ae]?t/{print $0}'
$
$ echo "bet" | gawk '/b[ae]?t/{print $0}'
bet
$ echo "baet" | gawk '/b[ae]?t/{print $0}'
$##加号是类似于星号的另一个模式符号,但跟问号也有不同。加号表明前面的字符可以出现1
次或多次,但必须至少出现1次。
$ echo "bt" | gawk '/b[ae]+t/{print $0}'
$
$ echo "bat" | gawk '/b[ae]+t/{print $0}'
bat
$ echo "bet" | gawk '/b[ae]+t/{print $0}'
bet
$ echo "beat" | gawk '/b[ae]+t/{print $0}'
beat##ERE中的花括号允许你为可重复的正则表达式指定一个上限。这通常称为间隔(interval)。
可以用两种格式来指定区间。
 m:正则表达式准确出现m次。
 m, n:正则表达式至少出现m次,至多n次。
这个特性可以精确调整字符或字符集在模式中具体出现的次数
$ echo bet|awk '/be{1}t/{print $0}'
bet
$ echo beet|awk '/be{1}t/{print $0}'
$
$ echo beet|awk '/be{1,2}t/{print $0}'
beet
$ echo beeet|awk '/be{1,2}t/{print $0}'
$
间隔模式匹配同样适用于字符组
$ echo "bt" | gawk --re-interval '/b[ae]{1,2}t/{print $0}'
$
$ echo "bat" | gawk --re-interval '/b[ae]{1,2}t/{print $0}'
bat
$ echo "bet" | gawk --re-interval '/b[ae]{1,2}t/{print $0}'
bet
$ echo "beat" | gawk --re-interval '/b[ae]{1,2}t/{print $0}'
beat
$ echo "beet" | gawk --re-interval '/b[ae]{1,2}t/{print $0}'
beet
$ echo "beeat" | gawk --re-interval '/b[ae]{1,2}t/{print $0}'
$##管道符号允许你在检查数据流时,用逻辑OR方式指定正则表达式引擎要用的两个或多个模
式。如果任何一个模式匹配了数据流文本,文本就通过测试。如果没有模式匹配,则数据流文本
匹配失败。
使用管道符号的格式如下:
expr1|expr2|...
管道符号两侧的正则表达式可以采用任何正则表达式模式(包括字符组)来定义文本。
$ echo "He has a hat." | gawk '/[ch]at|dog/{print $0}'
He has a hat.
这个例子会匹配数据流文本中的cat、hat或dog。##表达式分组
正则表达式模式也可以用圆括号进行分组。当你将正则表达式模式分组时,该组会被视为一
个标准字符。可以像对普通字符一样给该组使用特殊字符。举个例子:
$ echo "Sat" | gawk '/Sat(urday)?/{print $0}'
Sat
$ echo "Saturday" | gawk '/Sat(urday)?/{print $0}'
Saturday
$
结尾的urday分组以及问号,使得模式能够匹配完整的Saturday或缩写Sat。
将分组和管道符号一起使用来创建可能的模式匹配组是很常见的做法。
$ echo "cat" | gawk '/(c|b)a(b|t)/{print $0}'
cat
$ echo "bat" | gawk '/(c|b)a(b|t)/{print $0}'
bat
$ echo "tab" | gawk '/(c|b)a(b|t)/{print $0}'
$
模式(c|b)a(b|t)会匹配第一组中字母的任意组合以及第二组中字母的任意组合。
#wc命令可以对对数据中的文本进行计数。默认情况下,它会输出3个值:
 文本的行数
 文本的词数
 文本的字节数
$ echo $[ `ll /usr/sbin|wc -l` - 1 ]   #-l选项统计行数
387#PATH目录文件计数
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ cat countfiles.sh
#!/bin/bash
#count number of files in your PATH
mypath=$(echo $PATH|sed 's/:/ /g')
for dir in $mypath
do
files=`ls $dir`
count=0
for file in $files
do
((count+=1))
done
echo "$dir-$count"
done 2>/dev/null
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ sh countfiles.sh
/usr/local/bin-2
/bin-821
/usr/bin-821
/usr/local/sbin-0
/usr/sbin-387
/home/dongm/.local/bin-0
/home/dongm/bin-0#验证电话号码
$ cat test.sh
#!/bin/bash
#script to filter out bad phone numbers
gawk '/^\(?[2-9][0-9]{2}\)?(| |-|\.)[0-9]{3}( |-|\.)[0-9]{4}$/{print $0}'
$ echo "317-555-1234"|sh test.sh
317-555-1234
$ echo "000-555-1234"|sh test.sh
$
$ echo "312 555-1234"|sh test.sh
312 555-1234
$ cat phonelist
000-000-0000
123-456-7890
212-555-1234
(317)555-1234
(202) 555-9876
33523
1234567890
234.123.4567
$ cat phonelist |sh test.sh    ## $ sh test.sh < phonelist
212-555-1234
(317)555-1234
(202) 555-9876
234.123.4567#解析邮件地址
邮件地址的基本格式为:
username@hostname  #hostname组成:servername.一个或者多个子域名.顶级域名
$ cat email.sh
#!/bin/bash
#script to filter out correct mailing address
gawk '/^([a-zA-Z0-9_\-\.\+]+)@([a-zA-Z0-9_\.\-]+)\.([a-zA-Z]{2,5})$/'
$ echo "rich@here.now"|sh email.sh
rich@here.now
$ echo "rich@here.now."|sh email.sh
$
Linux-awk直接修改原文件
#注意:重定向符号后面的文件名需要加双引号
gawk '{print $0> "file"}' file
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ cat address.sh
$ echo "rich@here.now" | ./isemail
rich@here.now
$ echo "rich@here.now." | ./isemail
$
$ echo "rich@here.n" | ./isemail
$
$ echo "rich@here-now" | ./isemail
$
$ echo "rich.blum@here.now" | ./isemail
rich.blum@here.now
$ echo "rich_blum@here.now" | ./isemail
rich_blum@here.now
$ echo "rich/blum@here.now" | ./isemail
$
$ echo "rich#blum@here.now" | ./isemail
$
$ echo "rich*blum@here.now" | ./isemail
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ sed -i '/^\$$/d' address.sh
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ cat address.sh
$ echo "rich@here.now" | ./isemail
rich@here.now
$ echo "rich@here.now." | ./isemail
$ echo "rich@here.n" | ./isemail
$ echo "rich@here-now" | ./isemail
$ echo "rich.blum@here.now" | ./isemail
rich.blum@here.now
$ echo "rich_blum@here.now" | ./isemail
rich_blum@here.now
$ echo "rich/blum@here.now" | ./isemail
$ echo "rich#blum@here.now" | ./isemail
$ echo "rich*blum@here.now" | ./isemail
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ gawk -F\" '{print $2>"address"}' address.sh
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ cat address
rich@here.nowrich@here.now.
rich@here.n
rich@here-now
rich.blum@here.nowrich_blum@here.nowrich/blum@here.now
rich#blum@here.now
rich*blum@here.now
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ sed -i '/^$/d' address
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ cat address
rich@here.now
rich@here.now.
rich@here.n
rich@here-now
rich.blum@here.now
rich_blum@here.now
rich/blum@here.now
rich#blum@here.now
rich*blum@here.now
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ sh email.sh <address
rich@here.now
rich.blum@here.now
rich_blum@here.now

可移植操作系统接口(英语:Portable Operating System Interface,缩写为POSIX)

第21章        sed进阶

sed编辑器包含了三个可用来处理多行文本的特殊命令。
 N:将数据流中的下一行加进来创建一个多行组(multiline group)来处理。
 D:删除多行组中的一行。
 P:打印多行组中的一行。

$ cat data1.txt
This is the header line.This is a data line.This is the last line.
$ sed '/^$/d' data1.txt
This is the header line.
This is a data line.
This is the last line.
$ sed '/header/{n ; d}' data1.txt
This is the header line.
This is a data line.This is the last line.#单行next命令会将数据流中的下一文本行移动到sed编辑器的工作空间(称为模式空间)。
多行版本的next命令(用大写N)会将下一文本行添加到模式空间中已有的文本后。
这样的作用是将数据流中的两个文本行合并到同一个模式空间中。文本行仍然用换行符分隔,
但sed编辑器现在会将两行文本当成一行来处理。
$ cat data2.txt
This is the header line.
This is the first data line.
This is the second data line.
This is the last line.
$ sed '/first/{ N ; s/\n/ / }' data2.txt
This is the header line.
This is the first data line. This is the second data line.
This is the last line.#sed编辑器提供了多行删除命令D,它只删除模式空间中的第一行。该命令会删除到换行符(含
换行符)为止的所有字符
$ cat data5.txtThis is the header line.
This is a data line.This is the last line.
$ sed '/^$/{N ; /header/D}' data5.txt
This is the header line.
This is a data line.This is the last line.#多行打印命令(P)只打印多行模式空间中的第一行。这包括模式空间中直到换行符为止的所有字符
$ sed -n 'N ; /System\nAdministrator/P' data3.txt
On Tuesday, the Linux System
D命令的独特之处在于强制sed编辑器返回到脚本的起始处,对同一模式空间中的内容重新执
行这些命令(它不会从数据流中读取新的文本行)。在命令脚本中加入N命令,你就能单步扫过整
个模式空间,将多行一起匹配。
接下来,使用P命令打印出第一行,然后用D命令删除第一行并绕回到脚本的起始处。一旦返
回,N命令会读取下一行文本并重新开始这个过程。这个循环会一直继续下去,直到数据流结束

模式空间(工作空间)与保持空间

sed编辑器的保持空间命令
命 令 描 述
h 将模式空间复制到保持空间
H 将模式空间附加到保持空间
g 将保持空间复制到模式空间
G 将保持空间附加到模式空间
x 交换模式空间和保持空间的内容
$ cat data2.txt
This is the header line.
This is the first data line.
This is the second data line.
This is the last line.
$ sed -n '{1!G ; h ; $p }' data2.txt
This is the last line.
This is the second data line.
This is the first data line.
This is the header line.
$ tac data2.txt
This is the last line.
This is the second data line.
This is the first data line.
This is the header line.
#sed编辑器提供了一种方法,可以基于地址、地址模式或地址区间排除一整块命令。
这允许你只对数据流中的特定行执行一组命令。
分支(branch)命令b的格式如下:
[address]b [label]
address参数决定了哪些行的数据会触发分支命令。label参数定义了要跳转到的位置。如
果没有加label参数,跳转命令会跳转到脚本的结尾。
$ cat data2.txt
This is the header line.
This is the first data line.
This is the second data line.
This is the last line.
[root@iZbp16mm3xbwen89azh9ffZ 21]# sed '/first/b jump;s/the/this/
> :jump
> s/line/aaaaa/' data2.txt
This is this header aaaaa.
This is the first data aaaaa.
This is this second data aaaaa.
This is this last aaaaa.
跳转命令指定如果文本行中出现了first,程序应该跳到标签为jump的脚本行。
如果分支命令的模式没有匹配,sed编辑器会继续执行脚本中的命令,包括分支标签后的命令(因此,所
有的替换命令都会在不匹配分支模式的行上执行)。
$ echo "This, is, a, test, to, remove, commas." | sed -n '{
> :start
> s/,//1p
> /,/b start
> }'
This is, a, test, to, remove, commas.
This is a, test, to, remove, commas.
This is a test, to, remove, commas.
This is a test to, remove, commas.
This is a test to remove, commas.
This is a test to remove commas.#测试(test)命令(t)也可以用来改变sed编辑器脚本的执行流程。测
试命令会根据替换命令的结果跳转到某个标签,如果替换命令成功匹配并替换了一个模式,
测试命令就会跳转到指定的标签。如果替换命令未能匹配指定的模式,测试命令就不会跳转。
在没有指定标签的情况下,如果测试成功,sed会跳转到脚本的结尾。
$ echo "This, is, a, test, to, remove, commas. " | sed -n '{
> :start
> s/,//1p
> t start
> }'
This is, a, test, to, remove, commas.
This is a, test, to, remove, commas.
This is a test, to, remove, commas.
This is a test to, remove, commas.
This is a test to remove, commas.
This is a test to remove commas.
#模式替换
&符号
&符号可以用来代表替换命令中的匹配的模式。不管模式匹配的是什么样的文本,
你都可以在替代模式中使用&符号来使用这段文本。
$ echo "The cat sleeps in his hat." | sed 's/.at/"&"/g'
The "cat" sleeps in his "hat".替换单独的单词
sed编辑器用圆括号来定义替换模式中的子模式。你可以在替代模式中使用特殊字符来引用
每个子模式。替代字符由反斜线和数字组成。数字表明子模式的位置。sed编辑器会给第一个子
模式分配字符\1,给第二个子模式分配字符\2,依此类推。
当在替换命令中使用圆括号时,必须用转义字符将它们标示为分组字符而不是普通的圆
括号。这跟转义其他特殊字符正好相反。
来看一个在sed编辑器脚本中使用这个特性的例子。
$ echo "That furry cat is pretty" | sed 's/furry \(.at\)/\1/'
That cat is pretty
$
$ echo "That furry hat is pretty" | sed 's/furry \(.at\)/\1/'
That hat is pretty
$ echo "1234567" | sed '{
> :start
> s/\(.*[0-9]\)\([0-9]\{3\}\)/\1,\2/
> t start
> }'
1,234,567#在脚本中使用sed
使用包装脚本
$ cat reverse.sh
#!/bin/bash
# Shell wrapper for sed editor script to reverse text file lines.
sed -n '{1!G;h;$p}' $1
$ ./reverse.sh data2.txt
This is the last line.
This is the second data line.
This is the first data line.
This is the header line.重定向sed 的输出
$ cat fact.sh
#!/bin/bash
# Add commas to number in factorial answer
factorial=1
counter=1
number=$1
while [ $counter -le $number ]
do
factorial=$[ $factorial * $counter ]
counter=$[ $counter + 1 ]
done
result=$(echo $factorial | sed '{
:start
s/\(.*[0-9]\)\([0-9]\{3\}\)/\1,\2/
t start
}')
echo "The result is $result"
$ ./fact.sh 20
The result is 2,432,902,008,176,640,000#打印末尾行
$ cat data7.txt
This is line 1.
This is line 2.
This is line 3.
This is line 4.
This is line 5.
This is line 6.
This is line 7.
This is line 8.
This is line 9.
This is line 10.
This is line 11.
This is line 12.
This is line 13.
This is line 14.
This is line 15.
$ sed '{
> :start
> $q ; N ; 11,$D
> b start
> }' data7.txt
This is line 6.
This is line 7.
This is line 8.
This is line 9.
This is line 10.
This is line 11.
This is line 12.
This is line 13.
This is line 14.
This is line 15.
这个脚本会首先检查这行是不是数据流中最后一行。如果是,退出(quit)命令会停止循
环。N命令会将下一行附加到模式空间中当前行之后。如果当前行在第10行后面,11,$D命令会
删除模式空间中的第一行。这就会在模式空间中创建出滑动窗口效果。#删除结尾的空白行
$ cat data10.txt
This is the first line.
This is the second line.$ sed '{
> :start
> /^\n*$/{$d ; N ; b start }
> }' data10.txt
This is the first line.
This is the second line.
在正常脚本的花括号里还有花括号。这允许你在整个命令脚本
中将一些命令分组。该命令组会被应用在指定的地址模式上。地址模式能够匹配只含有一个换行
符的行。如果找到了这样的行,而且还是最后一行,删除命令会删掉它。如果不是最后一行,N
命令会将下一行附加到它后面,分支命令会跳到循环起始位置重新开始。
[root@iZbp16mm3xbwen89azh9ffZ 21]# cat data2.txt
This is the header line.
This is the first data line.
This is the second data line.
This is the last line.
[root@iZbp16mm3xbwen89azh9ffZ 21]# sed '
> :start
> /second/{$d;N;b start}
> ' data2.txt
This is the header line.
This is the first data line.#删除HTML标签
$ cat data11.txt
<html>
<head>
<title>This is the page title</title>
</head>
<body>
<p>
This is the <b>first</b> line in the Web page.
This should provide some <i>useful</i>
information to use in our sed script.
</body>
</html>
$ sed 's/<[^>]*>//g ; /^$/d' data11.txt
This is the page title
This is the first line in the Web page.
This should provide some useful
information to use in our sed script.

第22章        gawk进阶

gawk是一门功能丰富的编程语言
所有编程语言共有的一个重要特性是使用变量来存取值。gawk编程语言支持两种不同类型的
变量:
 内建变量
 自定义变量
gawk有一些内建变量。这些变量存放用来处理数据文件中的数据字段和记录的信息。你也可
以在gawk程序里创建你自己的变量。
数据字段是由字段分隔符来划定的。默认情况下,字段分隔符是一个空白字符,也就是空格
符或者制表符。可以在命令行下使用命令行参数-F或者在gawk程序中使用特殊的内建变量FS来更改字段分隔符。

gawk数据字段和记录变量
变 量 描 述
FIELDWIDTHS 由空格分隔的一列数字,定义了每个数据字段确切宽度
FS 输入字段分隔符
RS 输入记录分隔符
OFS 输出字段分隔符
ORS 输出记录分隔符
FNR 当前数据文件中的数据行数
NF 数据文件中的字段总数
NR 已处理的输入记录数
#默认情况下,gawk将OFS设成一个空格,只不过是用在print命令的输出上,所以如果你用命令:
print $1,$2,$3
会看到如下输出:
field1 field2 field3
$ cat data1
data11,data12,data13,data14,data15
data21,data22,data23,data24,data25
data31,data32,data33,data34,data35
$ gawk 'BEGIN{FS=","; OFS="-"} {print $0}' data1 #注意print $0与print $1,$2区别
data11,data12,data13,data14,data15
data21,data22,data23,data24,data25
data31,data32,data33,data34,data35
$ gawk 'BEGIN{FS=","; OFS="-"} {print $1,$2,$3}' data1
data11-data12-data13
data21-data22-data23
data31-data32-data33
$ cat data1b
1005.3247596.37
115-2.349194.00
05810.1298100.1
$ gawk 'BEGIN{FIELDWIDTHS="3 5 2 5"}{print $1,$2,$3,$4}' data1b
100 5.324 75 96.37
115 -2.34 91 94.00
058 10.12 98 100.1
$ cat data2
Riley Mullen
123 Main Street
Chicago, IL 60601
(312)555-1234Frank Williams
456 Oak Street
Indianapolis, IN 46201
(317)555-9876Haley Snell
4231 Elm Street
Detroit, MI 48201
(313)555-4938
$ gawk 'BEGIN{FS="\n"; RS=""} {print $1,$4}' data2
Riley Mullen (312)555-1234
Frank Williams (317)555-9876
Haley Snell (313)555-4938#NF变量含有数据文件中最后一个数据字段的数字值。可以在它前面加个美元符将其用作字段变量。
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ gawk '
BEGIN {FS=","}{print $1,"FNR="FNR,"NR="NR}
END{print "There were",NR,"records processed"}' data1 data1
data11 FNR=1 NR=1
data21 FNR=2 NR=2
data31 FNR=3 NR=3
data11 FNR=1 NR=4
data21 FNR=2 NR=5
data31 FNR=3 NR=6
There were 6 records processed#自定义变量
gawk自定义变量名可以是任意数目的字母、数字和下划线,但不能以数字开头。
重要的是,要记住gawk变量名区分大小写
在脚本中给变量赋值
$ gawk 'BEGIN{x=4; x= x * 2 + 3; print x}'
11在命令行上给变量赋值
$ cat script1
BEGIN{FS=","}
{print $n}
$ gawk -f script1 n=2 data1
data12
data22
data32
使用命令行参数来定义变量值会有一个问题。在你设置了变量后,这个值在代码的BEGIN部分不可用。
$ cat script2
BEGIN{print "The starting value is",n; FS=","}
{print $n}
$ gawk -f script2 n=3 data1
The starting value is
data13
data23
data33
可以用-v命令行参数来解决这个问题。它允许你在BEGIN代码之前设定变量。在命令行上,
-v命令行参数必须放在脚本代码之前。
$ gawk -v n=3 -f script2 data1
The starting value is 3
data13
data23
data33
#使用模式
正则表达式
$ gawk 'BEGIN{FS=","} /d/{print $1}' data1
data11
data21
data31匹配操作符
匹配操作符(matching operator)允许将正则表达式限定在记录中的特定数据字段。匹配操
作符是波浪线(~)。可以指定匹配操作符、数据字段变量以及要匹配的正则表达式。
$ gawk 'BEGIN{FS=","} $2 ~ /^data2/{print $0}' data1
data21,data22,data23,data24,data25
$ gawk -F: '$1 ~ /rich/{print $1,$NF}' /etc/passwd
rich /bin/bash
$ gawk –F: '$1 !~ /rich/{print $1,$NF}' /etc/passwd   #!符号来排除正则表达式的匹配
root /bin/bash
daemon /bin/sh
bin /bin/sh
sys /bin/sh
--- output truncated --- 数学表达式
$ gawk -F: '$4 == 0{print $1}' /etc/passwd
root
sync
shutdown
可以使用任何常见的数学比较表达式。
 x == y:值x等于y。
 x <= y:值x小于等于y。
 x < y:值x小于y。
 x >= y:值x大于等于y。
 x > y:值x大于y。
也可以对文本数据使用表达式。跟正则表达式不同,表达式必须完全匹配。数
据必须跟模式严格匹配。
$ gawk -F, '$1 == "data"{print $1}' data1
$
$ gawk -F, '$1 == "data11"{print $1}' data1
data11
#结构化命令
if 语句
if (condition)
statement1
也可以将它放在一行上,像这样:
if (condition) statement1
$ cat data4
10
5
13
50
34
$ gawk '{if ($1 > 20) print $1}' data4
50
34
如果需要在if语句中执行多条语句,就必须用花括号将它们括起来。
$ gawk '{
> if ($1 > 20)
> {
> x = $1 * 2
> print x
> }
> }' data4
100
68
gawk的if语句也支持else子句,允许在if语句条件不成立的情况下执行一条或多条语句。
这里有个使用else子句的例子。
$ gawk '{
> if ($1 > 20)
> {
> x = $1 * 2
> print x
> } else
> {
> x = $1 / 2
> print x
> }}' data4
5
2.5
6.5
100
68
可以在单行上使用else子句,但必须在if语句部分之后使用分号。
if (condition) statement1; else statement2
以下是上一个例子的单行格式版本。
$ gawk '{if ($1 > 20) print $1 * 2; else print $1 / 2}' data4
5
2.5
6.5
100
68while语句
while (condition)
{
statements
}
$ cat data5
130 120 135
160 113 140
145 170 215
$ gawk '{
> total = 0
> i = 1
> while (i < 4)
> {
> total += $i
> if (i == 2)
> break
> i++
> }
> avg = total / 2
> print "The average of the first two data elements is:",avg
> }' data5
The average of the first two data elements is: 125
The average of the first two data elements is: 136.5
The average of the first two data elements is: 157.5
while语句会遍历记录中的数据字段,将每个值都加到total变量上,并将计数器变量i增值。
当计数器值等于4时,while的条件变成了FALSE,循环结束,然后执行脚本中的下一条语句。
这条语句会计算并打印出平均值。这个过程会在数据文件中的每条记录上不断重复。do-while 语句
do
{
statements
} while (condition)
$ gawk '{
> total = 0
> i = 1
> do                   #脚本会保证在条件被求值前至少读取第一个数据字段的内容
> {
> total += $i
> i++
> } while (total < 150)
> print total }' data5
250
160
315for语句
for( variable assignment; condition; iteration process)
将多个功能合并到一个语句有助于简化循环。
$ gawk '{
> total = 0
> for (i = 1; i < 4; i++)
> {
> total += $i
> }
> avg = total / 3
> print "Average:",avg
> }' data5
Average: 128.333
Average: 137.667
Average: 176.667
定义了for循环中的迭代计数器,你就不用担心要像使用while语句一样自己负责给计数器
增值了

格式化打印
下面是printf命令的格式:
printf "format string", var1, var2 . . .
format string是格式化输出的关键。它会用文本元素和格式化指定符来具体指定如何呈
现格式化输出。格式化指定符是一种特殊的代码,会指明显示什么类型的变量以及如何显示。gawk
程序会将每个格式化指定符作为占位符,供命令中的变量使用。第一个格式化指定符对应列出的
第一个变量,第二个对应第二个变量,依此类推。
格式化指定符采用如下格式:
%[modifier]control-letter
其中control-letter是一个单字符代码,用于指明显示什么类型的数据,而modifier则
定义了可选的格式化特性。

格式化指定符的控制字母
控制字母 描 述
c 将一个数作为ASCII字符显示
d 显示一个整数值
i 显示一个整数值(跟d一样)
e 用科学计数法显示一个数
f 显示一个浮点值
g 用科学计数法或浮点数显示(选择较短的格式)
o 显示一个八进制值
s 显示一个文本字符串
x 显示一个十六进制值
X 显示一个十六进制值,但用大写字母A~F

因此,如果你需要显示一个字符串变量,可以用格式化指定符%s。如果你需要显示一个整数
值,可以用%d或%i(%d是十进制数的C风格显示方式)。如果你要用科学计数法显示很大的值,
就用%e格式化指定符。


$ gawk 'BEGIN{
> x = 10 * 100
> printf "The answer is: %e\n", x
> }'
The answer is: 1.000000e+03

除了控制字母外,还有3种修饰符可以用来进一步控制输出。
 width:指定了输出字段最小宽度的数字值。如果输出短于这个值,printf会将文本右
对齐,并用空格进行填充。如果输出比指定的宽度还要长,则按照实际的长度输出。
 prec:这是一个数字值,指定了浮点数中小数点后面位数,或者文本字符串中显示的最
大字符数。
 -(减号):指明在向格式化空间中放入数据时采用左对齐而不是右对齐。

在使用printf语句时,你可以完全控制输出样式。举个例子,我们用print命
令来显示数据行中的数据字段。
$ gawk 'BEGIN{FS="\n"; RS=""} {print $1,$4}' data2
Riley Mullen (312)555-1234
Frank Williams (317)555-9876
Haley Snell (313)555-4938
可以用printf命令来帮助格式化输出,使得输出信息看起来更美观。首先,让我们将print
命令转换成printf命令,看看会怎样。
$ gawk 'BEGIN{FS="\n"; RS=""} {printf "%s %s\n", $1, $4}' data2
Riley Mullen (312)555-1234
Frank Williams (317)555-9876
Haley Snell (313)555-4938
它会产生跟print命令相同的输出。printf命令用%s格式化指定符来作为这两个字符串值的占位符。
注意,你需要在printf命令的末尾手动添加换行符来生成新行。没添加的话,printf命令
会继续在同一行打印后续输出。
如果需要用几个单独的printf命令在同一行上打印多个输出,这就会非常有用。
$ gawk 'BEGIN{FS=","} {printf "%s ", $1} END{printf "\n"}' data1
data11 data21 data31
每个printf的输出都会出现在同一行上。为了终止该行,END部分打印了一个换行符。
$ gawk 'BEGIN{FS=","} {printf "%s ", $1} END{print "\n"}' data1  #END关键字后print与printf区别
data11 data21 data31$
下一步,用修饰符来格式化第一个字符串值。
$  gawk 'BEGIN{FS="\n"; RS=""} {printf "%16s %s\n", $1, $4}' data2Riley Mullen (312)555-1234Frank Williams (317)555-9876Haley Snell (313)555-4938
通过添加一个值为16的修饰符,我们强制第一个字符串的输出宽度为16个字符。默认情况下,
printf命令使用右对齐来将数据放到格式化空间中。要改成左对齐,只需给修饰符加一个减号
即可。
$ gawk 'BEGIN{FS="\n"; RS=""} {printf "%-16s %s\n", $1, $4}' data2
Riley Mullen     (312)555-1234
Frank Williams   (317)555-9876
Haley Snell      (313)555-4938
printf命令在处理浮点值时也非常方便。通过为变量指定一个格式,你可以让输出看起来更统一。
$ gawk '{
> total = 0
> for (i = 1; i < 4; i++)
> {
> total += $i
> }
> avg = total / 3
> printf "Average: %5.1f\n",avg
> }' data5
Average: 128.3
Average: 137.7
Average: 176.7
可以使用%5.1f格式指定符来强制printf命令将浮点值近似到小数点后一位。
#内建函数
1.数学函数
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ awk 'BEGIN{print rand()}'
0.237788
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ awk 'BEGIN{x=int(10*rand());print x}'
2
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ awk 'BEGIN{print int(10*rand())}'
22.字符串函数3.时间函数#自定义函数
定义函数
除了gawk中的内建函数,还可以在gawk程序中创建自定义函数。
要定义自己的函数,必须用function关键字。
function name([variables])
{
statements
}
例如:
function printthird()
{
print $3
}
这个函数会打印记录中的第三个数据字段。
函数还能用return语句返回值:
return value
值可以是变量,或者是最终能计算出值的算式:
function myrand(limit)
{
return int(limit * rand())
}
你可以将函数的返回值赋给gawk程序中的一个变量:
x = myrand(100)
这个变量包含函数的返回值使用自定义函数
在定义函数时,它必须出现在所有代码块之前(包括BEGIN代码块)。
$ gawk '
> function myprint()
> {
> printf "%-16s - %s\n", $1, $4
> }
> BEGIN{FS="\n"; RS=""}
> {
> myprint()
> }' data2
Riley Mullen - (312)555-1234
Frank Williams - (317)555-9876
Haley Snell - (313)555-4938
这个函数定义了myprint()函数,它会格式化记录中的第一个和第四个数据字段以供打印输
出。gawk程序然后用该函数显示出数据文件中的数据
$ awk '
> function myrand(limit) {
> return int( limit * rand())     #return返回值
> }
> BEGIN{
> x = myrand(100)
> print x
> }'
23#创建函数库
$ cat funclib
function myprint()
{
printf "%-16s - %s\n", $1, $4
}
function myrand(limit)
{
return int(limit * rand())
}
function printthird()
{
print $3
}
funclib文件含有三个函数定义。需要使用-f命令行参数来使用它们。很遗憾,不能将-f命令
行参数和内联gawk脚本放到一起使用,不过可以在同一个命令行中使用多个-f参数。
因此,要使用库,只要创建一个含有你的gawk程序的文件,然后在命令行上同时指定库文件
和程序文件就行了。
$ cat script4
BEGIN{ FS="\n"; RS=""}
{
myprint()       ##gawk程序内调用函数使用  functionname()
}
$ gawk -f funclib -f script4 data2
Riley Mullen - (312)555-1234
Frank Williams - (317)555-9876
Haley Snell - (313)555-4938
#实例
$ cat scores.txt
Rich Blum,team1,100,115,95
Barbara Blum,team1,110,115,100
Christine Bresnahan,team2,120,115,118
Tim Bresnahan,team2,125,112,116
$ cat bowling.sh
#!/bin/bash
for team in $(gawk –F, '{print $2}' scores.txt | uniq)
do
gawk –v team=$team 'BEGIN{FS=","; total=0}
{
if ($2==team)
{
total += $3 + $4 + $5
}
}
END {
avg = total / 6;
print "Total for", team, "is", total, ",the average is",avg
}' scores.txt
done
for循环中的第一条语句过滤出数据文件中的队名,然后使用uniq命令返回不重复的队名。
for循环再对每个队进行迭代。for循环内部的gawk语句进行计算操作。对于每一条记录,
首先确定队名是否和正在进行循环的队名相符。这是通过利用gawk的-v选项来实现的,
该选项允许我们在gawk程序中传递shell变量。如果队名相符,代码会对数据记录中的三场比赛得分求和,
然后将每条记录的值再相加,只要数据记录属于同一队。在循环迭代的结尾处,gawk代码会显示出总分以及平均分。输出结果如下。
$ ./bowling.sh
Total for team1 is 635, the average is 105.833
Total for team2 is 706, the average is 117.667使用printf
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ cat test.awk
for team in `gawk -F, '{print $2}' scores.txt|uniq`
do
awk -v team=$team 'BEGIN{FS=",";total=0}
{
if ($2==team)
{
total+=$3+$4+$5
}
}
END{avg=total/6
printf "the avg is:%5.1f,the total is:%d\n",avg,total}' scores.txt
done
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ sh test.awk
the avg is:105.8,the total is:635
the avg is:117.7,the total is:706

第23章        使用其他shell

dash shell:

Debian的dash shell的历史很有趣。它是ash shell的直系后代,而ash shell则是Unix系统上原来
的Bourne shell的简化版本(参见第1章)。Kenneth Almquist为Unix系统开发了一个Bourne shell的简化版本,并将它命名为Almquist shell,缩写为ash。
NetBSD Unix操作系统移植了ash shell,直到今天依然将它用作默认shell。NetBSD开发人员
对ash shell进行了定制,增加了一些新的功能,使它更接近Bourne shell。
Debian Linux发行版创建了它自己的ash shell版本(称作Debian ash,或dash)以供自用。dash
复制了ash shell的NetBSD版本的大多数功能,提供了一些高级命令行编辑能力。
实际上dash shell在许多基于Debian的Linux发行版中并不是默认的shell。由于bash shell在Linux中的流行,大多数基于Debian的Linux发行版将bash shell用作普通登录shell,而只将dash shell作为安装脚本的快速启动shell,用于安装发行版文件。
流行的Ubuntu发行版是例外。这经常让shell脚本程序员摸不清头脑,给Linux环境中运行shell
脚本带来了很多问题。Ubuntu Linux发行版将bash shell用作默认的交互shell,但将dash shell用作
默认的/bin/sh shell。这个“特性”着实让shell脚本程序员一头雾水。
在Unix世界中,默认shell一直是/bin/sh,如果是#!/bin/sh开头的脚本需要查看/bin/sh指向哪里

[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ cat /etc/shells
/bin/sh
/bin/bash
/usr/bin/sh
/usr/bin/bash
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ ll `which sh`
lrwxrwxrwx. 1 root root 4 Jul 11  2019 /bin/sh -> bash#命令行参数
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ bash -c "echo $BASH"
/bin/bash
[dongm@iZbp16mm3xbwen89azh9ffZ ~]$ sh -i
sh-4.2$ echo $BASH
/bin/sh
dash命令行参数
参 数 描 述
-c 从特定命令字符串中读取命令
-i 强制shell运行在交互式模式下

由于dash shell只是Bourne shell功能的一个子集, bash shell脚本中的有些功能没法在dash shell中使用。这些通常被称作bash主义(bashism)。
1. 算术运算
第11章介绍了三种在bash shell脚本中进行数学运算的方法。
 使用expr命令:expr operation。
 使用方括号:$[ operation ]。
 使用双圆括号:$(( operation ))。
dash shell支持expr命令和双圆括号方法,但不支持方括号方法
2. test命令
bash shell的test命令允许你使用双等号(==)来测试两个字符串是否相等。这是为了照顾
习惯在其他编程语言中使用这种格式的程序员而加上去的。
但是,dash shell中的test命令不能识别用作文本比较的==符号,只能识别=符号。如果你在
bash脚本中使用了==符号,就得将文本比较符号改成单个的等号
3. function命令
bash shell支持两种定义函数的方法:
 使用function()语句
 只使用函数名
dash shell不支持function语句。在dash shell中,你必须用函数名和圆括号定义函数。

zsh shell:

1. 执行计算
zsh shell提供了执行数学运算的两种方法:
 let命令
 双圆括号
在使用let命令时,你应该在算式前后加上双引号,这样才能使用空格。
% let value1=" 4 * 5.1 / 3.2 "
% echo $value1
6.3750000000
注意,使用浮点数会带来精度问题。为了解决这个问题,通常要使用printf命令,并指定能正确显示结果所需的小数点精度。
% printf "%6.3f\n" $value1
6.375
现在好多了!
第二种方法是使用双圆括号。这个方法结合了两种定义数学运算的方法。
% value1=$(( 4 * 5.1 ))
% (( value2 = 4 * 5.1 ))
% printf "%6.3f\n" $value1 $value2
20.400
20.400
你可以将双圆括号放在算式两边(前面加个美元符)或整个赋值表达式两边。两种方法输出同样的结果。
如果一开始没用typeset命令来声明变量的数据类型,那么zsh shell会尝试自动分配数据类
型。这在处理整数和浮点数时很危险。看看下面这个例子。
% value1=10
% value2=$(( $value1 / 3 ))
% echo $value2
3
现在这个结果可能并不是你所期望的。在指定数字时没指定小数点后的位数的话,zsh shell
会将它们都当成整数值并进行整数运算。要保证结果是浮点数,你必须指定该数小数点后的位数。
% value1=10.
% value2=$(( $value1 / 3. ))
% echo $value2
3.3333333333333335
结果是浮点数形式了。

小结
本章讨论了可能遇到的两种流行的可选择Linux shell。dash shell是作为Debian Linux发行版的一部分开发的,主要出现在Ubuntu Linux发行版中。它是Bourne shell的精简版,所以它并不像bash shell一样支持那么多功能,这可能会给脚本编程带来一些问题。
zsh shell通常会用在编程环境中,因为它为shell脚本程序员提供了许多好用的功能。它使用可加载的模块来加载单独的代码库,这使得高级函数的使用与在命令行上运行命令一样简单。从复杂的数学算法到网络应用(如FTP和HTTP),可加载模块支持很多功能。

linux命令行和shell脚本编程大全笔记相关推荐

  1. 《Linux命令行和Shell脚本开发圣经》:内容目录

    致谢 导语 第一部分:Linux命令行 第一节:从Linux Shell开始 1 Linux是什么? 1.1 管窥Linux内核(Kernel) 1.2 GNU应用程序 1.3 Linux桌面环境 2 ...

  2. 黑客进阶必读书《Linux命令行与Shell脚本编程大全》(第2版)

    欢迎来到命令行和shell脚本编程的世界!厚厚的一本小样书,拿在手上,厚厚的一本,六百多页.不只是掂在手上的重量,更是一本优秀的初学者入门指南,又会是你的Linux书库中非常不错的参考书.如果你想从整 ...

  3. 【2018深信服 醒狮计划】《Linux命令行与Shell脚本编程大全》学习笔记

    2018深信服"醒狮计划"笔记 第3周(5.02-5.13) 课程 必修 选修 基本要求 Shell编程 <Linux命令行与Shell脚本编程大全> <Perl ...

  4. 《linux命令行与shell脚本编程大全》第三版 - 核心笔记(3/4):函数与图形化脚本

    <linux命令行与shell脚本编程大全> 全书4部分: ☆ [1]linux命令行(1-10章) ☆ [2]shell脚本编程基础(11-16章) ☆ [3]高级shell脚本编程(1 ...

  5. 《Linux命令行与shell脚本编程大全 第3版》Shell脚本编程基础---34

    以下为阅读<Linux命令行与shell脚本编程大全 第3版>的读书笔记,为了方便记录,特地与书的内容保持同步,特意做成一节一次随笔,特记录如下: 转载于:https://www.cnbl ...

  6. 《Linux命令行与shell脚本编程大全 第3版》Shell脚本编程基础---02

    以下为阅读<Linux命令行与shell脚本编程大全 第3版>的读书笔记,为了方便记录,特地与书的内容保持同步,特意做成一节一次随笔,特记录如下: 转载于:https://www.cnbl ...

  7. 《Linux命令行与shell脚本编程大全 第3版》Shell脚本编程基础---20

    以下为阅读<Linux命令行与shell脚本编程大全 第3版>的读书笔记,为了方便记录,特地与书的内容保持同步,特意做成一节一次随笔,特记录如下: 转载于:https://www.cnbl ...

  8. 《Linux命令行与shell脚本编程大全 第3版》Shell脚本编程基础---43

    以下为阅读<Linux命令行与shell脚本编程大全 第3版>的读书笔记,为了方便记录,特地与书的内容保持同步,特意做成一节一次随笔,特记录如下: 转载于:https://www.cnbl ...

  9. 《Linux命令行与shell脚本编程大全 第3版》高级Shell脚本编程---32

    以下为阅读<Linux命令行与shell脚本编程大全 第3版>的读书笔记,为了方便记录,特地与书的内容保持同步,特意做成一节一次随笔,特记录如下: 转载于:https://www.cnbl ...

最新文章

  1. linux下如何查看系统和内核版本
  2. 支付宝支付 第九集:产品数据和支付二维码对接
  3. oracle如何删除可回收归档,Oracle正确删除归档并回收空间的方法
  4. 【每日一题】4月1日题目 Rinne Loves Edges
  5. execl执行linux命令,使用execl运行Linux命令
  6. [转] TF-IDF与余弦相似性的应用(三):自动摘要
  7. php会员整合,会员整合Ucenter/Discuz!/PHPWind教程
  8. 比dropout更好的方法_经营公司有更好的方法
  9. LINUX下的gdb调试方法
  10. Linux配置虚拟主机后,只能访问到主页怎么办?
  11. 【java学习之路】(java SE篇)007.常用类
  12. Bzoj4480: [Jsoi2013]快乐的jyy 广义后缀自动机 倍增 哈希 manacher
  13. 用XAML做网页!!—广告展示区
  14. 单个用户OWA登陆失败,提示用户名和密码错误
  15. linux 16.04系统下载,Ubuntu下载16.04_Ubuntu Desktop32位标准版 - 系统之家
  16. 泱泱大中华,美丽我的家 - 俗晒网速,感受幸福
  17. 深圳大学物计算机黄yilin,中国科学引文数据库(CSCD)收录本校教师论文情况.doc...
  18. 基于STM32使用TTP223点动触摸传感器模块
  19. CSP-S 2020 儒略日
  20. linux与linux之间共享目录

热门文章

  1. 每一年,每一天,我们都在进步
  2. 整个部门就一个研发?
  3. 吴恩达对话刘慈欣 预言AI未来20年
  4. 电脑好玩技巧3——任务栏透明度
  5. 大衣哥唱火的《火火的情怀》,和合国际会100万向孟文豪收购吗
  6. 包子笔记 - 三知道原则
  7. 《羊了个羊》创始人被母校制成展牌...
  8. 新冠肺炎的中西医结合康复方案
  9. dw如何把html转换成网址,我用flash做的网页,怎么把它在dw里变成html网页?
  10. (转)【翻译】火影忍者鸣人 疾风传 终级风暴2 制作介绍