Bash基础

学习Shell脚本第一步!

Linux下使用的Shell标准Shell是bash, 这是BShelll家族的一员, BSD.Unix下的标准SHell是CShell.

BShell(Bourne Shell)主要包括sh.ksh.Bash.psh.zsh, Cshell主要包括csh, tcsh.

Bash的基础功能

命令别名和快捷键

在Shell中使用alias可以看到当前的别名, 有些命令敲起来很长, 很不方便, 所以就建立一个映射.

一个最典型的例子就是: ll = ll -l --color=auto.

临时更改SHell别名可以直接在alias中设置, alias sth.='another th'.

这样的设置在重启机器后就会失效, 如果想要永久设置就在.bashrc下.

编辑后,该文件不会立即生效.这里使用source来使得系统重新载入.

接下来我来说一下Liuux中命令的生效顺序:

1
2
3
4
第一顺位: 执行用绝对路径或相对路径执行的命令
第二顺位: 执行别名
第三顺位: 执行Bash的内部命令
第四顺位: 执行按照$PATH的环境变量定的目录查找顺序找到的第一个命令

下面说一些超常用的快捷键:

Ctrl + c terminate current order.
Ctrl + l clear screen
Ctrl + a move cursor to the head of the line
Ctrl + e move cursor to the end of the line
Ctrl + u delete from the head of the line to the current position of the cursor

另外补充一个: 使用 ALT+. 可以复制上一条命令的最后一个单词, 这里说的单词是指命令的. ( 使用远程连接的话, 需要使用Esc来代替ALT, 或者可以想个办法做mapping.

历史命令

Bash是可以查看历史命令的, 在早期的BShell是没有历史命令功能的.

查看历史使用history命令, 和这个关联的文件时.bash_history, 我们知道使用history加上-c参数可以清空历史命令.

事实上, 历史仍然保存在文件中, -c仅仅是清空Buffer中的命令, 而如果想要彻底删个干净就要在-c之后再加上-w执行一遍.-w的意思是将缓存的历史命令写入到文件中(覆盖).,

在我们每次注销的时候, 系统会自动执行-w.

另外, 历史命令还有这样的使用方法.

1
2
!! 重复执行上一条命令
!字符串 执行最后一条以该字符串开头的命令

在Shell中使用Tab键进行命令补全, 这是一个很重要的功能, 它不仅大大加速我们的命令输入, 而且还有一定的查错能力.

输出重定向

首先要明确的是三个标准输入输出:

1
2
3
4
5
键盘 /dev/stdin     0  标准输入

显示器 /dev/stdout 1 标准输出

/dev/stderr 2 标准错误输出

命令的输出默认当然就是显示屏幕上, 也就是标准输出1.

但是Bash是可以重定向输出流的,像这样

1
ifconfig > test.log

命令输出 > / >> 仅能保存正确的输出, 如果遇到错误的情况还是会输出到屏幕上, 这就是因为标准错误输出和标准输出是两个东西.

参见下表:

类型 符号 作用
标准输出重定向 Command > file 覆盖方式
标准输出重定向 Command >> file 追加方式
标准错误输出重定向 COOMAND 2>file 覆盖方式
标准错误输出重定向 COOMAND 2>>file 追加方式

注意:标准错误输出重定向的时候,>>和文件之间是没有空格的!一旦出现空格,重定向就会失效

但是,仔细想一下2>/2>>这种我们根本就不会使用到, 因为如果已经知道命令是错的, 为什么我还要执行呢?

所以下表更有参考价值:

类型 符号 作用
标准错误输出和正确输出重定向 命令 > 文件 2>&1 覆盖方式
标准错误输出和正确输出重定向 命令 >>文件 2>&1 追加方式
标准错误输出和正确输出重定向 命令 &>文件 覆盖方式
标准错误输出和正确输出重定向 命令 &>>文件 追加方式
标准错误输出和正确输出重定向 命令>>文件1 2>>文件2 追加方式

其中,13和24等价.

另外, 如果不想将输出输出到文件中, 可以重定向到”黑洞”中, 也就是/dev/null, 这个文件就像是路由黑洞一样吃进去就没反应了, 或者类比成Windows下的回收站.

这个文件就是可以理解成Linux预置的垃圾桶, 在一些命令/脚本执行过程中发生的一些无关紧要的错误, 我没有必要将其显示出来. 而是可以直接丢弃掉.

关于输入重定向, 我们以wc来举个例子, wc可以统计输入流中的字节数, 单词数, 行数.

1
2
3
4
5
6
7
8
wc<anaconda-ks.cfg 
# 148 228 2523
wc<anaconda-ks.cfg -l
# 148
wc<anaconda-ks.cfg -w
# 228
wc<anaconda-ks.cfg -c
# 2523

这个意思是anaconda-ks.cfg文件中, 有148行, 228个单词, 2523个字节.

管道符

首先来说一下多命令执行符这个东西.

同高级语言一样, Shell的对多命令顺序执行的执行符和一些逻辑关联词是一样的.

多命令执行符 格式 作用
; 1;2 顺序执行, 没有逻辑联系
&& 1&&2 逻辑与, 只有1正确执行, 2才会执行
&Iota;&Iota; 1 &Iota;&Iota; 2 逻辑或, 只有1执行不正确, 2才会执行

一个简单的综合使用就像是这样

1
./abash.sh && echo yes || echo no

接下来看一下管道符:

|.

简单的一个竖杠, 可以将命令1的正确输出作为命令2的操作对象.

来看一个简单的小栗子:

1
netstat -an | grep ESTABLISHED | wc -l

这个命令组合显示当前已建立的连接数.

通配符

最后来看一下通配符.

通配符 作用
? 匹配一个任意字符
* 匹配0个或任意, 可以匹配任意内容
[] 匹配中括号内的任意一个字符
[-] 匹配中括号内任意一个字符, -表示一个范围
[^] 逻辑非, 匹配不是中括号内的一个字符

接着再来说一下Bash中的特殊字符:

符号 作用
‘’ 在单引号中的特殊符号都失去意义
“” 除了”$”, “`”, “", 其他都失去特殊含义
`` 调用系统命令, Bash中会优先执行
$() 等价于``,引用系统命令
# 注释
$ 调用变量的值
\ 转义字符

其中, 推荐使用 $() 来代替 `, 因为更清晰.


在开始进行Shell编程前, 必须还要了解一些环境变量,位置参数变量以及预定义变量.

我们已经知道如何去定义一个变量: a=4.

但是这样定义的变量是不能在Shell间通用的,现在就可以引入环境变量的概念了.

环境变量是作用于系统的一类变量, 只要在声明时加上export关键字就可以生效了.

使用set查看所有的系统变量, 使用env仅仅查看环境变量.

接下来说一下位置参数变量, 其实这个就像是Pythonsys.argv, 获取脚本参数的变量.

有这些位置参数:

参数 作用
$n n是数字, 0代表本身, 1-?就是后面的参数
$* 命令行中的所有参数(作为整体)
$@ 命令行中的所有参数(作为单独的部分)
$# 参数的个数(不包括脚本本身)

最后, 就是一些预定义变量了.

主要有三个:

参数 作用
$? 返回上次命令执行的状态
$$ 当前进程的PID
$! 后台运行的最后一个进程的PID

接下来开始进行Shell的学习吧!

变量


之前说过了如何去声明和打印一个变量, 但要知道的是, Linux中的所有变量默认都是字符串型的.

也就是说:

1
2
3
4
5
[root@localhost ~]# a=11
[root@localhost ~]# b=22
[root@localhost ~]# c=$a+$b
[root@localhost ~]# echo $c
11+22

这样就没办法进行数值运算了.所以先来学习一下怎么声明种类.

使用declare来声明变量类型,语法结构和支持的类型是:

1
2
3
4
5
6
7
8
declare [+/-][Opt.] 变量名
- : 设定
+ : 取消
-a : (array)数组型
-i : (interger)整数
-x : (env)环境变量
-r : (read-only)只读变量
-p : (print)打印变量声明的类型

举几个例子:

1
2
3
4
5
[root@localhost ~]# aa=11 
[root@localhost ~]# bb=22
[root@localhost ~]# declare -i cc=$aa+$bb
[root@localhost ~]# echo $cc
33

或者声明数组变量:

1
2
3
4
5
6
7
8
9
[root@localhost ~]# movie[0]=a
[root@localhost ~]# movie[1]=b
[root@localhost ~]# declare -a movie[2]=c
[root@localhost ~]# echo ${movie}
a
[root@localhost ~]# echo ${movie[1]}
b
[root@localhost ~]# echo ${movie[*]}
a b c

现在可以告诉你啦!

之前使用的export实际上调用的就是declare-x选项, 不信的话你可以试试declare -p, 接着你就会看到大部分的系统环境变量都是declare -x.

最后再看一下,只读属性(-r), 请注意:不要随意添加只读属性, 因为只读的属性是不可删除 不可改变 不可取消的.

现在就来学习数值运算了.

虽然之前说的声明类型同样可以进行数值运算, 但是这还是有点麻烦的.

所以当进行表达式(expression)运算时, 使用expr来进行运算.

e.g:

1
2
3
4
5
[root@localhost ~]# aa=11
[root@localhost ~]# bb=22
[root@localhost ~]# dd=$(expr $aa + $bb)
[root@localhost ~]# echo $dd
33

注意!expr后面的运算符两侧必须要加空格否则会失效

这就很好敲了? 相信你一定满脸MMP..

是的..这个命令更难敲.

所以来看一下这个:

1
2
3
4
5
6
7
8
9
[root@localhost ~]# aa=22 
[root@localhost ~]# bb=11
[root@localhost ~]# ff=$(( $aa+$bb ))
[root@localhost ~]# # 或者
[root@localhost ~]# gg=$[$aa+$bb]
[root@localhost ~]# echo $ff
33
[root@localhost ~]# echo $gg
33

这样就更好记了.

那么Shell支持哪些运算呢?

按照优先级从高到低:

运算符
单目正负
逻辑非.按位反
乘除 取模
加减
左移 右移
比较运算
按位运算
逻辑运算
赋值及运算赋值符

继续下去..我们来看一下变量测试

这个东西只有Shell中才有, 其他语言中是没有的.

好吧..这个东西主要是用来脚本调优.(真的我觉得没什么用)

下一个表来看,其实语法上没什么难的..

好了变量测试就先这样吧..