一、Shell入门简介
1.1 什么是shell
1.2
1.3 第一个伟大的shell脚本
#创建HelloWorld.sh文件
[lln@hadoop102 shell]$ touch HelloWorld.sh
#编辑HelloWorld.sh文件
[lln@hadoop102 shell]$ vim HelloWorld.sh
#查看HelloWorld.sh文件
[root@hadoop102 shell]# cat HelloWorld.sh
#!/bin/bash
# This is ower first shell
# by author lln 2022.8.25
echo "hello world"
#查看文件目录
[lln@hadoop102 shell]$ ll
总用量 4
-rw-rw-r--. 1 lln lln 84 8月 25 09:46 HelloWorld.sh
#运行HelloWorld.sh脚本
[lln@hadoop102 shell]$ ./HelloWorld.sh
-bash: ./HelloWorld.sh: 权限不够
#赋予执行权限
[root@hadoop102 shell]# chmod +x HelloWorld.sh
#再次运行HelloWorld.sh脚本
[root@hadoop102 shell]# ./HelloWorld.sh
hello world
[root@hadoop102 shell]#
1.4 shell脚本执行方式
在新进程中运行shell脚本
- 将shell脚本作为程序运行
Shell 脚本也是一种解释执行的程序,可以在终端直接调用(需要使用 chmod 命令给 Shell 脚本加上执行权限),如下所示:
#! 是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即使用哪一种 Shell。
通过这种方式运行脚本,脚本文件第一行的#!/bin/bash一定要写对,好让系统查找到正确的解释器。
chmod +x表示给 a.sh 增加执行权限。
./表示当前目录,整条命令的意思是执行当前目录下的 a.sh 脚本。如果不写./,Linux 会到系统路径(由 PATH环境变量指定)下查找 a.sh,而系统路径下显然不存在这个脚本,所以会执行失败。
- 将shell脚本作为参数传递给Bash解释器
直接运行 Bash 解释器,将脚本文件的名字作为参数传递给 Bash。
通过这种方式运行脚本,不需要在脚本文件的第一行指定解释器信息,写了也没用。
更加简洁的写法是运行 bash 命令。bash 是一个外部命令,Shell 会在 /bin 目录中找到对应的应用程序,也即 /bin/bash,这点我们已在《Shell命令的本质到底是什么》一节中提到。
这两种写法在本质上是一样的:第一种写法给出了绝对路径,会直接运行 Bash 解释器;第二种写法通过 bash 命令找到 Bash 解释器所在的目录,然后再运行,只不过多了一个查找的过程而已。
检测是否开启了新进程
Linux 中的每一个进程都有一个唯一的 ID,称为 PID,使用$$变量就可以获取当前进程的 PID。
进程的 PID 都不一样,当然就是两个进程了。
在当前进程中运行shell脚本
source 是 Shell 内置命令的一种,它会读取脚本文件中的代码,并依次执行所有语句。你也可以理解为,source 命令会强制执行脚本文件中的全部命令,而忽略脚本文件的权限。
source 命令的用法为:
source filename
也可以简写为:
. filename
两种写法的效果相同。对于第二种写法,注意点号.
和文件名
中间有一个空格
。
使用 source 命令不用给脚本增加执行权限,并且写不写./
都行。
进程的 PID 都是一样的,当然是同一个进程了。
现有一a.sh脚本
#!/bin/bash
echo "step 1 sleeping"
sleep 200
echo "step 2 sleeping"
sleep 200
./a.sh
sh ./a.sh
bash ./a.sh
,此三种执行脚本的方式都是重新启动一个shell,在子shell中执行此脚本,脚本中设置的变量在脚本执行完毕后不会保存。(但是若 script.sh 脚本不是以 #!/bin/bash 开头,那么也不会在子进程中执行。)./a.sh
将a.sh当成一个文件执行,此时需要x运行权限,同时有两个进程进行:a.sh和sleep。如果在执行到第一个sleep时终止脚本,a.sh和sleep一起终止,并且第二个sleep不会执行,因为整个a.sh运行已经终止。source ./a.sh
和. ./a.sh
的执行方式是等价的,即这两种执行方式都是在当前shell进程中执行此脚本,而不是重新启动一个shell在子shell进程中执行此脚本,并且脚本中设置的变量在脚本执行完毕后会保存下来。source
是Shell(准确地说是Bash)的内置命令
,在Bourne Shell
中的等价命令是一个点.
,即点命令
。用source
命令执行脚本文件时,是在当前Shell进程中
执行,而不是像./
与sh
方式一样在新的Shell进程
中执行,因此早先设置的变量在脚本里是可以读取到的。不需要执行权限,有读取权限(r权限)即可。如果在执行第1个sleep时按ctrl+c终止,那么第2个sleep接着运行,直到脚本所有命令执行完。source
一般不用来执行业务脚本,最常见用途是在某些初始化脚本修改之后使其立即生效,即source /etc/profile这样。
二、shell变量
2.1 shell变量介绍
Shell编程中变量分为三种,分别是系统变量、环境变量和用户变量。
Shell变量名在定义时,首个字符必须为字母(a-z,A-Z),不能以数字开头,中间不能有空格,可以使用下划线_
,不能使用-
,也不能使用标点符号等。
等号两侧不能有空格。
2.2 shell系统变量
# 系统变量,主要是用于对参数判断和命令返回值判断时使用,系统变量详解如下:
$0 当前脚本的名称;
$n 当前脚本的第n个参数,n=1,2,…9;
$* 当前脚本的所有参数(不包括程序本身);
$# 当前脚本的参数个数(不包括程序本身);
$? 令或程序执行完后的状态,返回0表示执行成功;
$$ 程序本身的PID号。
2.3 shell环境变量
环境变量,主要是在程序运行时需要设置,环境变量详解如下:
PATH 命令所示路径,以冒号为分割;
HOME 打印用户家目录;
SHELL 显示当前Shell类型;
USER 打印当前用户名;
ID 打印当前用户id信息;
PWD 显示当前所在路径;
TERM 打印当前终端类型;
HOSTNAME 显示当前主机名;
PS1 定义主机命令提示符的;
HISTSIZE 历史命令大小,可通过 HISTTIMEFORMAT 变量设置命令执行时间;
RANDOM 随机生成一个 0 至 32767 的整数;
HOSTNAME 主机名
2.4 shell用户变量
# 用户变量又称为局部变量,主要用在Shell脚本内部或者临时局部使用,系统变量详解如下:
a=rivers 自定义变量A;
Httpd_sort=httpd-2.4.6-97.tar 自定义变量N_SOFT;
BACK_DIR=/data/backup/ 自定义变量BACK_DIR;
IPaddress=10.0.0.1 自定义变量IP1;
2.5 变量的定义
2.6 Shell变量的作用域
Shell 变量的作用域可以分为三种:
- 有的变量只能在函数内部使用,这叫做局部变量(local variable);
- 有的变量可以在当前 Shell 进程中使用,这叫做全局变量(global variable);
- 而有的变量还可以在子进程中使用,这叫做环境变量(environment variable)。
2.7 局部变量
Shell 也支持自定义函数,但是 Shell 函数和 C++、Java、C# 等其他编程语言函数的一个不同点就是:在 Shell 函数中定义的变量默认也是全局变量,它和在函数外部定义变量拥有一样的效果。
#!/bin/bash
#定义函数
function func(){
a=99
}
#调用函数
func
#输出函数内部的变量
echo $a
输出结果:
99
a 是在函数内部定义的,但是在函数外部也可以得到它的值,证明它的作用域是全局的,而不是仅限于函数内部。
要想变量的作用域仅限于函数内部,可以在定义时加上 local
命令,此时该变量就成了局部变量。
#!/bin/bash
#定义函数
function func(){
local a=99
}
#调用函数
func
#输出函数内部的变量
echo $a
输出结果为空,表明变量 a 在函数外部无效,是一个局部变量。
Shell 变量的这个特性和 JavaScript 中的变量是类似的。在 JavaScript 函数内部定义的变量,默认也是全局变量,只有加上var关键字,它才会变成局部变量。
2.8 全局变量
所谓全局变量,就是指变量在当前的整个 Shell 进程中都有效。
每个 Shell 进程都有自己的作用域,彼此之间互不影响。
在 Shell 中定义的变量,默认就是全局变量。
想要实际演示全局变量在不同 Shell 进程中的互不相关性,可在图形界面下同时打开两个 Shell,或使用两个终端远程连接到服务器(SSH)。
首先打开一个 Shell 窗口,定义一个变量 a 并赋值为 99,然后打印,这时在同一个 Shell 窗口中是可正确打印变量 a 的值的。然后再打开一个新的 Shell 窗口,同样打印变量 a 的值,但结果却为空,如下图所示。
这说明全局变量 a 仅仅在定义它的第一个 Shell 进程中有效,对新的 Shell 进程没有影响。
需要强调的是,全局变量的作用范围是当前的 Shell 进程,而不是当前的 Shell 脚本文件,它们是不同的概念。打开一个 Shell 窗口就创建了一个 Shell 进程,打开多个 Shell 窗口就创建了多个 Shell 进程,每个 Shell 进程都是独立的,拥有不同的进程 ID。在一个 Shell 进程中可以使用 source 命令执行多个 Shell 脚本文件,此时全局变量在这些脚本文件中都有效。
例如:
a.sh
#!/bin/bash
echo $a
b=200
b.sh
#!/bin/bash
echo $b
这三条命令都是在一个进程中执行的,从输出结果可以发现,在 Shell 窗口中以命令行的形式定义的变量 a,在 a.sh 中有效;在 a.sh 中定义的变量 b,在 b.sh 中也有效,变量 b 的作用范围已经超越了 a.sh。
注意,必须在当前进程中运行 Shell 脚本,不能在新进程中运行 Shell 脚本。
2.9 环境变量
全局变量只在当前 Shell 进程中有效,对其它 Shell 进程和子进程都无效。如果使用export命令将全局变量导出,那么它就在所有的子进程中也有效了,这称为“环境变量”。
环境变量被创建时所处的 Shell 进程称为父进程,如果在父进程中再创建一个新的进程来执行 Shell 命令,那么这个新的进程被称作 Shell 子进程。当 Shell 子进程产生时,它会继承父进程的环境变量为自己所用,所以说环境变量可从父进程传给子进程。不难理解,环境变量还可以传递给孙进程。
注意,两个没有父子关系的 Shell 进程是不能传递环境变量的,并且环境变量只能向下传递而不能向上传递,即“传子不传父”。
创建 Shell 子进程最简单的方式是运行 bash 命令
通过exit命令可以一层一层地退出 Shell。
演示环境变量的使用:
[redhat@localhost ~]$ a=100 #定义一个全局变量
[redhat@localhost ~]$ echo $a #在当前Shell中输出a,成功
100
[redhat@localhost ~]$ bash #进入Shell子进程
[redhat@localhost ~]$ echo $a #在子进程中输出a,失败
[redhat@localhost ~]$ exit #退出Shell子进程,返回上一级Shell
exit
[redhat@localhost ~]$ export a #将a导出为环境变量
[redhat@localhost ~]$ bash #重新进入Shell子进程
[redhat@localhost ~]$ echo $a #在子进程中再次输出a,成功
100
[redhat@localhost ~]$ exit #退出Shell子进程
exit
[redhat@localhost ~]$ exit #退出父进程,结束整个Shell会话
可以发现,默认情况下,a 在 Shell 子进程中是无效的;使用 export 将 a 导出为环境变量后,在子进程中就可以使用了。
export a这种形式是在定义变量 a 以后再将它导出为环境变量,如果想在定义的同时导出为环境变量,可以写作export a=22。
我们一直强调的是环境变量在 Shell 子进程中有效,并没有说它在所有的 Shell 进程中都有效;如果你通过终端创建了一个新的 Shell 窗口,那它就不是当前 Shell 的子进程,环境变量对这个新的 Shell 进程仍然是无效的。第一个窗口中的环境变量 a 在第二个窗口中就无效。
环境变量也是临时的
通过 export 导出的环境变量只对当前 Shell 进程以及所有的子进程有效,如果最顶层的父进程被关闭了,那么环境变量也就随之消失了,其它的进程也就无法使用了,所以说环境变量也是临时的。
如果我想让一个变量在所有 Shell 进程中都有效,不管它们之间是否存在父子关系,该怎么办呢?
只有将变量写入 Shell 配置文件中才能达到这个目的!Shell 进程每次启动时都会执行配置文件中的代码做一些初始化工作,如果将变量放在配置文件中,那么每次启动进程都会定义这个变量。
2.10 命令替换
Shell 命令替换是指将命令的输出结果赋值给某个变量。比如,在某个目录中输入 ls 命令可查看当前目录中所有的文件,但如何将输出内容存入某个变量中呢?这就需要使用命令替换了,这也是 Shell 编程中使用非常频繁的功能。
Shell 中有两种方式可以完成命令替换,一种是反引号,一种是$(),使用方法如下:
variable=`commands`
variable=$(commands)
其中,variable 是变量名,commands 是要执行的命令。commands 可以只有一个命令,也可以有多个命令,多个命令之间以分号;
分隔。
例如,date 命令用来获得当前的系统时间,使用命令替换可以将它的结果赋值给一个变量。
#!/bin/bash
begin_time=`date` #开始时间,使用``替换
sleep 20s #休眠20秒
finish_time=$(date) #结束时间,使用$()替换
echo "Begin time: $begin_time"
echo "Finish time: $finish_time"
运行脚本,20 秒后可以看到输出结果:
Begin time: 2022年 08月 26日 星期五 09:59:58 CST
Finish time: 2022年 08月 26日 星期五 10:00:18 CST
使用 data 命令的%s
格式控制符可以得到当前的 UNIX 时间戳,这样就可以直接计算脚本的运行时间了。UNIX 时间戳是指从 1970 年 1 月 1 日 00:00:00 到目前为止的秒数。
#!/bin/bash
begin_time=`date +%s` #开始时间,使用``替换
sleep 20s #休眠20秒
finish_time=$(date +%s) #结束时间,使用$()替换
run_time=$((finish_time - begin_time)) #时间差
echo "begin time: $begin_time"
echo "finish time: $finish_time"
echo "run time: ${run_time}s"
运行脚本,20 秒后可以看到输出结果:
begin time: 1555639864
finish time: 1555639884
run time: 20s
第 5 行代码中的(( ))
是 Shell 数学计算命令。和 C++、C#、Java 等编程语言不同,在 Shell 中进行数据计算不那么方便,必须使用专门的数学计算命令,(( ))
就是其中之一。
注意,如果被替换的命令的输出内容包括多行(也即有换行符),或者含有多个连续的空白符,那么在输出变量时应该将变量用双引号包围,否则系统会使用默认的空白符来填充,这会导致换行无效,以及连续的空白符被压缩成一个。
#!/bin/bash
LSL=`ls -l`
echo $LSL #不使用双引号包围
echo "--------------------------" #输出分隔符
echo "$LSL" #使用引号包围
运行结果:
total 8 drwxr-xr-x. 2 root root 21 7月 1 2016 abc -rw-rw-r--. 1 mozhiyan mozhiyan 147 10月 31 10:29 demo.sh -rw-rw-r--. 1 mozhiyan mozhiyan 35 10月 31 10:20 demo.sh~
--------------------------
total 8
drwxr-xr-x. 2 root root 21 7月 1 2016 abc
-rw-rw-r--. 1 mozhiyan mozhiyan 147 10月 31 10:29 demo.sh
-rw-rw-r--. 1 mozhiyan mozhiyan 35 10月 31 10:20 demo.sh
所以,为了防止出现格式混乱的情况,我建议在输出变量时加上双引号。
再谈反引号和 $ ()
原则上讲,上面提到的两种变量替换的形式是等价的,可以随意使用;但是,反引号毕竟看起来像单引号,有时候会对查看代码造成困扰,而使用 $ () 就相对清晰,能有效避免这种混乱。而且有些情况必须使用 $ (), $ () 支持嵌套,反引号不行。
下面的例子演示了使用计算 ls 命令列出的第一个文件的行数,这里使用了两层嵌套。
[c.biancheng.net]$ Fir_File_Lines=$(wc -l $(ls | sed -n '1p'))
[c.biancheng.net]$ echo "$Fir_File_Lines"
36 anaconda-ks.cfg
要注意的是,$ () 仅在 Bash Shell 中有效,而反引号可在多种 Shell 中使用。所以这两种命令替换的方式各有特点,究竟选用哪种方式全看个人需求。
2.11 位置参数
运行Shell脚本文件时我们可以给它传递一些参数,这些参数在脚本文件内部可以使用$n的形式来接收,例如,$1 表示第一个参数,$2 表示第二个参数,依次类推。
同样,在调用函数时也可以传递参数。Shell 函数参数的传递和其它编程语言不同,没有所谓的形参和实参,在定义函数时也不用指明参数的名字和数目。换句话说,定义 Shell 函数时不能带参数,但是在调用函数时却可以传递参数,这些传递进来的参数,在函数内部就也使用$n的形式接收,例如,$1 表示第一个参数,$2 表示第二个参数,依次类推。
这种通过$n的形式来接收的参数,在 Shell 中称为位置参数。
在讲解变量的命名时,我们提到:变量的名字必须以字母或者下划线开头,不能以数字开头;但是位置参数却偏偏是数字,这和变量的命名规则是相悖的,所以我们将它们视为“特殊变量”。
- 给脚本文件传递位置参数
test003.sh
#!/bin/bash
echo "Language: $1"
echo "Url: $2"
运行 test.sh,并附带参数:
- 给函数传递位置参数
#!/bin/bash
#定义函数
function func(){
echo "Language: $1"
echo "URL: $2"
}
#调用函数
func C++ http://c.biancheng.net/cplus/
运行:
[mozhiyan@localhost demo]$ . ./test.sh
Language: C++
URL: http://c.biancheng.net/cplus/
注意事项
如果参数个数太多,达到或者超过了 10 个,那么就得用${n}的形式来接收了,例如
10
、
{10}、
10、{23}。{ }的作用是为了帮助解释器识别参数的边界,这跟使用变量时加{ }是一样的效果。
2.12 特殊变量
Shell特殊变量及其含义
- 给脚本文件传递参数
#!/bin/bash
echo "Process ID: $$"
echo "File Name: $0"
echo "First Parameter : $1"
echo "Second Parameter : $2"
echo "All parameters 1: $@"
echo "All parameters 2: $*"
echo "Total: $#"
运行,并附带参数:
[mozhiyan@localhost demo]$ . ./test.sh Shell Linux
Process ID: 5943
File Name: bash
First Parameter : Shell
Second Parameter : Linux
All parameters 1: Shell Linux
All parameters 2: Shell Linux
Total: 2
- 给函数传递参数
#!/bin/bash
#定义函数
function func(){
echo "Language: $1"
echo "URL: $2"
echo "First Parameter : $1"
echo "Second Parameter : $2"
echo "All parameters 1: $@"
echo "All parameters 2: $*"
echo "Total: $#"
}
#调用函数
func Java http://c.biancheng.net/java/
运行结果:
Language: Java
URL: http://c.biancheng.net/java/
First Parameter : Java
Second Parameter : http://c.biancheng.net/java/
All parameters 1: Java http://c.biancheng.net/java/
All parameters 2: Java http://c.biancheng.net/java/
Total: 2
三、shell流程控制语句
3.1 if条件语句
3.1.1 if分支
在这里插入代码片
3.1.2 if常见判断运算符
3.1.3 判断crond进程是否在运行
echo 命令
echo是一个Shell内建命令,用来在终端输出字符串,并在最后加默认加上换行符。
【实例】
#!/bin/bash
name="Shell教程"
url="http://c.biancheng.net/shell/"
echo "读者,你好!" #直接输出字符串
echo $url #输出变量
echo "${name}的网址是:${url}" #双引号包围的字符串中可以解析变量
echo '${name}的网址是:${url}' #单引号包围的字符串中不能解析变量
运行结果:
读者,你好!
http://c.biancheng.net/shell/
Shell教程的网址是:http://c.biancheng.net/shell/
${name}的网址是:${url}
不换行
echo命令输出结束后默认会换行,如果不希望换行,可以加上-n
参数,如下所示:
#!/bin/bash
name="Tom"
age=20
height=175
weight=62
echo -n "${name} is ${age} years old, "
echo -n "${height}cm in height "
echo "and ${weight}kg in weight."
echo "Thank you!"
运行结果:
Tom is 20 years old, 175cm in height and 62kg in weight.
Thank you!
输出转义字符
默认情况下,echo 不会解析以反斜杠\开头的转义字符。比如,\n
表示换行,echo 默认会将它作为普通字符对待。
[root@localhost ~]# echo "hello \nworld"
hello \nworld
可以添加-e
参数来让 echo 命令解析转义字符。
[root@localhost ~]# echo -e "hello \nworld"
hello
world
\c
转义字符
有了-e
参数,我们也可以使用转义字符-c
来强制echo命令不换行了。
#!/bin/bash
name="Tom"
age=20
height=175
weight=62
echo -e "${name} is ${age} years old, \c"
echo -e "${height}cm in height \c"
echo "and ${weight}kg in weight."
echo "Thank you!"
运行结果:
Tom is 20 years old, 175cm in height and 62kg in weight.
Thank you!
read命令
read是Shell的内置命令,用来从标准输入中读取数据并赋值给变量。如果没有进行重定向,默认就是从键盘读取用户输入的数据;如果进行了重定向,那么可以从文件中读取数据。
read命令的用法为:
read [-options] [variables]
options
表示选项,如下表所示;variables
表示用来存储数据的变量,可以有一个或多个,如果输入多个数据,则第一个数据给第一个变量,第二个数据给第二个变量,如果输入数据个数过多,则最后所有的值都给第一个变量。如果太少输入不会结束。
options
和variables
都是可选的,如果没有提供变量名,那么读取的数据将存放到环境变量REPLY中。
【实例1】使用read命令给多个变量赋值
#!/bin/bash
read -p "Enter some information > " name url age
echo "网站名字:$name"
echo "网址:$url"
echo "年龄:$age"
运行结果:
Enter some information > C语言中文网 http://c.biancheng.net 7
网站名字:C语言中文网
网址:http://c.biancheng.net
年龄:7
注意,必须在一行内输入所有的值,不能换行,否则只能给第一个变量赋值,后续变量都会赋值失败。
本例还使用了-p
选项,该选项会用一段文本来提示用户输入。
【实例2】只读取一个字符
#!/bin/bash
read -n 1 -p "Enter a char > " char
printf "\n" #换行
echo $char
运行结果:
Enter a char > 1
1
-n 1
表示只读取一个字符。运行脚本后,只要用户输入一个字符,立即读取结束,不用等待用户按下回车键。
printf "\n"
语句用来达到换行的效果,否则 echo 的输出结果会和用户输入的内容位于同一行,不容易区分。
【实例3】在指定时间内输入密码
#!/bin/bash
if
read -t 20 -sp "Enter password in 20 seconds(once) > " pass1 && printf "\n" && #第一次输入密码
read -t 20 -sp "Enter password in 20 seconds(again)> " pass2 && printf "\n" && #第二次输入密码
[ $pass1 == $pass2 ] #判断两次输入的密码是否相等
then
echo "Valid password"
else
echo "Invalid password"
fi
这段代码中,我们使用&&
组合了多个命令,这些命令会依次执行,并且从整体上作为if
语句的判断条件,只要其中一个命令执行失败(退出状态为非 0 值),整个判断条件就失败了,后续的命令也就没有必要执行了。
如果两次输入密码相同,运行结果为:
Enter password in 20 seconds(once) >
Enter password in 20 seconds(again)>
Valid password
如果两次输入密码不同,运行结果为:
Enter password in 20 seconds(once) >
Enter password in 20 seconds(again)>
Invalid password
如果第一次输入超时,运行结果为:
Enter password in 20 seconds(once) > Invalid password
如果第二次输入超时,运行结果为:
Enter password in 20 seconds(once) >
Enter password in 20 seconds(again)> Invalid password
【实例4】读文件
每次调用read命令都会读取文件中的"一行"文本。当文件没有可读的行时,read命令将以非零状态退出。
读取文件的关键是如何将文本中的数据传送给read命令。
最常用的方法是对文件使用cat命令并通过管道将结果直接传送给包含read命令的while命令
alias
alias用来给命令创建一个别名。
若直接输入alias
且不带任何参数,则会列出当前进程中使用了哪些别名。
例如ll
和ls -l
的效果一样。
[redhat@localhost shell]$ alias
alias egrep='egrep --color=auto'
alias fgrep='fgrep --color=auto'
alias grep='grep --color=auto'
alias l.='ls -d .* --color=auto'
alias ll='ls -l --color=auto'
alias ls='ls --color=auto'
alias vi='vim'
alias which='(alias; declare -f) | /usr/bin/which --tty-only --read-alias --read-functions --show-tilde --show-dot'
alias xzegrep='xzegrep --color=auto'
alias xzfgrep='xzfgrep --color=auto'
alias xzgrep='xzgrep --color=auto'
alias zegrep='zegrep --color=auto'
alias zfgrep='zfgrep --color=auto'
alias zgrep='zgrep --color=auto'
为了让我们使用方便,Shell 会给某些命令默认创建别名。