命令都是在终端会话中输入并执行的。打开终端时会出现一个提示符。有很多方法可以配置提示符,不过其形式通常如下:
[zhangsan@izuf67tjsjwg8yv8bjejo1z ~]$
$表示普通用户,#表示管理员用户root。root是Linux系统中权限最高的用户。
以root用户(管理员)的身份直接使用shell来执行任务可不是个好主意。因为如果shell具备较高的权限,命令中出现的输入错误有可能造成更严重的破坏,所以推荐使用普通用户(shell会在提示符中以$来表明这种身份)登录系统,然后借助sudo这类工具来运行特权命令。使用sudo执行命令的效果和root一样。
shell脚本通常以shebang起始:
#/bin/bash
shebang是一个文本行,其中#!位于解释器路径之前。/bin/bash是Bash的解释器命令路径。
bash将以#符号开头的行视为注释。脚本中只有第一行可以使用shebang来定义解释该脚本所使用的解释器。
命令历史
Bash shell还维护了一个历史记录文件~/.bash_history,用于保存用户运行过的命令。
运行多个命令
shell使用分号或换行符来分隔单个命令或命令序列。比如:
[zhangsan@izuf67tjsjwg8yv8bjejo1z ~]$ cmd1; cmd2
变量
变量名由一系列字母、数字和下划线组成,其中不包含空白字符。常用的惯例是在脚本中使用大写字母命名环境变量,使用驼峰命名法或小写字母命名其他变量。
所有的应用程序和脚本都可以访问环境变量。可以使用env或printenv命令查看当前shell中所定义的全部环境变量:
可以使用等号操作符为变量赋值:
[zhangsan@izuf67tjsjwg8yv8bjejo1z ~]$ name=zhangsan
[zhangsan@izuf67tjsjwg8yv8bjejo1z ~]$ echo $name
zhangsan
环境变量
环境变量是从父进程中继承而来的变量。例如环境变量HTTP_PROXY,它定义了Internet连接应该使用哪个代理服务器。
PATH变量列出了一系列可供shell搜索特定应用程序的目录
如果需要在PATH中添加一条新路径,可以使用如下命令:
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# export PATH=”$PATH:/home/user/bin”
另外还有一些众所周知的环境变量:HOME、PWD、USER、UID、SHELL等。
使用shell进行数学运算
let命令可以直接执行基本的算术操作。当使用let时,变量名之前不需要再添加$,例如:
[zhangsan@izuf67tjsjwg8yv8bjejo1z ~]$ num1=10
[zhangsan@izuf67tjsjwg8yv8bjejo1z ~]$ num2=20
[zhangsan@izuf67tjsjwg8yv8bjejo1z ~]$ let total=num1+num2
[zhangsan@izuf67tjsjwg8yv8bjejo1z ~]$ echo $total
30
文件描述符与重定向
文件描述符是与某个打开的文件或数据流相关联的整数。文件描述符0、1以及2是系统预留的。
- 0 —— stdin(标准输入)
- 1 —— stdout(标准输出)
- 2 —— stderr(标准错误)。
1:使用大于号将文本保存到文件中:
[zhangsan@izuf67tjsjwg8yv8bjejo1z ~]$ echo ’test‘ > /tmp/123.txt
[zhangsan@izuf67tjsjwg8yv8bjejo1z ~]$ cat /tmp/123.txt
’test‘
该命令会将输出的文本保存在123.txt中。如果123.txt已经存在,大于号会清空该文件中先前的内容。
2:使用双大于号将文本追加到文件中:
[zhangsan@izuf67tjsjwg8yv8bjejo1z ~]$ echo ’test1 test2‘ >> /tmp/123.txt
[zhangsan@izuf67tjsjwg8yv8bjejo1z ~]$ cat /tmp/123.txt
’test‘
’test1 test2‘
3:当命令产生错误信息时,该信息会被输出到stderr流。
[zhangsan@izuf67tjsjwg8yv8bjejo1z ~]$ mkdir2 124
-bash: mkdir2: command not found
我们使用2>(数字2以及大于号)将stderr重定向到out.txt:
[zhangsan@izuf67tjsjwg8yv8bjejo1z ~]$ mkdir2 124 2> /tmp/out.txt
[zhangsan@izuf67tjsjwg8yv8bjejo1z ~]$ cat /tmp/out.txt
-bash: mkdir2: command not found
你可以将stderr和stdout分别重定向到不同的文件中:
[zhangsan@izuf67tjsjwg8yv8bjejo1z ~]$ mkdir2 124 2> /tmp/stderr.txt 1>/tmp/stdout.txt
[zhangsan@izuf67tjsjwg8yv8bjejo1z ~]$ cat /tmp/stderr.txt
-bash: mkdir2: command not found
[zhangsan@izuf67tjsjwg8yv8bjejo1z ~]$ cat /tmp/stdout.txt
下面这种更好的方法能够将stderr转换成stdout,使得stderr和stdout都被重定向到同一个文件中:
[zhangsan@izuf67tjsjwg8yv8bjejo1z ~]$ mkdir2 124 >& /tmp/stdout.txt
4:tee命令
有一种方法既可以将数据重定向到文件,还可以提供一份重定向数据的副本作为管道中后续命令的stdin。tee命令从stdin中读取,然后将输入数据重定向到stdout以及一个或多个文件中。
[zhangsan@izuf67tjsjwg8yv8bjejo1z ~]$ ls -l /tmp/std* | tee /tmp/5678.txt |wc -l
3
[zhoujun@izuf67tjsjwg8yv8bjejo1z ~]$ cat /tmp/5678.txt
-rw-rw-r– 1 zhangsan zhangsan 33 Nov 6 20:10 /tmp/stderr.txt
-rw-rw-r– 1 zhangsan zhangsan 33 Nov 6 20:14 /tmp/stdout1.txt
-rw-rw-r– 1 zhangsan zhangsan 33 Nov 6 20:14 /tmp/stdout.txt
默认情况下,tee命令会将文件覆盖,但它提供了一个-a选项,可用于追加内容。
工作原理
重定向操作符(>和>>)可以将输出发送到文件中,而不是终端。>和>>略有差异。尽管两者都可以将文本重定向到文件,但是前者会先清空文件,然后再写入内容,而后者会将内容追加到现有文件的尾部。
默认情况下,重定向操作针对的是标准输出。如果想使用特定的文件描述符,你必须将描述符编号置于操作符之前。
等同于1>;对于>>来说,情况也类似(即>>等同于1>>)。
处理错误时,来自stderr的输出被倾倒入文件/dev/null中。./dev/null是一个特殊的设备文件,它会丢弃接收到的任何数据。null设备通常也被称为黑洞,因为凡是进入其中的数据都将一去不返。
普通数组和关联数组
Bash支持普通数组和关联数组,前者使用整数作为数组索引,后者使用字符串作为数组索引。当数据以数字顺序组织的时候,应该使用普通数组,例如一组连续的迭代。当数据以字符串组织的时候,关联数组就派上用场了。
1:普通数组
[root@iZ2ze9v82ax0ox0j12ahfhZ ~]# name_arr=(zhangsan lisi wangwu)
#根据下标查看
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# echo ${name_arr[0]}
zhangsan
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# echo ${name_arr[1]}
lisi
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# echo ${name_arr[2]}
wangwu
#覆盖下标2的值
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# name_arr[2]=’wangwu_2′
#新增
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# name_arr[3]=’zhaoliu’
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# echo ${name_arr[2]}
wangwu_2
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# echo ${name_arr[3]}
zhaoliu
#以列表形式打印出数组中的所有值
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# echo ${name_arr[*]}
zhangsan lisi wangwu_2 zhaoliu
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# echo ${name_arr[@]}
zhangsan lisi wangwu_2 zhaoliu
#查看数组长度
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# echo ${#name_arr[@]}
4
2:关联数组
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# declare -A score_arr
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# score_arr=([zhangsan]=80 [lisi]=70)
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# score_arr[wangwu]=90
#根据索引找到对应的值
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# echo ${score_arr[zhangsan]}
80
#查看索引列表
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# echo ${!score_arr[*]}
zhangsan lisi wangwu
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# echo ${!score_arr[@]}
zhangsan lisi wangwu
#查看值列表
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# echo ${score_arr[*]}
80 70 90
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# echo ${score_arr[@]}
80 70 90
别名
别名就是一种便捷方式,可以为用户省去输入一长串命令序列的麻烦。
下面我们会看到如何使用alias命令创建别名
例如:
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# alias show=’ls -a’
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# show
. .bash_history .bash_profile .cache .cshrc install.sh .mysql_history .pearrc perf.data.old .pki .ssh .viminfo
.. .bash_logout .bashrc .config .gitconfig .local .npm perf.data .pip .pydistutils.cfg .tcshrc
alias命令的效果只是暂时的。一旦关闭当前终端,所有设置过的别名就失效了。
为了使别名在所有的shell中都可用,可以将其定义放入~/.bashrc文件中。每当一个新的交互式shell进程生成时,都会执行~/.bashrc中的命令。
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# echo ” alias show=’ls -a’ ” >> ~/.bashrc
如果需要删除别名,只需将其对应的定义(如果有的话)从~/.bashrc中删除,或者使用unalias命令。也可以使用alias example=,这会取消别名example。
函数
函数的定义包括function命令、函数名、开/闭括号以及包含在一对花括号中的函数体。
传入脚本的参数可以通过下列形式访问。
- $0是脚本名称。
- $1是第一个参数
- $2是第二个参数。
- $n是第n个参数。
- “$@”被扩展成”$1” “$2” “$3″等。
- “$*”被扩展成”$1c$2c$3″,其中c是IFS的第一个字符。
- “$@”要比”$*”用得多。由于”$*”将所有的参数当作单个字符串,因此它很少被使用。
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# function test(){
echo “test”;
echo $1,$2;
echo “$@”;
echo “$*”;
return 0;
}
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# test zhangsan lisi wangwu zhaoliu
test
zhangsan,lisi
zhangsan lisi wangwu zhaoliu
zhangsan lisi wangwu zhaoliu
管道
Unix shell脚本最棒的特性之一就是可以轻松地将多个命令组合起来生成输出。一个命令的输出可以作为另一个命令的输入,而这个命令的输出又会传递至下一个命令,以此类推。这种命令组合的输出可以被存储在变量中
命令输入通常来自于stdin或参数。输出可以发送给stdout或stderr。当我们组合多个命令时,通常将stdin用于输入,stdout用于输出。在这种情况下,这些命令被称为过滤器(filter)。
我们使用管道(pipe)连接每个过滤器,管道操作符是|。
例如:
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# ls -l /tmp/ | wc -l
231
read命令
Bash命令read能够从键盘或标准输入中读取文本。
1:从输入中读取2个字符并存入变量name:
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# read -n 2 name
2:用无回显的方式读取密码:
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# read -s password
3:使用read显示提示信息:
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# read -s -p “Enter Your Password:” password
4:在给定时限内读取输入
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# read -t 2 name
流程控制
程序中的流程控制是由比较语句和测试语句处理的。Bash能够执行各种测试。我们可以用if、if else以及逻辑运算符来测试,用比较运算符来比较数据项。除此之外,还有一个test命令也可以用于测试。
if条件:
else if 和else
if和else语句能够嵌套使用。if的条件判断部分可能会变得很长,但可以用逻辑运算符将它变得简洁一些:
- [condition] && action; # 如果condition为真,则执行action
- [condition] || action; # 如果condition为假,则执行action
算术比较:
比较条件通常被放置在封闭的中括号内。一定要注意在[]与操作数之间有一个空格。
对变量或值进行算术条件测试:
其他重要的操作符如下。
- -gt:大于。
- -lt:小于。
- -ge:大于或等于。
- -le:小于或等于。
案例:
[root@iZ2ze9v82ax0ox0j12ahfhZ tmp]# cat test.sh
#!/bin/bash
num1=$1
num2=$2
if [ $num1 -gt $num2 ]
then
echo ‘num1>num2’
else
echo ‘num2>num1’
fi
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ tmp]# bash ./test.sh 10 20
num2>num1
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ tmp]# bash ./test.sh 20 30
num2>num1
文件系统相关测试
- [-f $file_var]:如果给定的变量包含正常的文件路径或文件名,则返回真。
- [-x $var]:如果给定的变量包含的文件可执行,则返回真。
- [-d $var]:如果给定的变量包含的是目录,则返回真。
- [-e $var]:如果给定的变量包含的文件存在,则返回真。
- [-c $var]:如果给定的变量包含的是一个字符设备文件的路径,则返回真。
- [-b $var]:如果给定的变量包含的是一个块设备文件的路径,则返回真。
- [-w $var]:如果给定的变量包含的文件可写,则返回真。
- [-r $var]:如果给定的变量包含的文件可读,则返回真。
- [-L $var]:如果给定的变量包含的是一个符号链接,则返回真。
字符串比较
进行字符串比较时,最好用双中括号,因为有时候采用单个中括号会产生错误。
测试两个字符串是否相同。
[[$str1=$str2]]:当str1等于str2时,返回真。也就是说,str1和str2包含的文本是一模一样的
[[$str1==$str2]]:这是检查字符串是否相同的另一种写法。
测试两个字符串是否不同。
[[$str1 !=$str2]]:如果str1和str2不相同,则返回真。
测试空串。
- [[-z $str1]]:如果str1为空串,则返回真。
- [[-n $str1]]:如果str1不为空串,则返回真。
录制并回放终端会话
script命令能够录制你的击键以及击键时机,并将输入和输出结果保存在对应的文件中。scriptreplay命令可以回放会话
script和scriptreplay命令在绝大多数GNU/Linux发行版上都可以找到。你可以通过录制终端会话来制作命令行技巧视频教程,也可以与他人分享会话记录文件,研究如何使用命令行完成某项任务。你甚至可以调用其他解释器并录制发送给该解释器的击键。但你无法记录vi、emacs或其他将字符映射到屏幕特定位置的应用程序。
案例:
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ tmp]# rm -rf testscript.txt
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ tmp]# script -t 2>testscripterror.txt -a testscript.txt
Script started, file is testscript.txt
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ tmp]# ls -l |wc -l
233
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ tmp]# cd /www/wwwroot/
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ wwwroot]# ls -l
total 260
drwxr-xr-x 27 501 games 4096 Mar 20 2014 apue.3e
drwxr-xr-x 4 www www 4096 Aug 4 22:45 blog
drwxr-xr-x 8 root root 4096 Sep 12 10:10 gitrep
-rw-r–r– 1 root root 238649 Jun 30 05:31 tlpi-161214-dist.tar.gz
drwxr-xr-x 48 www www 4096 Jul 6 10:11 tlpi-dist
drwxr-xr-x 40 root root 4096 Apr 14 2021 unpv13e
drwxr-xr-x 7 www www 4096 Nov 6 23:04 wordpress
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ wwwroot]# exit
exit
Script done, file is testscript.txt
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ tmp]# cat testscript.txt
Script started on Sun 07 Nov 2021 08:24:08 PM CST
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ tmp]# ls -l |wc -l
233
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ tmp]# cd /www/wwwroot/
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ wwwroot]# ls -l
total 260
drwxr-xr-x 27 501 games 4096 Mar 20 2014 apue.3e
drwxr-xr-x 4 www www 4096 Aug 4 22:45 blog
drwxr-xr-x 8 root root 4096 Sep 12 10:10 gitrep
-rw-r–r– 1 root root 238649 Jun 30 05:31 tlpi-161214-dist.tar.gz
drwxr-xr-x 48 www www 4096 Jul 6 10:11 tlpi-dist
drwxr-xr-x 40 root root 4096 Apr 14 2021 unpv13e
drwxr-xr-x 7 www www 4096 Nov 6 23:04 wordpress
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ wwwroot]# exit
exit
Script done on Sun 07 Nov 2021 08:24:20 PM CST
xargs命令
xargs 是一个强有力的命令,它能够捕获一个命令的输出,然后传递给另外一个命令。
之所以能用到这个命令,关键是由于很多命令不支持|管道来传递参数,而日常工作中有有这个必要,所以就有了 xargs 命令,例如:
find /sbin -perm +700 |ls -l #这个命令是错误的 find /sbin -perm +700 |xargs ls -l #这样才是正确的
xargs 一般是和管道一起使用。
命令格式:
somecommand |xargs [-options] command
参数:
- -a file 从文件中读入作为 stdin
- -e flag ,注意有的时候可能会是-E,flag必须是一个以空格分隔的标志,当xargs分析到含有flag这个标志的时候就停止。
- -p 当每次执行一个argument的时候询问一次用户。
- -n num 后面加次数,表示命令在执行的时候一次用的argument的个数,默认是用所有的。
- -t 表示先打印命令,然后再执行。
- -i 或者是-I,这得看linux支持了,将xargs的每项名称,一般是一行一行赋值给 {},可以用 {} 代替。
- -r no-run-if-empty 当xargs的输入为空的时候则停止xargs,不用再去执行了。
- -s num 命令行的最大字符数,指的是 xargs 后面那个命令的最大命令行字符数。
- -L num 从标准输入一次读取 num 行送给 command 命令。
- -l 同 -L。
- -d delim 分隔符,默认的xargs分隔符是回车,argument的分隔符是空格,这里修改的是xargs的分隔符。
- -x exit的意思,主要是配合-s使用
用tr进行转换
tr是translate(转换)的简写,因为它可以将一组字符转换成另一组字符。
tr只能通过stdin(标准输入)接收输入(无法通过命令行参数接收)。其调用格式如下:
tr [options] set1 set2
来自stdin的输入字符会按照位置从set1映射到set2(set1中的第一个字符映射到set2中的第一个字符,以此类推),然后将输出写入stdout(标准输出)。
set1和set2是字符类或字符组。如果两个字符组的长度不相等,那么set2会不断复制其最后一个字符,直到长度与set1相同。如果set2的长度大于set1,那么在set2中超出set1长度的那部分字符则全部被忽略。
#要将输入中的字符由小写转换成大写,可以使用下面的命令:
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ tmp]# echo ” hello world ” | tr ‘a-z’ ‘A-Z’
HELLO WORLD
分割文件与数据
split命令可以用来分割文件。该命令接受文件名作为参数,然后创建出一系列体积更小的文件,其中依据字母序排在首位的那个文件对应于原始文件的第一部分,排在次位的文件对应于原始文件的第二部分,以此类推。
例如,通过指定分割大小,可以将100KB的文件分成一系列10KB的小文件。
在split命令中,除了k(KB),我们还可以使用M(MB)、G(GB)、c(byte)和w(word)。
#将文件按照5k大小切割
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# ls -lh
total 7.2M
-rw-r–r– 1 www www 29K Aug 2 23:35 app.20210802.log
-rw-r–r– 1 root root 25K Jan 9 2021 install.sh
-rw——- 1 root root 887K May 19 22:48 perf.data
-rw——- 1 root root 6.3M May 19 22:48 perf.data.old
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# split -b 5k app.20210802.log
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# ls -lh
total 7.3M
-rw-r–r– 1 www www 29K Aug 2 23:35 app.20210802.log
-rw-r–r– 1 root root 25K Jan 9 2021 install.sh
-rw——- 1 root root 887K May 19 22:48 perf.data
-rw——- 1 root root 6.3M May 19 22:48 perf.data.old
-rw-r–r– 1 root root 5.0K Nov 8 21:19 xaa
-rw-r–r– 1 root root 5.0K Nov 8 21:19 xab
-rw-r–r– 1 root root 5.0K Nov 8 21:19 xac
-rw-r–r– 1 root root 5.0K Nov 8 21:19 xad
-rw-r–r– 1 root root 5.0K Nov 8 21:19 xae
-rw-r–r– 1 root root 4.0K Nov 8 21:19 xaf
这些新文件以xab、xac、xad的形式命名。split默认使用字母后缀。如果想使用数字后缀,需要使用-d选项。此外,-a length可以指定后缀长度:
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# split -b 5k -d -a 4 app.20210802.log
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# ls -l
total 7428
-rw-r–r– 1 www www 29632 Aug 2 23:35 app.20210802.log
-rw-r–r– 1 root root 25386 Jan 9 2021 install.sh
-rw——- 1 root root 907384 May 19 22:48 perf.data
-rw——- 1 root root 6543280 May 19 22:48 perf.data.old
-rw-r–r– 1 root root 5120 Nov 8 21:24 x0000
-rw-r–r– 1 root root 5120 Nov 8 21:24 x0001
-rw-r–r– 1 root root 5120 Nov 8 21:24 x0002
-rw-r–r– 1 root root 5120 Nov 8 21:24 x0003
-rw-r–r– 1 root root 5120 Nov 8 21:24 x0004
-rw-r–r– 1 root root 4032 Nov 8 21:24 x0005
-rw-r–r– 1 root root 5120 Nov 8 21:19 xaa
-rw-r–r– 1 root root 5120 Nov 8 21:19 xab
-rw-r–r– 1 root root 5120 Nov 8 21:19 xac
-rw-r–r– 1 root root 5120 Nov 8 21:19 xad
-rw-r–r– 1 root root 5120 Nov 8 21:19 xae
-rw-r–r– 1 root root 4032 Nov 8 21:19 xaf
之前那些分割后的文件名都是以x作为前缀。如果要分割的文件不止一个,我们自然希望能自己命名这些分割后的文件,这样才能够知道这些文件分别属于哪个原始文件。这可以通过提供一个前缀作为最后一个参数来实现。
[root@iZ2ze9v82ax0ox0j12ahfhZ ~]# split -b 5k -d -a 4 app.20210802.log log
[root@iZ2ze9v82ax0ox0j12ahfhZ ~]# ls -l
total 7472
-rw-r–r– 1 www www 29632 Aug 2 23:35 app.20210802.log
-rw-r–r– 1 root root 25386 Jan 9 2021 install.sh
-rw-r–r– 1 root root 5120 Nov 8 21:29 log0000
-rw-r–r– 1 root root 5120 Nov 8 21:29 log0001
-rw-r–r– 1 root root 5120 Nov 8 21:29 log0002
-rw-r–r– 1 root root 5120 Nov 8 21:29 log0003
-rw-r–r– 1 root root 5120 Nov 8 21:29 log0004
-rw-r–r– 1 root root 4032 Nov 8 21:29 log0005
生成任意大小的文件
创建特定大小的文件最简单的方法就是利用dd命令。
dd命令会克隆给定的输入内容,然后将一模一样的一份副本写入到输出。
stdin、设备文件、普通文件等都可作为输入,stdout、设备文件、普通文件等也可作为输出。
#生成一个10M的文件
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# dd if=/dev/zero of=/tmp/test.log bs=1M count=10
10+0 records in
10+0 records out
10485760 bytes (10 MB) copied, 0.00729728 s, 1.4 GB/s
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# ls -lh /tmp/test.log
-rw-r–r– 1 zhangsan zhangsan 10M Nov 8 21:40 /tmp/test.log
来看一下命令参数:
- if表示输入文件(input file);
- of表示输出文件(output file);
- bs指定了以字节为单位的块大小(block size);
- count表示需要被复制的块数
以root身份使用dd命令时一定得留意,该命令运行在设备底层。要是你不小心出了岔子,搞不好会把磁盘清空或是损坏数据。一定要反复检查dd命令所用的语法是否正确,尤其是参数of=。
块大小(bs)可以使用各种计量单位:
如果不指定输入参数(if),dd会从stdin中读取输入。如果不指定输出参数(of),则dd会使用stdout作为输出。
将文件设置为不可修改
chattr命令可用于更改扩展属性。它能够将文件设置为不可修改。一旦设置,包括root在内的任何用户都无法删除该文件,除非撤销其不可修改的属性。
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ tmp]# chattr +i test.sh
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ tmp]# rm test.sh
rm: remove regular file ‘test.sh’? y
rm: cannot remove ‘test.sh’: Operation not permitted
如果需要使文件恢复可写状态,撤销不可修改属性即可
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ tmp]# chattr -i test.sh
按列合并多个文件
可以用paste命令实现按列合并,其语法如下:
paste file1 fil2 file3 …..
默认的分隔符是制表符,也可以用-d指定分隔符:
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ tmp]# cat file1.txt
1
2
3
4
5
6
7
8
9
10
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ tmp]# cat file2.txt
zhangsan
lisi
wangwu
zhaoliu
xiaoming
zhaoliang
wangjun
wuyan
maliang
hegang
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ tmp]# paste file1.txt file2.txt
1 zhangsan
2 lisi
3 wangwu
4 zhaoliu
5 xiaoming
6 zhaoliang
7 wangjun
8 wuyan
9 maliang
10 hegang
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ tmp]# paste file1.txt file2.txt -d “,”
1,zhangsan
2,lisi
3,wangwu
4,zhaoliu
5,xiaoming
6,zhaoliang
7,wangjun
8,wuyan
9,maliang
10,hegang
wget的使用
wget是一个用于文件下载的命令行工具,选项繁多且用法灵活。
1:下载限速
当下载带宽有限,却又有多个应用程序共享网络连接时,下载大文件会榨干所有的带宽,严重阻滞其他进程(可能是交互式用户)。选项–limit-rate可以限定下载任务能够占有的最大带宽,从而保证其他应用程序能够公平地访问Internet:
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# wget –limit-rate 20k https://bc58.net/
在命令中可以用k(千字节)和m(兆字节)指定速度限制
选项–quota或-Q可以指定最大下载配额(quota)。配额一旦用尽,下载随之停止。在下载多个文件时,对于存储空间有限的系统,限制总下载量是有必要的:
2:断点续传
如果wget在下载完成之前被中断,可以利用选项-c从断点开始继续下载:
3:复制整个网站(镜像)
wget像爬虫一样以递归的方式遍历网页上所有的URL链接,并逐个下载。要实现这种操作,可以使用选项–mirror:
或者
选项-l指定页面层级(深度)。这意味着wget只会向下遍历指定层数的页面。该选项要与-r(recursive,递归选项)一同使用。另外,-N表示使用文件的时间戳。URL表示欲下载的网站起始地址。-k或–convert-links指示wget将页面的链接地址转换为本地地址。
对网站进行镜像时,请三思而行。除非获得许可,否则你只应出于个人使用的目的才可以这么做,而且不要频繁地做镜像。
TCP/IP网络的运作过程
TCP/IP网络的运作过程就是在节点之间传递分组(packet)。每一个分组中都包含了目标的IP地址以及处理分组中数据的应用程序端口号。
当节点接收到分组时,它会查看自己是否就是该分组的目的地。如果是,节点会再检查端口号并调用相应的应用程序来处理分组数据。如果不是,节点则根据已知的网络配置,将分组发送到离最终目的地更近的下一个节点。
网络设置
网络接口用于将主机以有线或无线的形式连接到网络。Linux使用eth0、eth1或enp0s25(指代以太网接口)这种方式来命名网络接口。还有一些其他的接口,如usb0、wlan0以及tun0,分别对应USB网络接口、无线LAN和隧道。
名字服务器与DNS(域名服务)
Internet底层的寻址方案是采用点分十进制形式的IP地址(例如83.166.169.231)。相较于数字,人类更喜欢使用文字,因此Internet上的资源是通过被称为URL或域名的字符串来标识的。
将IP地址映射为符号名称的这种技术称为域名服务(DNS)。当我们输入www.google.com,计算机使用DNS服务器将域名解析为对应的IP地址。在本地网络中,我们可以设置本地DNS为本地主机命名。
名字服务器是在文件 /etc/resolv.conf中定义的:
DNS查找
有多种基于命令行的DNS查找工具都可以实现名字与IP地址的解析。host和nslookup就是其中两个常用工具。
host命令会列出某个域名所有的IP地址:
zhangsan@dev ~ % host baidu.com
baidu.com has address 220.181.38.251
baidu.com has address 220.181.38.148
baidu.com mail is handled by 20 usmx01.baidu.com.
baidu.com mail is handled by 10 mx.maillb.baidu.com.
baidu.com mail is handled by 15 mx.n.shifen.com.
baidu.com mail is handled by 20 mx1.baidu.com.
baidu.com mail is handled by 20 jpmx.baidu.com.
baidu.com mail is handled by 20 mx50.baidu.com.
nslookup命令可以完成名字与IP地址之间的相互映射:
zhoujuzhangsan@dev ~ % nslookup baidu.com
Server: 192.168.2.1
Address: 192.168.2.1#53
Non-authoritative answer:
Name: baidu.com
Address: 220.181.38.251
Name: baidu.com
Address: 220.181.38.148
显示路由表信息
多个网络相互连接是很常见的场景。例如,工作场所或学校的不同部门可能处于不同的网络中。如果一个网络中的设备想同另一个网络中的设备通信,就需要借助某个同时连接了两个网络的设备发送分组。这个特殊的设备叫作网关,它的作用是在不同的网络中转发分组。
操作系统维护着一个叫作路由表的表格,它包含了分组如何转发的信息。route命令可以显示路由表:
[zhangsan@dev ~]# route
也可以使用
[zhangsan@dev ~]# route -n
如果系统不知道如何分组到目的地的路由,它会将其发送到默认网关。默认网关可以连接到Internet或部门内部的路由器。
ping
ping是一个基础的网络命令,所有主流操作系统都支持该命令。ping可用于检验网络上主机之间的连通性,找出活动主机。
ping命令使用Internet控制消息协议(Internet Control Message Protocol,ICMP)中的echo分组检验网络上两台主机之间的连通性。当向某台主机发送echo分组时,如果分组能够送达且该主机处于活动状态,那么它就会返回一条回应(reply)。如果没有通往目标主机的路由或是目标主机不知道如何将回应返回给请求方,ping命令则执行失败
[zhangsan@dev ~]# ping baidu.com
PING baidu.com (220.181.38.251) 56(84) bytes of data.
64 bytes from 220.181.38.251 (220.181.38.251): icmp_seq=1 ttl=53 time=5.30 ms
64 bytes from 220.181.38.251 (220.181.38.251): icmp_seq=2 ttl=53 time=4.82 ms
64 bytes from 220.181.38.251 (220.181.38.251): icmp_seq=3 ttl=53 time=4.97 ms
64 bytes from 220.181.38.251 (220.181.38.251): icmp_seq=4 ttl=53 time=4.83 ms
如果主机不可达,则输出如下信息:
在主机不可达时,ping返回错误信息Destination Host Unreachable。
网络管理员通常会对网络设备(如路由器)进行配置,使其不响应ping命令。这样做是为了降低安全风险,因为ping可以被攻击者(使用蛮力)用来获取主机的IP地址。
跟踪IP路由
当应用程序通过Internet请求服务时,服务器可能位于远端,两者之间通过多个网关或路由器相连。
traceroute命令可以显示分组途径的所有网关的地址。这些信息可以帮助我们搞明白分组到达目的地需要经过多少跳。中途的网关或路由器的数量给出了网络上两个节点之间的有效距离,这未必和物理距离有关。传输时间会随着每一跳增加。对于路由器而言,接收、解析以及发送分组都是需要花时间的。
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# traceroute baidu.com
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# mtr baidu.com
分析网络流量与端口
每一个应用程序都需要通过端口访问网络。通过获取开放端口列表、使用特定端口的应用以及运行该应用的用户,是跟踪系统中出现预期和非预期行为的一种方法。这些信息既可用于分配资源,也可用于检查rootkits或其他恶意软件。
很多命令都可用来列出端口以及运行在端口上的服务。lsof和netstat命令在绝大部分GNU/Linux发行版中都可以使用。
lsof(list open files)命令可以列出已打开的文件。选项-i将范围限制在已打开的网络连接:
[root@iZ2ze9v82ax0ox0j12ahfhZ ~]# lsof -i
lsof的每一项输出都对应着一个开放端口上的服务。输出的最后一列类似于:
输出中的laptop.local:41197对应本地主机,192.168.0.2:3128对应远程主机。41197是本地主机当前的开放端口,3128是远程主机上的服务端口
测量网络带宽
iperf能够提供更多的网络性能指标。系统中默认并没有安装该命令,可以通过发行版的包管理器自行安装。
iperf必须安装在链路的两端(服务器端和客户端)。安装好之后,启动服务器端:
然后运行客户端,生成吞吐量统计:
选项-m会使得iperf找出最大传输单元(Maximum Transfer Size,MTU):
监视磁盘使用情况
du(disk usage)和df(disk free)命令可以报告磁盘使用情况。这两个工具能够统计出文件和目录的磁盘占用情况以及可用的磁盘空间。
计算命令执行时间
time命令可以测量出应用程序的执行时间:
time命令会执行APPLICATION。当APPLICATION执行完毕后,time命令将其real时间、sys时间以及user时间输出到stderr中,将APPLICATION的正常输出发送到stdout。
time命令默认报告3类时间。
- Real:指的是壁钟时间(wall clock time),也就是命令从开始执行到结束的时间。这段时间包括其他进程所占用的时间片(time slice)以及进程被阻塞时所消耗的时间(例如,为等待I/O操作完成所用的时间)。
- User:是指进程花费在用户模式(内核模式之外)中的CPU时间。这是执行进程所花费的时间。执行其他进程以及花费在阻塞状态中的时间并没有计算在内。
- Sys:是指进程花费在内核中的CPU时间。它代表在内核中执行系统调用所使用的时间,这和库代码(library code)不同,后者仍旧运行在用户空间。与“user时间”类似,这也是真正由进程使用的CPU时间。
记录文件及目录访问情况
出于各种原因,我们需要清楚文件何时被访问。可能是出于备份的需要,也可能是因为想知道/bin中的文件是否被骇客修改过。
inotifywait命令会监视文件或目录并报告何时发生了某种事件。Linux发行版默认并没有包含该命令,你得用软件包管理器自行安装inotify-tools。这个命令还需要Linux内核的支持。目前大多数新的GNU/Linux发行版已经将inotify支持编译进了内核。
使用syslog记录日志
与守护进程和系统进程相关的日志文件位于/var/log目录中。在Linux系统中,由守护进程sylogd使用syslog标准协议处理日志。每一个标准应用程序都可以利用syslogd记录日志。在这则攻略中,我们将讨论如何在脚本中用syslogd进行日志记录。
日志文件有助于我们推断系统出现了什么故障。作为一种良好的实践,应当使用日志文件记录程序的执行过程。logger命令可以通过syslogd记录日志。
表9-3是一些标准的Linux日志文件。有些发行版采用了不同的文件名。
信号
信号能够中断正在运行的程序。当进程接收到一个信号时,它会执行对应的信号处理程序(signalhandler)作为响应。编译型的应用程序使用系统调用kill生成信号。在命令行(或是shell脚本)中是通过kill命令来实现的。trap命令可以在脚本中用来处理所接收的信号。
每个信号都有对应的名字以及整数值。SIGKILL (9)信号会立即终止进程。Ctrl+C会发送信号中断任务[插图],Ctrl+Z会发送信号将任务置入后台。
1:kill -l命令可以列出所有可用的信号
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
2:终止进程:
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# kill PROCESS_ID_LIST
kill命令默认发送SIGTERM信号。进程ID列表中使用空格来分隔各个进程ID。
3:我们经常需要强行杀死进程,这样做的时候要小心。这种做法立刻生效,根本没有机会保存数据或执行通常的清理工作。应该先尝试使用SIGTERM,将SIGKILL留作最后一招:
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# kill 9 PROCESS_ID
参数SIGNAL可以是信号名或编号。尽管信号的用途各种各样,但常用的其实也就是那么几个。
- SIGHUP 1:对控制进程或终端的结束进行挂起检测(hangup detection)。
- SIGINT 2:当按下Ctrl+C时发送该信号。
- SIGKILL 9:用于强行杀死进程。
- SIGTERM 15:默认用于终止进程。
- SIGTSTP 20:当按下Ctrl+Z时发送该信号。
/proc文件系统
/proc是一种存在于内存中的伪文件系统(pseudo filesystem),它的引入是为了可以从用户空间中读取Linux内核的内部数据结构。其中大多数伪文件都是只读的。
/proc目录中包含了多个文件和目录。其中大多数文件可以使用cat、less或more命令来查看,其内容都是纯文本格式
系统中每一个运行的进程在/proc中都有一个对应的目录,目录名和进程ID相同。以Bash为例,它的PID是4295(pgrep bash),那么就会存在一个对应的目录/proc/4295。该目录中包含了大量有关进程的信息。/proc/PID中的文件包括以下几个。
- environ:包含与进程相关的环境变量。使用cat /proc/4295/environ可以显示所有传递给进程4295的环境变量。
- cwd:这是一个到进程工作目录的符号链接。
- exe:这是一个到进程所对应的可执行文件的符号链接。
- fd:这是一个目录,包含了进程所用到的文件描述符。0、1、2分别对应于stdin、stdout、stderr。
- io:该文件显示了进程所读/写的字符数。
使用cron进行调度
cron表项指定了执行时间以及要执行的命令。cron表中的每一行都定义了单条命令。命令可以是脚本或二进制可执行文件。当cron执行命令的时候是以该表项创建者的身份执行的,但它不会去执行该用户的.bashrc文件。如果命令需要使用环境变量,必须在crontab中定义。
cron表中的每一行(表项)均由6个字段组成,字段之间以空格分隔并按照以下顺序排列:
- 分钟(0~59);
- 小时(0~23);
- 天(1~31);
- 月份(1~12);
- 星期中的某天(0~6);
- 命令(在指定时间执行的脚本或命令)
前5个字段指定了命令开始执行的时间。多个值之间用逗号分隔(不要用空格)。星号表示任何时间段。除号表示调度的时间间隔(在分钟字段上出现的*/5表示每隔5分钟)。