正文共: 10051 字 7 图 预计阅读时间: 26 分钟
Be kind; everyone you meet is fighting a hard battle.
友善点,你遇到的每个人都在进行一场艰苦的战斗。
小闫语录:
生活不易,人人都在挣扎,没人会为你的任性与脾气买单。对人友善一点,在带给他人温暖的时候,自己也会面向阳光。
shell是什么?在计算机科学中,shell就是一个命令解释器。它位于系统与应用之间,将应用程序的命令解释给操作系统,好像翻译一样。当然翻译不能是单向的,它也会将操作系统指令处理后解释给应用程序,这才是一个好翻译嘛~
说起shell大家首先想到的肯定是"黑窗口",其实命令行式shell只是shell的一种。shell分为图形界面shell和命令行式shell。图形界面shell就是 GUI shell
,而命令行式shell在Windows中为 cmd.exe
命令提示字符、在Linux中则是 bash
等等。
以后如果不指定,默认shell为命令行式shell,在工作中,最常用的便是Linux系统下的bash。
首先我们需要了解当前系统的shell类型,我们可以在终端中通过输入下面的命令进行查看:
echo $SHELL
返回结果为:
/bin/bash
如果想查看当前系统环境支持的shell,可以通过下面的命令查看:
cat /etc/shells
返回结果为:
# /etc/shells: valid login shells
/bin/sh
/bin/bash
/bin/rbash
/bin/dash
每个人返回的结果是不同的,小编使用的系统是deepin。
我们一般是将shell的命令直接输入到终端中,一边输入,一边获取结果。毋庸置疑,手工的方式效率很低,适合简单的指令或者完成我们简单的需求。大家一定听过shell脚本,那么什么是shell脚本呢?其实就是一个特殊的文件,这个文件中包含了shell命令,我们执行shell脚本即可执行文件中所有的shell命令。
那么shell脚本长什么样子呢?它通常以 .sh
结尾,在执行的时候通过 bash
执行。执行脚本的命令如下:
/bin/bash hello.sh
/bin/bash
为解释器,hello.sh
为脚本。
我们创建shell脚本最常用的编辑器就是vi和vim,这也是程序员最喜欢的工具。上面我们讲了文件后缀是 .sh
,那么内容都有些什么呢?
首先,我们需要在首行注明脚本解释器(必须):
#!/bin/bash
然后我们就可以写各种的shell命令了,当然良好的代码书写规范少不了注释,shell中也有注释,同编程语言,它有单行注释和多行注释。
#
开头,后面为注释内容。# 我是单行注释
注意:首行指定脚本解释器的时候,不是注释。
:<<!
和 !
之间。:<<!
这是多行注释
这个也是注释
这个还是注释
!
感叹号可以换成
...
我们上面介绍了一种执行方式,如下:
/bin/bash hello.sh
# 可简化为下面的命令
bash hello.sh
上面的命令也是最常用的。但千万不要认为它只有这一种方式,它还有下面两种:
1.利用路径指定文件:(可以使用绝对路径或者相对路径)
./hello.sh
用此方法执行脚本,前提是你有此文件的可执行权限。
2.利用 source
命令:
source hello.sh
或者
. hello.sh # 注意`.`和文件名之间有空格
使用此命令执行时需要注意:shell脚本内容环境和当前用户环境一致。
三种方式执行脚本,除了上面的适用场景不同,还有什么区别呢?有一个值得注意的区别: source
或者 .
点号执行方式不会开启子进程,能共享当前终端定义的变量,其他执行方式会开启子进程(比如bash)。
其中变量的问题,下面会讲到。
.sh
。bash脚本名
。成对的标点要一次写出来,防止丢掉出错(如括号等)。
变量定义的语法为:
变量名=变量值
变量的分类:本地变量/全局变量/shell内置变量。
注意在定义的时候
=
左右两边是没有空格的。
本地变量就是在当前系统的某个环境下才能生效的变量,作用范围小。本地变量再细分为普通变量和命令变量。
环境:每打开一个终端,就是一个环境,使用非source执行方式时,会开启子进程,也是一个shell环境,称为子shell环境。
方式一:
变量名=变量值
此种方式定义的变量值必须是一个整体,中间没有特殊的符号。
方式二:
变量名='变量值'
不会解析变量值的内容。
方式三:
变量名="变量值"
如果变量值中有可以解析的变量,那么会先解析这个变量,然后将结果和变量值中其他内容组合成一个整体。 数字不加引号。
方式一:
变量名=`命令`
方式二:
变量名=$(命令)
先执行命令,然后将命令的结果赋值给变量名。
全局变量就是在当前的所有环境下都能生效的变量。
方式一:
变量名=值
export 变量
方式二:(这种方式也是最常用的)
export 变量名=值
env
只显示全局变量,主要是加载了
~/.bashrc
和/etc/profile
文件
在终端定义全局变量时,只对当前shell环境以及子shell环境有效,对新开的终端无效,当前终端关闭后,它定义的全局变量消失。
你一定有疑问,不是说全局变量是针对所有的环境吗?你个骗子.....
如果想让自己定义的全局变量同系统的全局变量一样,在所有的环境中都有效,可以在 ~/.bashrc
或者 /etc/profile
文件中进行定义。
修改 ~/.bashrc
后,我们可以直接打开新的终端见证奇迹的时刻。(此方法只对当前用户有效)
修改 /etc/profile
后,需要重启操作系统,全局变量才生效。(此方法对所有的用户有效)
修改完后,你会发现一个问题,就是当前终端为什么不能看到全局变量,因为你需要加载一下,让当前环境中有你定义的全局变量。(修改了哪个文件就执行下面对应的命令)
source ~/.bashrc
source /etc/profile
我们可以定义变量,当然也可以使用那些内置的变量。
符号 | 意义 |
---|---|
$0 | 获取当前执行的shell脚本文件名 |
$$ | 获取执行shell脚本的进程号 |
$n | 获取当前执行的shell脚本的第n个参数值,n=1..9,当n为0时表示脚本的文件名,如果n大于9就要用大括号括起来${10} |
$# | 获取当前shell命令行中参数的总个数 |
$? | 获取执行上一个指令的返回值(0为成功,非0为失败) |
在脚本文件中使用:
#!/bin/bash
# 获取脚本的名称
echo "我脚本的名称是:$0"
echo "我脚本执行的进程号的是:$$"
# 获取当前脚本传入的参数数量
echo "当前脚本传入的参数数量是: $#"
# 获取指定位置的参数
echo "第一个位置的参数是: $1"
echo "第二个位置的参数是: $2"
echo "第三个位置的参数是: $3"
echo "第四个位置的参数是: $4"
执行脚本:
bash test_shell.sh 1 2 3 name
执行结果:
我脚本的名称是:test_shell.sh
我脚本执行的进程号的是:19755
当前脚本传入的参数数量是: 4
第一个位置的参数是: 1
第二个位置的参数是: 2
第三个位置的参数是: 3
第四个位置的参数是: name
$?
可以返回执行脚本或者执行命令的状态值。那么怎么使用呢?我们先执行所需的脚本或命令,然后输入下面命令进行查看:(根据返回的值判断,0为成功,其他值为失败)
echo $?
我们可以对变量值进行截取。格式为:
${变量名:起始位置:截取长度}
首先我们先定义一个变量:
a="testhello"
然后输入命令进行截取:
echo ${a:1:5}
场景一:
如果变量有内容,那么返回变量值,否则返回默认值,下面举个例子:
格式:
${变量名:-默认值}
我们在一个脚本中输入下面的内容:
#!/bin/bash
a="$1"
echo "您选择的套餐为: 套餐 ${a:-1}"
我们在终端中执行下面的命令分别得到结果如下:
bash hello.sh
您选择的套餐为: 套餐 1
bash hello.sh 2
您选择的套餐为: 套餐 2
没有输入参数,变量a获取不到值,那么默认为套餐1。第2次输入了参数2,变量a获取到参数2,那么输出套餐2。
场景二:
无论变量是否有内容,都输出默认值。
格式:
${变量名+默认值}
我们在一个脚本中定义下面的内容:
#!/bin/bash
a={$1}
echo "小仙女永远 ${a+18} 岁"
我们执行下面的命令,分别得到下面的结果:
bash hello.sh
小仙女永远 18 岁
bash hello.sh 22
小仙女永远 18 岁
结果就是小仙女永远18岁,无论你指定多少。哈哈哈,也希望看这篇文章的小仙女们永远18岁。
查看变量:
name为变量名。
方式一
$name
方式二
"$name"
方式三
${name}
方式四
"${name}"
取消变量:
unset 变量名
注意空格。
前面我们介绍了 echo $?
可以判断命令执行状态,0为成功,其他值失败。如果我们有特殊的需求,需要判断条件了,那么怎么测试呢?有两种测试语句:
注意:上面[]中条件表达式两侧有空格,否则会报错。 两个语句都是:测试条件表达式成立返回状态值是0,不成立返回1。
逻辑表达式,大家首先想到的一定是「 与 」「 或 」「 非 」。这里没有那么多,常见的逻辑表达式只有两个: &&
和 ||
&&
命令1 && 命令2
如果命令1执行成功,那么执行命令2
如果命令1执行失败,那么不执行命令2
||
命令1 || 命令2
如果命令1执行成功,那么不执行命令2
如果命令1执行失败,那么执行命令2
示例:
[ 1 = 1 ] && echo "yes"
yes
[ 1 = 2 ] && echo "yes"
--------------------------
[ 1 = 2 ] || echo "yes"
yes
[ 1 = 1 ] || echo "yes"
-f
判断输入内容是否是一个文件
[ -f hello.sh ] && echo "是一个文件"
是一个文件
[ -f hello.shl ] || echo "不是一个文件"
不是一个文件
-d
判断输入内容是否是一个目录
[ -d hello.sh ] || echo "不是一个目录"
不是一个目录
[ -d mi/ ] && echo "是一个目录"
是一个目录
-x
判断输入内容是否可执行
[ -x hello.sh ] || echo "文件没有可执行的权限"
文件没有可执行的权限
chmod +x hello.sh
[ -x hello.sh ] && echo "文件有可执行的权限"
文件有可执行的权限
-eq
相等
-gt
大于
-lt
小于
-ne
不等于
==
相等,可以判断字符串
!=
不相等,可以判断字符串
计算表达式,就是我们需要对具体的内容进行算数计算。
格式:
$(( 计算表达式 ))
# 或者:
let 计算表达式
注意:$(())中只能用+-*/和()运算符,并且只能做整数运算
示例:
ethanyan@ethanyan-PC:~$ n=100
ethanyan@ethanyan-PC:~$ echo $(($n/10))
10
ethanyan@ethanyan-PC:~$ echo $((n/10))
10
ethanyan@ethanyan-PC:~$ let i=n+1
ethanyan@ethanyan-PC:~$ echo i
i
ethanyan@ethanyan-PC:~$ echo $i
101
大家要仔细对比区别,此处留一个悬念,不做总结。
重定向大家一定并不陌生,简单做一个回顾即可。shell脚本中常用的两种重定向符号是 >
和 >>
,它们代表的都是将左侧的内容或输出结果,输入到右侧的文件。不同的是 >
是覆盖的形式执行, >>
是追加的形式进行。
示例:
cat hello.txt # 查看文件内容,开始为hello ~
hello ~
------------------
echo "nihao ~" > hello.txt
------------------
cat hello.txt # 再次查看文件内容,发生了覆盖,变为了nihao ~
nihao ~
------------------
echo "ethanyan" >> hello.txt
------------------
cat hello.txt # 查看文件内容,是追加,原先内容还在
nihao ~
ethanyan
|
:这就是管道符,传递信息时使用。
格式:
命令1 | 命令2
管道符左侧命令1执行后的结果传递给右侧的命令2使用。
示例:
ethanyan@ethanyan-PC:~$ env | grep SHELL
SHELL=/bin/bash
env查看当前系统中所有的全局变量,grep过滤出SHELL。
&
后台展示符号
我们在执行命令的时候,只需要在尾部加 &
,即可将从命令从前台转向后台执行。
格式:
命令 &
匹配文本内容,如果单独使用的话,格式如下:
grep [参数] [关键字] <文件名>
-c
:只输出匹配行的计数。-n
:显示匹配行及行号。-v
:显示不包含匹配文本的所有行。
如果配合管道符使用,后面的文件名是不用写的。
小技巧:精确定位错误代码。
-r
代表的是递归的执行命令。
grep -nr [错误关键字]* <文件名>
sed以「行」为单位编辑文件,称为行文件编辑工具。
格式:
sed [参数] '<匹配条件>[动作]' [文件名]
参数:
-i
:表示对文件进行编辑(如果不添加这个参数,修改结果在终端输出,但是不会修改原文件)
匹配条件:数字行号或者关键字匹配
关键字匹配格式:
'/关键字/'
注意:关键字两边的隔离符号 / 可以更换成 @、#、!等符号,如果关键字和隔离符号有冲突,就更换成其他的符号即可。
动作:
-a
:在匹配到的内容下一行增加内容
-i
:在匹配到的内容上一行增加内容
-d
:删除匹配到的内容
-s
:替换匹配到的内容
注意:上面的动作应该在参数为-i的时候使用,不然的话不会修改原文件。
替换操作:
命令格式: sed-i[替换格式][文件名]
替换格式: 's#原内容#替换后内容#'
示例:
准备一个测试文件 sed.txt
,内容如下:
nihao sed sed sed
nihao sed sed sed
nihao sed sed sed
替换每行第一个匹配到的内容sed为SED:
ethanyan@ethanyan-PC:~$ sed -i 's#sed#SED#' sed.txt
ethanyan@ethanyan-PC:~$ cat sed.txt
nihao SED sed sed
nihao SED sed sed
nihao SED sed sed
替换匹配到的全部内容sed为SED:
ethanyan@ethanyan-PC:~$ sed -i 's#sed#SED#g' sed.txt
ethanyan@ethanyan-PC:~$ cat sed.txt
nihao SED SED SED
nihao SED SED SED
nihao SED SED SED
替换第2行中第一个匹配到的内容SED为sed:
ethanyan@ethanyan-PC:~$ sed -i '2s#SED#sed#' sed.txt
ethanyan@ethanyan-PC:~$ cat sed.txt
nihao SED SED SED
nihao sed SED SED
nihao SED SED SED
替换每行的匹配到的SED第2列为sed:
ethanyan@ethanyan-PC:~$ sed -i 's#SED#sed#2' sed.txt
ethanyan@ethanyan-PC:~$ cat sed.txt
nihao SED sed SED
nihao sed SED sed
nihao SED sed SED
替换第3行匹配到的SED中第2列为sed:
ethanyan@ethanyan-PC:~$ sed -i '3s#SED#sed#2' sed.txt
ethanyan@ethanyan-PC:~$ cat sed.txt
nihao SED sed SED
nihao sed SED sed
nihao SED sed sed
追加操作:
作用:在指定行号的下一行增加内容。
格式: sed-i'行号a\增加的内容'文件名
注意:如果增加多行,可以在行号位置写个范围值,彼此间使用逗号隔开:
sed -i '1,3a\增加内容' 文件名
示例:
在第2行下一行增加内容 add-first
:
ethanyan@ethanyan-PC:~$ sed -i '2a\add-first' sed.txt
ethanyan@ethanyan-PC:~$ cat sed.txt
nihao SED sed SED
nihao sed SED sed
add-first
nihao SED sed sed
在第1到3行,每行的下一行都增加内容 add-second
:
ethanyan@ethanyan-PC:~$ sed -i '1,3a\add-second' sed.txt
ethanyan@ethanyan-PC:~$ cat sed.txt
nihao SED sed SED
add-second
nihao sed SED sed
add-second
add-first
add-second
nihao SED sed sed
插入操作:
作用:在指定行号的当行插入内容。
格式: sed-i'行号i\插入的内容'文件名
注意:如果插入多行,可以在行号位置写个范围值,彼此间使用逗号隔开。
示例:
在第一行插入内容 insert-first
:
ethanyan@ethanyan-PC:~$ sed -i '1i\insert-first' sed.txt
ethanyan@ethanyan-PC:~$ cat sed.txt
insert-first
nihao SED sed SED
add-second
nihao sed SED sed
add-second
add-first
add-second
nihao SED sed sed
删除操作:
作用:指定行号删除。
格式: sed-i'行号d'文件名
注意:如果删除多行,可以在行号位置写个范围值,彼此间使用逗号隔开。
示例:
删除第4行内容:
ethanyan@ethanyan-PC:~$ sed -i '4d' sed.txt
ethanyan@ethanyan-PC:~$ cat sed.txt
insert-first
nihao SED sed SED
add-second
add-second
add-first
add-second
nihao SED sed sed
删除3到5行内容:
ethanyan@ethanyan-PC:~$ sed -i '3,5d' sed.txt
ethanyan@ethanyan-PC:~$ cat sed.txt
insert-first
nihao SED sed SED
add-second
nihao SED sed sed
awk是一个更加强大的文档编辑工具,它不仅能以行为单位,还能以列为单位处理文件。
命令格式:
awk [参数] '[动作]' [文件名]
常见参数:
-F
:制定行的分割符。
常见动作:
print
:显示内容。
$0
:显示当前行所有内容。
$n
:显示当前行的第n列内容,如果存在多个 $n
,它们之间使用逗号隔开。
常见内置变量:
FILENAME
:当前输入文件的文件名,该变量是只读的。
NR
:指定显示行的行号。
NF
:输出最后一列的内容。
OFS
:输出格式的列分隔符,缺省是空格。
FS
:输入文件的列分隔符,缺省是连续的空格和Tab 模板文件内容。
示例:
先创建一个测试文件 awk.txt
,内容如下:
nihao awk awk awk
nihao awk awk awk
打印第1列的内容:
ethanyan@ethanyan-PC:~$ awk '{print $1}' awk.txt
nihao
nihao
打印第1行第1列和第3列内容:
ethanyan@ethanyan-PC:~$ awk 'NR==1 {print $1,$3}' awk.txt
nihao awk
指定分割符查看内容:
ethanyan@ethanyan-PC:~$ echo 'root:x:0:0:root:/root:/bin/bash' > awk-second.txt
ethanyan@ethanyan-PC:~$ cat awk-second.txt
root:x:0:0:root:/root:/bin/bash
ethanyan@ethanyan-PC:~$ awk -F ':' '{print $1,$7}' awk-second.txt
root /bin/bash
设置显示分割符,显示内容:
ethanyan@ethanyan-PC:~$ awk 'BEGIN{OFS=":"} {print NR,$0}' awk.txt
1:nihao awk awk awk
2:nihao awk awk awk
find
不同与 grep
,它是匹配文件名的。
格式:
find [路径] [参数] [关键字]
参数:
-name
:按照文件名查找文件。
-perm
:按照文件权限来查找文件。
-user
:按照文件属主来查找文件。
-group
:按照文件所属的组来查找文件。
-type
:查找某一类型的文件,文件类型诸如:
-size n[c]
:查找文件长度为n块的文件,带有c时表示文件长度以字节计。
-depth
:在查找文件时,首先查找当前目录中的文件,然后再在其子目录中查找。
-mindepth n
:在查找文件时,查找当前目录中的第n层目录的文件,然后再在其子目录中查找。
!
: 表示取反 命令
全部信息符号为 2>&1
1:标准输出信息,将执行正确的信息保存到一个文件中。 2:标准错误的信息,将执行报错的信息保存到一个文件中。
2>&1
:综合了上面的1和2,会将标准输出和标准错误的信息都输入到一个文件中。
我们先来看一下标准正确输出实例:
cat nihao.txt 1>> ok
这条命令会生成一个 ok
文件,用来接收前面命令执行正确的结果。
我们再来看一下标准错误输出实例:
ethan 2>> error
这条命令同样会生成一个 error
文件,用来接收错误的结果。我们来查看一下目录:
ethanyan@ethanyan-PC:~$ ls
error nihao.txt ok
我们再来查看一下 erro
文件的内容:
ethanyan@ethanyan-PC:~$ cat error
bash: ethan: 未找到命令
文件保存了出错的信息。
这两条命令其实还可以一起使用:
bash test.sh 1>> test-ok 2>> test-err
最后会将正确输出的内容,输入到
test-ok
文件中;将报错信息输入到test-err
文件中。
最后我们来看一下全部信息符号怎么用:
首先创建一个脚本,内容如下:
#!/bin/bash
echo '下一条错误命令'
dsfsafsafdsa
下面执行一下脚本:
ethanyan@ethanyan-PC:~$ bash test.sh
下一条错误命令
test.sh:行3: dsfsafsafdsa: 未找到命令
可以看到脚本中第2行正确执行。第3行报错,提示未找到命令。
我们使用全部信息:
bash test.sh >> test-all 2>&1
然后我们查看文件 test-all
:
ethanyan@ethanyan-PC:~$ cat test-all
下一条错误命令
test.sh:行3: dsfsafsafdsa: 未找到命令
在我们的linux系统中,有一个设备文件,他叫做 /dev/null
。为什么说它神奇呢?因为它好似一个无敌洞,你可以向里面随意扔东西,而且填不满。如果你还是不知道它干什么用,那我还是说一下吧。
你可以将它作为垃圾桶,不重要的信息随意重定向至这个文件,它的特点就是无限大。
优质文章推荐: