shell(一)

简介: shell(一)

每日分享

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类型,我们可以在终端中通过输入下面的命令进行查看:

  1. echo $SHELL

返回结果为:

  1. /bin/bash

如果想查看当前系统环境支持的shell,可以通过下面的命令查看:

  1. 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执行。执行脚本的命令如下:

  1. /bin/bash hello.sh

/bin/bash为解释器, hello.sh为脚本。

2.1创建shell脚本

我们创建shell脚本最常用的编辑器就是vi和vim,这也是程序员最喜欢的工具。上面我们讲了文件后缀是 .sh,那么内容都有些什么呢?

首先,我们需要在首行注明脚本解释器(必须):

  1. #!/bin/bash

然后我们就可以写各种的shell命令了,当然良好的代码书写规范少不了注释,shell中也有注释,同编程语言,它有单行注释和多行注释。

  • 单行注释:以 #开头,后面为注释内容。
  1. # 我是单行注释

注意:首行指定脚本解释器的时候,不是注释。

  • 多行注释:将内容放在 :<<!!之间。
:<<!
这是多行注释
这个也是注释
这个还是注释
!

感叹号可以换成 ...

2.2执行脚本

我们上面介绍了一种执行方式,如下:

/bin/bash hello.sh
# 可简化为下面的命令
bash hello.sh

上面的命令也是最常用的。但千万不要认为它只有这一种方式,它还有下面两种:

1.利用路径指定文件:(可以使用绝对路径或者相对路径)

  1. ./hello.sh

用此方法执行脚本,前提是你有此文件的可执行权限。

2.利用 source命令:

source hello.sh
或者
. hello.sh # 注意`.`和文件名之间有空格

使用此命令执行时需要注意:shell脚本内容环境和当前用户环境一致。

2.2.1区别

三种方式执行脚本,除了上面的适用场景不同,还有什么区别呢?有一个值得注意的区别: source或者 .点号执行方式不会开启子进程,能共享当前终端定义的变量,其他执行方式会开启子进程(比如bash)。

其中变量的问题,下面会讲到。

2.2.2脚本开发规范

  • 脚本命名要做到见名知意,文件后缀为 .sh
  • 脚本首行要注明脚本解释器。
  • 脚本文件中尽量使用英文注释,防止切换系统后出现乱码的情况。
  • 脚本最常使用的执行方式是 bash脚本名
  • 脚本内容的执行顺序是从上到下依次执行。
  • 代码书写要养成良好习惯。

成对的标点要一次写出来,防止丢掉出错(如括号等)。

  • 通过缩进让代码易读,该有空格的地方请写空格。

2.3变量

变量定义的语法为:

  1. 变量名=变量值

变量的分类:本地变量/全局变量/shell内置变量。

注意在定义的时候 =左右两边是没有空格的。

2.3.1本地变量

本地变量就是在当前系统的某个环境下才能生效的变量,作用范围小。本地变量再细分为普通变量和命令变量。

环境:每打开一个终端,就是一个环境,使用非source执行方式时,会开启子进程,也是一个shell环境,称为子shell环境。

2.3.1.1定义普通变量

方式一

  1. 变量名=变量值

此种方式定义的变量值必须是一个整体,中间没有特殊的符号。

方式二

  1. 变量名='变量值'

不会解析变量值的内容。

方式三

  1. 变量名="变量值"

如果变量值中有可以解析的变量,那么会先解析这个变量,然后将结果和变量值中其他内容组合成一个整体。

数字不加引号。

2.3.1.2定义命令变量

方式一

  1. 变量名=`命令`

方式二

  1. 变量名=$(命令)
命令变量的执行流程

先执行命令,然后将命令的结果赋值给变量名。

2.3.2全局变量

全局变量就是在当前的所有环境下都能生效的变量。

2.3.2.1定义全局变量

方式一

  1. 变量名=值
  2. export变量

方式二:(这种方式也是最常用的)

  1. export变量名=值
2.3.2.2查看全局变量
  1. env

只显示全局变量,主要是加载了 ~/.bashrc/etc/profile文件

2.3.2.3总结

在终端定义全局变量时,只对当前shell环境以及子shell环境有效,对新开的终端无效,当前终端关闭后,它定义的全局变量消失。

你一定有疑问,不是说全局变量是针对所有的环境吗?你个骗子.....

如果想让自己定义的全局变量同系统的全局变量一样,在所有的环境中都有效,可以在 ~/.bashrc或者 /etc/profile文件中进行定义。

修改 ~/.bashrc后,我们可以直接打开新的终端见证奇迹的时刻。(此方法只对当前用户有效)

修改 /etc/profile后,需要重启操作系统,全局变量才生效。(此方法对所有的用户有效)

修改完后,你会发现一个问题,就是当前终端为什么不能看到全局变量,因为你需要加载一下,让当前环境中有你定义的全局变量。(修改了哪个文件就执行下面对应的命令)

  1. source ~/.bashrc
  2. 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"

执行脚本:

  1. bash test_shell.sh 123 name

执行结果:

我脚本的名称是:test_shell.sh
我脚本执行的进程号的是:19755
当前脚本传入的参数数量是: 4
第一个位置的参数是: 1
第二个位置的参数是: 2
第三个位置的参数是: 3
第四个位置的参数是: name

$?可以返回执行脚本或者执行命令的状态值。那么怎么使用呢?我们先执行所需的脚本或命令,然后输入下面命令进行查看:(根据返回的值判断,0为成功,其他值为失败)

  1. echo $?
2.3.3.2和字符串相关的变量

我们可以对变量值进行截取。格式为:

  1. ${变量名:起始位置:截取长度}

首先我们先定义一个变量:

  1. a="testhello"

然后输入命令进行截取:

  1. echo ${a:1:5}
2.3.3.3和默认值相关的变量

场景一:

如果变量有内容,那么返回变量值,否则返回默认值,下面举个例子:

格式

  1. ${变量名:-默认值}

我们在一个脚本中输入下面的内容:

#!/bin/bash
a="$1"
echo "您选择的套餐为: 套餐 ${a:-1}"

我们在终端中执行下面的命令分别得到结果如下:

bash hello.sh 
您选择的套餐为: 套餐 1
bash hello.sh 2
您选择的套餐为: 套餐 2

没有输入参数,变量a获取不到值,那么默认为套餐1。第2次输入了参数2,变量a获取到参数2,那么输出套餐2。

场景二:

无论变量是否有内容,都输出默认值。

格式

  1. ${变量名+默认值}

我们在一个脚本中定义下面的内容:

#!/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}"

取消变量:

  1. 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计算表达式

计算表达式,就是我们需要对具体的内容进行算数计算。

格式

  1. $((计算表达式))  
  2. # 或者:
  3. let计算表达式
  1. 注意:$(())中只能用+-*/和()运算符,并且只能做整数运算

示例

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. 命令1|命令2

管道符左侧命令1执行后的结果传递给右侧的命令2使用。

示例

  1. ethanyan@ethanyan-PC:~$ env | grep SHELL
  2. SHELL=/bin/bash

env查看当前系统中所有的全局变量,grep过滤出SHELL。

3.3.3后台展示符号

&后台展示符号

我们在执行命令的时候,只需要在尾部加 &,即可将从命令从前台转向后台执行。

格式

  1. 命令&

3.3.4grep

匹配文本内容,如果单独使用的话,格式如下:

  1. grep [参数][关键字]<文件名>

-c:只输出匹配行的计数。

-n:显示匹配行及行号。

-v:显示不包含匹配文本的所有行。

如果配合管道符使用,后面的文件名是不用写的。

小技巧:精确定位错误代码。

-r代表的是递归的执行命令。

  1. grep -nr [错误关键字]*<文件名>

3.3.5sed

sed以「行」为单位编辑文件,称为行文件编辑工具。

格式

  1. sed [参数]'<匹配条件>[动作]'[文件名]

参数

-i:表示对文件进行编辑(如果不添加这个参数,修改结果在终端输出,但是不会修改原文件)

匹配条件:数字行号或者关键字匹配

关键字匹配格式:

  1. '/关键字/'

注意:关键字两边的隔离符号 / 可以更换成 @、#、!等符号,如果关键字和隔离符号有冲突,就更换成其他的符号即可。

动作

-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\增加的内容'文件名

注意:如果增加多行,可以在行号位置写个范围值,彼此间使用逗号隔开:

  1. 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是一个更加强大的文档编辑工具,它不仅能以行为单位,还能以列为单位处理文件。

命令格式

  1. awk [参数]'[动作]'[文件名]

常见参数

-F:制定行的分割符。

常见动作

print:显示内容。

$0:显示当前行所有内容。

$n:显示当前行的第n列内容,如果存在多个 $n,它们之间使用逗号隔开。

常见内置变量

FILENAME:当前输入文件的文件名,该变量是只读的。

NR:指定显示行的行号。

NF:输出最后一列的内容。

OFS:输出格式的列分隔符,缺省是空格。

FS:输入文件的列分隔符,缺省是连续的空格和Tab 模板文件内容。

示例

先创建一个测试文件 awk.txt,内容如下:

  1. nihao awk awk awk
  2. nihao awk awk awk

打印第1列的内容:

  1. ethanyan@ethanyan-PC:~$ awk '{print $1}' awk.txt
  2. nihao
  3. 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,它是匹配文件名的。

格式

  1. 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,会将标准输出和标准错误的信息都输入到一个文件中。

我们先来看一下标准正确输出实例:

  1. cat nihao.txt 1>> ok

这条命令会生成一个 ok文件,用来接收前面命令执行正确的结果。

我们再来看一下标准错误输出实例:

  1. 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行报错,提示未找到命令。

我们使用全部信息:

  1. 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。为什么说它神奇呢?因为它好似一个无敌洞,你可以向里面随意扔东西,而且填不满。如果你还是不知道它干什么用,那我还是说一下吧。

你可以将它作为垃圾桶,不重要的信息随意重定向至这个文件,它的特点就是无限大。

相关文章
|
6月前
|
Shell Linux
什么是shell?
什么是shell?
120 0
|
29天前
|
Unix Shell Linux
Shell
【10月更文挑战第15天】
17 3
|
11月前
|
Shell 程序员
Shell 替代
Shell 替代
39 0
|
Shell
如何在shell下面实现a+b
如何在shell下面实现a+b
65 0
|
机器学习/深度学习 Shell Linux
shell
shell
84 0
|
Shell 数据库 Windows