每日分享
Be kind; everyone you meet is fighting a hard battle.
友善点,你遇到的每个人都在进行一场艰苦的战斗。
小闫语录:
生活不易,人人都在挣扎,没人会为你的任性与脾气买单。对人友善一点,在带给他人温暖的时候,自己也会面向阳光。
shell(一)
shell是什么?在计算机科学中,shell就是一个命令解释器。它位于系统与应用之间,将应用程序的命令解释给操作系统,好像翻译一样。当然翻译不能是单向的,它也会将操作系统指令处理后解释给应用程序,这才是一个好翻译嘛~
说起shell大家首先想到的肯定是"黑窗口",其实命令行式shell只是shell的一种。shell分为图形界面shell和命令行式shell。图形界面shell就是 GUI shell
,而命令行式shell在Windows中为 cmd.exe
命令提示字符、在Linux中则是 bash
等等。
以后如果不指定,默认shell为命令行式shell,在工作中,最常用的便是Linux系统下的bash。
1.查看shell信息
首先我们需要了解当前系统的shell类型,我们可以在终端中通过输入下面的命令进行查看:
echo $SHELL
返回结果为:
/bin/bash
如果想查看当前系统环境支持的shell,可以通过下面的命令查看:
cat /etc/shells
返回结果为:
# /etc/shells: valid login shells /bin/sh /bin/bash /bin/rbash /bin/dash
每个人返回的结果是不同的,小编使用的系统是deepin。
2.shell基础知识
我们一般是将shell的命令直接输入到终端中,一边输入,一边获取结果。毋庸置疑,手工的方式效率很低,适合简单的指令或者完成我们简单的需求。大家一定听过shell脚本,那么什么是shell脚本呢?其实就是一个特殊的文件,这个文件中包含了shell命令,我们执行shell脚本即可执行文件中所有的shell命令。
那么shell脚本长什么样子呢?它通常以 .sh
结尾,在执行的时候通过 bash
执行。执行脚本的命令如下:
/bin/bash hello.sh
/bin/bash
为解释器,hello.sh
为脚本。
2.1创建shell脚本
我们创建shell脚本最常用的编辑器就是vi和vim,这也是程序员最喜欢的工具。上面我们讲了文件后缀是 .sh
,那么内容都有些什么呢?
首先,我们需要在首行注明脚本解释器(必须):
#!/bin/bash
然后我们就可以写各种的shell命令了,当然良好的代码书写规范少不了注释,shell中也有注释,同编程语言,它有单行注释和多行注释。
- 单行注释:以
#
开头,后面为注释内容。
# 我是单行注释
注意:首行指定脚本解释器的时候,不是注释。
- 多行注释:将内容放在
:<<!
和!
之间。
:<<! 这是多行注释 这个也是注释 这个还是注释 !
感叹号可以换成
...
2.2执行脚本
我们上面介绍了一种执行方式,如下:
/bin/bash hello.sh # 可简化为下面的命令 bash hello.sh
上面的命令也是最常用的。但千万不要认为它只有这一种方式,它还有下面两种:
1.利用路径指定文件:(可以使用绝对路径或者相对路径)
./hello.sh
用此方法执行脚本,前提是你有此文件的可执行权限。
2.利用 source
命令:
source hello.sh 或者 . hello.sh # 注意`.`和文件名之间有空格
使用此命令执行时需要注意:shell脚本内容环境和当前用户环境一致。
2.2.1区别
三种方式执行脚本,除了上面的适用场景不同,还有什么区别呢?有一个值得注意的区别: source
或者 .
点号执行方式不会开启子进程,能共享当前终端定义的变量,其他执行方式会开启子进程(比如bash)。
其中变量的问题,下面会讲到。
2.2.2脚本开发规范
- 脚本命名要做到见名知意,文件后缀为
.sh
。 - 脚本首行要注明脚本解释器。
- 脚本文件中尽量使用英文注释,防止切换系统后出现乱码的情况。
- 脚本最常使用的执行方式是
bash脚本名
。 - 脚本内容的执行顺序是从上到下依次执行。
- 代码书写要养成良好习惯。
成对的标点要一次写出来,防止丢掉出错(如括号等)。
- 通过缩进让代码易读,该有空格的地方请写空格。
2.3变量
变量定义的语法为:
变量名=变量值
变量的分类:本地变量/全局变量/shell内置变量。
注意在定义的时候
=
左右两边是没有空格的。
2.3.1本地变量
本地变量就是在当前系统的某个环境下才能生效的变量,作用范围小。本地变量再细分为普通变量和命令变量。
环境:每打开一个终端,就是一个环境,使用非source执行方式时,会开启子进程,也是一个shell环境,称为子shell环境。
2.3.1.1定义普通变量
方式一:
变量名=变量值
此种方式定义的变量值必须是一个整体,中间没有特殊的符号。
方式二:
变量名='变量值'
不会解析变量值的内容。
方式三:
变量名="变量值"
如果变量值中有可以解析的变量,那么会先解析这个变量,然后将结果和变量值中其他内容组合成一个整体。
数字不加引号。
2.3.1.2定义命令变量
方式一:
变量名=`命令`
方式二:
变量名=$(命令)
命令变量的执行流程
先执行命令,然后将命令的结果赋值给变量名。
2.3.2全局变量
全局变量就是在当前的所有环境下都能生效的变量。
2.3.2.1定义全局变量
方式一:
变量名=值
export变量
方式二:(这种方式也是最常用的)
export变量名=值
2.3.2.2查看全局变量
env
只显示全局变量,主要是加载了
~/.bashrc
和/etc/profile
文件
2.3.2.3总结
在终端定义全局变量时,只对当前shell环境以及子shell环境有效,对新开的终端无效,当前终端关闭后,它定义的全局变量消失。
你一定有疑问,不是说全局变量是针对所有的环境吗?你个骗子.....
如果想让自己定义的全局变量同系统的全局变量一样,在所有的环境中都有效,可以在 ~/.bashrc
或者 /etc/profile
文件中进行定义。
修改
~/.bashrc
后,我们可以直接打开新的终端见证奇迹的时刻。(此方法只对当前用户有效)修改
/etc/profile
后,需要重启操作系统,全局变量才生效。(此方法对所有的用户有效)修改完后,你会发现一个问题,就是当前终端为什么不能看到全局变量,因为你需要加载一下,让当前环境中有你定义的全局变量。(修改了哪个文件就执行下面对应的命令)
source ~/.bashrc
source /etc/profile
2.3.3shell内置变量
我们可以定义变量,当然也可以使用那些内置的变量。
2.3.3.1和脚本文件相关的变量
符号 | 意义 |
$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 123 name
执行结果:
我脚本的名称是:test_shell.sh 我脚本执行的进程号的是:19755 当前脚本传入的参数数量是: 4 第一个位置的参数是: 1 第二个位置的参数是: 2 第三个位置的参数是: 3 第四个位置的参数是: name
$?
可以返回执行脚本或者执行命令的状态值。那么怎么使用呢?我们先执行所需的脚本或命令,然后输入下面命令进行查看:(根据返回的值判断,0为成功,其他值为失败)
echo $?
2.3.3.2和字符串相关的变量
我们可以对变量值进行截取。格式为:
${变量名:起始位置:截取长度}
首先我们先定义一个变量:
a="testhello"
然后输入命令进行截取:
echo ${a:1:5}
2.3.3.3和默认值相关的变量
场景一:
如果变量有内容,那么返回变量值,否则返回默认值,下面举个例子:
格式:
${变量名:-默认值}
我们在一个脚本中输入下面的内容:
#!/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岁。
2.3.4 变量的查看和取消
查看变量:
name为变量名。
方式一 $name 方式二 "$name" 方式三 ${name} 方式四 "${name}"
取消变量:
unset 变量名
注意空格。
3.shell进阶
前面我们介绍了 echo $?
可以判断命令执行状态,0为成功,其他值失败。如果我们有特殊的需求,需要判断条件了,那么怎么测试呢?有两种测试语句:
- test 条件表达式
- [ 条件表达式 ]
注意:上面[]中条件表达式两侧有空格,否则会报错。
两个语句都是:测试条件表达式成立返回状态值是0,不成立返回1。
3.1条件表达式
3.1.1逻辑表达式
逻辑表达式,大家首先想到的一定是「 与 」「 或 」「 非 」。这里没有那么多,常见的逻辑表达式只有两个: &&
和 ||
&& 1. 命令1 && 命令2 2. 如果命令1执行成功,那么执行命令2 3. 如果命令1执行失败,那么不执行命令2 || 1. 命令1 || 命令2 2. 如果命令1执行成功,那么不执行命令2 3. 如果命令1执行失败,那么执行命令2
示例:
[ 1 = 1 ] && echo "yes" yes [ 1 = 2 ] && echo "yes" -------------------------- [ 1 = 2 ] || echo "yes" yes [ 1 = 1 ] || echo "yes"
3.1.2文件表达式
-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 "文件有可执行的权限" 文件有可执行的权限
3.1.3数值比较符号
-eq
相等
-gt
大于
-lt
小于
-ne
不等于
==
相等,可以判断字符串
!=
不相等,可以判断字符串
3.2计算表达式
计算表达式,就是我们需要对具体的内容进行算数计算。
格式:
$((计算表达式))
# 或者:
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
大家要仔细对比区别,此处留一个悬念,不做总结。
3.3linux常见符号
3.3.1重定向
重定向大家一定并不陌生,简单做一个回顾即可。shell脚本中常用的两种重定向符号是 >
和 >>
,它们代表的都是将左侧的内容或输出结果,输入到右侧的文件。不同的是 >
是覆盖的形式执行, >>
是追加的形式进行。
示例:
cat hello.txt # 查看文件内容,开始为hello ~ hello ~ ------------------ echo "nihao ~" > hello.txt ------------------ cat hello.txt # 再次查看文件内容,发生了覆盖,变为了nihao ~ nihao ~ ------------------ echo "ethanyan" >> hello.txt ------------------ cat hello.txt # 查看文件内容,是追加,原先内容还在 nihao ~ ethanyan
3.3.2管道
|
:这就是管道符,传递信息时使用。
格式:
命令1|命令2
管道符左侧命令1执行后的结果传递给右侧的命令2使用。
示例:
ethanyan@ethanyan-PC:~$ env | grep SHELL
SHELL=/bin/bash
env查看当前系统中所有的全局变量,grep过滤出SHELL。
3.3.3后台展示符号
&
后台展示符号
我们在执行命令的时候,只需要在尾部加 &
,即可将从命令从前台转向后台执行。
格式:
命令&
3.3.4grep
匹配文本内容,如果单独使用的话,格式如下:
grep [参数][关键字]<文件名>
-c
:只输出匹配行的计数。
-n
:显示匹配行及行号。
-v
:显示不包含匹配文本的所有行。
如果配合管道符使用,后面的文件名是不用写的。
小技巧:精确定位错误代码。
-r
代表的是递归的执行命令。
grep -nr [错误关键字]*<文件名>
3.3.5sed
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
3.3.6awk
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
3.3.7find
find
不同与 grep
,它是匹配文件名的。
格式:
find [路径][参数][关键字]
参数:
-name
:按照文件名查找文件。
-perm
:按照文件权限来查找文件。
-user
:按照文件属主来查找文件。
-group
:按照文件所属的组来查找文件。
-type
:查找某一类型的文件,文件类型诸如:
- b - 块设备文件
- d - 目录
- c - 字符设备文件
- p - 管道文件
- l - 符号链接文件
- f - 普通文件。
-size n[c]
:查找文件长度为n块的文件,带有c时表示文件长度以字节计。
-depth
:在查找文件时,首先查找当前目录中的文件,然后再在其子目录中查找。
-mindepth n
:在查找文件时,查找当前目录中的第n层目录的文件,然后再在其子目录中查找。
!
: 表示取反 命令
3.3.8全部信息符号
全部信息符号为 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
:
1. ethanyan@ethanyan-PC:~$ cat test-all 2. 下一条错误命令 3. test.sh:行3: dsfsafsafdsa: 未找到命令 3.3.9一个神奇的文件
在我们的linux系统中,有一个设备文件,他叫做 /dev/null
。为什么说它神奇呢?因为它好似一个无敌洞,你可以向里面随意扔东西,而且填不满。如果你还是不知道它干什么用,那我还是说一下吧。
你可以将它作为垃圾桶,不重要的信息随意重定向至这个文件,它的特点就是无限大。