刘小泽写于18.6.24-7.1 一个星期入门linux
我相信这是比之前写的 “测序的世界” 更浩大的工程,但值得写出来!
离用户最近的强大程序——bash
bash及其特性:
-
发展历史:
shell翻译为外壳,是用户连入计算机后进行交互式操作的程序
广义上shell包含两类:
GUI:Gnome、KDE、Xface
CLI:最早期bsh(近似B语言) --> csh(编程方式类似C语言), 大大促进Unix流行-->ksh(比csh更强大,但需要付费) --> linux流行后也需要一个shell, 出现了bash(born again shell),兼具了csh、ksh的各种特性,且更强大 --> 最新的zsh更丰富,但不是很流行 -
shell特点:
-
shell本身就是进程。可以新建多个shell,且互不冲突。
进程:在每个进程看来,当前主机上只存在内核和当前进程。名字可以相同,但进程号各自唯一,Linux只识别进程号【就像全国重名的人很多,但身份证没有重复】
shell作为一个程序、一个外部命令(相对于内核来讲)。作为程序就能运行内部命令,因此还能继续创建子shell。因此很多时候,对子shell的设定,对父shell无效;反之亦如此。
shell可以交互打开。例如当前打开的是bash,可以在bash中敲ksh,在ksh中又可以敲zsh...
-
-
bash特性
命令行编辑:
光标跳转:
Ctrl+A:跳到命令行首
Ctrl+E:跳到命令行尾
Ctrl+U:删除光标至命令行首的内容
Ctrl+K:删除光标至命令行尾的内容
Ctrl+L:清屏-
命令历史:history
-
删除:
-c : 清空全部历史
-d:删除指定位置【要删除指定位置向下n个,在-d后加n】 -
调用:
!n: 执行第n条命令
!-n:执行倒数第n条
!!:执行上一条
!string: 执行命令历史中最近一个以指定字符串开头的命令
Esc松开后按 .
: 引用上一个命令的最后一个参数【也可以!$】
-
命令补全:
输入的前几个字符是在PATH中能唯一识别的,敲一次tab就能打出来;
如果不能,敲两次tab会列出所有和输入字符相关的命令-
命令别名alias:
-
bash只是一个程序,当前所有的设置将在退出这一个程序后失效
若要长期使用alias,可将相应的alias命令存放到bash的初始化文件
/etc/bashrc
中 方法:alias 别名='原命令 -选项/参数'
撤销:unalias 别名
\别名:适用于别名和原命令一样,只是添加了一些参数,现在想使用原命令
-
-
命令替换:
把$()中的子命令替换为前面命令的执行结果,举两个例子就能懂例如要打印当前路径:
echo "The current dir is (pwd)" 例如要在当前目录下新建一个包含年月日时分秒的文件 touch ./file-(date +%F-%H-%M-%S).txt -
命令行展开
用命令行展开特性一步完成需要分开成多步完成的操作
使用{}
将相应的参数括起来,括号中的参数以逗号分隔, 例如:/tmp/{x,y} #生成/tmp/x和/tmp/y
mkdir {1..5} #生成1-5为名的文件夹
mkdir -p {1..5}/{1..5} #在1-5的文件夹里再生成1-5的文件夹 -
文件名通配 globbing
快速匹配到你想要的文件例如:touch a123 abc ab123 xyz x12 xyz123 新建这6个文件
目的:-
找出a开头的文件:ls a* => a123 ab123 abc
【:匹配任意长度的任意字符】*
-
找出第二个字母是y的文件 ls ?y* => xyz xyz123
【?:任意单个字符】
【如果找第三个字母是y的,只需要 ??y* 】 -
以字母开头,数字结尾,中间不限
ls [a-zA-Z] * [0-9]
[ ]:匹配指定范围内的任意单个字符
[a-z], [A-Z], [0-9], [a-zA-Z]
[^]:匹配指定范围之外的任意单个字符
[^0-9] 非数字 [^[:alpha]] 非大小写字母
[[:alpha:]]:大小写字母 = [a-zA-Z]
[[:space:]]:空白字符
[[:punct:]]:标点符号
[[:lower:]]:小写字母
[[:upper:]]:大写字母
[[:digit:]]:数字
[[:alnum:]]:数字和大小写字母 = [a-zA-Z0-9]
【练习:】
【1. 创建如下文件:xi、jie6、u56m、my、m.r、t 94、8%u、567
注意:t 94文件中间有空格!- 显示以a或m开头的文件;
3. 显示文件名中包含数字的文件;
4. 显示以数字结尾且文件名中没有空白的文件;
5. 显示文件名中包含非字母或数字的特殊符号文件
-
Shell编程:
1. 基础知识:
人和机器交流需要语言,这种语言叫做编程语言,使用编译器或解释器让人机解读
编程语言:机器语言、汇编语言、高级语言
- 高级语言:最接近人类的思维方式,相对容易学习;并且通过编译器也可以让机器理解
-
静态语言:编译型语言
有一个程序开发环境,不需要借助额外的二进制程序,就能直接写代码。写完代码后需要一个编译器,将其直接转换为二进制后可以独立运行
强类型语言(类型即变量类型):必须事先声明变量,可能还需要初始化
(数值为0,字符串为空NULL
)需要事先完全转换成可执行的二进制格式
C、C++、JAVA、C# -
动态语言:解释型语言
弱类型语言:变量随用随声明(默认为字符串)执行的时候再转换【有一个解释器,边解释边执行】
PHP、SHELL、python、perl面向过程: Shell, C【linux就是基于C语言】
面向对象:JAVA,Python,perl【perl也面向过程】,C++
- 变量:明确的说变量是内存空间
首先数据存储在内存中,如果将所有待计算的数值都存在内存,内存肯定不够大。因此随着运算器运算,内存空间是不断变化的。比如要计算1加到1000,先存进来1,然后运算器拿走,接着把2放入内存空间,然后2被拿走...
所以这段内存空间是不断变化的,这就是变量
【了解即可】变量有两种类型:字符和数值(整型和浮点型)
为何要区分类型呢?=> 用于事先确定数据存储格式和长度,防止数据溢出
【逻辑运算】:与(&)、或、非(!) 1:真;0:假
短路逻辑运算:
(1): 与:只要有一个为假,结果一定为假
(2): 或:只要有一个为真,结果一定为真
【 变量赋值】VAR_NAME=VALUE
【变量名称】只能包含字母、数字、下划线,且不能以数字开头;
不应该与系统中已有的变量重名;
最好做到见名知义
2. bash变量:
-
变量类型:本地变量(局部变量)、环境变量、位置变量、特殊变量
-
本地变量(局部变量)
本地: VARNAME=VALUE:【作用域为当前bash进程, 对子shell失效】; 引用变量:${VARNAME},不影响变量名时可以略去括号。 e.g. 定义animal=pig 分别输入三条命令,看看结果: 1. echo "there are some $animals" 2. echo "there are some ${animal}s" 3. echo 'there are some ${animal}s' 【单引号是强引用;双引号弱引用】 局部: local VARNAME=VALUE:作用域为当前代码段;
-
环境变量:
作用域为:【当前shell进程及其子进程】。 脚本在执行时会启动一个子shell进程: 命令行中启动的脚本会继承当前shell环境变量; 系统自动执行的脚本(非命令行启动)就要定义需要的各环境变量 export VARNAME=VALUE “导出环境变量” VARNAME=VALUE export VARNAME
-
位置变量:
$1, $2, ... #表示命令的第 1 个参数,第 2 个参数... shift:对于同一个$1,可以进行轮换【好比执行过的$1为老员工,他要退休,把位置让给年轻人,也就是下一个$1】;同时shift # 也可以同时shift多个
-
特殊变量:
$? 上一个命令的执行状态返回值 【当数据被丢掉/dev/null中,没有返回结果时,可以用这个判断】 一般程序执行可能有两类返回值: 1. 程序执行结果 2. 程序状态返回代码(0-255) 0:正确执行--幸福 1-255:错误执行--不幸[1,2,127系统预留] 【应了一句名言:幸福的家庭都是相似的,不幸的家庭各有各的不幸】 $# 参数的个数 $* 参数列表 $@ 参数列表
-
撤销变量:
定义变量可以用set,set $VARNAME, 但是不用也行
撤销变量用unset VARNAME查看shell中的变量
set -- 包括了环境变量和本地变量
printenv / env / export -- 查看环境变量-
添加变量:(如添加环境变量)
#在环境变量后面添加变量 export PATH=$PATH:/usr/local/apache/bin #在环境变量前面添加变量 export PATH=/usr/local/apache/bin:$PATH
3.脚本:
什么是脚本?
命令的堆砌,按实际需要,结合命令流程控制机制实现的程序
-
魔数:shebang
#!/bin/bash #注释行不执行
-
条件判断:【可以说是追求真值的过程】
-
命令间的逻辑关系:
逻辑与:&&【只为验证结果是否为真】
第一个条件为假,第二个条件不用再判断;
第一个条件为真,结果取决于第二个条件-
逻辑或:||【只为验证结果是否为真】
第一个条件为假,第二个继续判断,有一个真就行;
第一个为真,则第二个不用再判断例如:如果用户存在,就显示“用户存在”,否则添加:
id user1 && echo "user exists" || useradd user1进阶: 如果用户不存在,新建用户并且给他密码,否则显示存在
! id user1 && useadd user1 && echo "user1" | passwd -stdin user1 || echo "user1 exists"
-
-
条件测试: 【整数、字符、文件测试】
测试表达式:【一定要有空格】[ expression ] -- 命令测试法
[[ expression ]] — 关键字测试法
-
test expression
例如:测试两个整数变量是否相等
INT1=66
INT2=77[ $INT1 -eq $INT2 ]
[[ $INT1 -eq INT2 ]]
set $INT1 -eq $INT2
- 整数测试:
[ expression ]:表达式两段必须要有空格 -eq:测试两个整数是否相等;比如 `$A -eq $`B; -ne:测试两个整数是否不等;不等,为真;相等,为假; -gt:[great than]测试一个数是否大于另一个数;大于,为真; -lt:[less than]测试一个数是否小于另一个数;小于,为真; -ge:大于或等于 -le:小于或等于
例如:检查用户UID是否为0,为0就显示管理员,否则显示为普通
【注意:先定义变量,这样在整个脚本中都能重复使用】#!/bin/bash NAME=user1 USERID=`id -u $NAME` [ $USERID -eq 0 ] && echo 'Admin' || echo 'Common'
⚠️小Tip:
【反引号的意思是调取命令执行的结果;**
不加反引号只是表明命令的运行状态,是成功,还是失败,如果只需要状态的话,那么命令结果对我们来讲就是无用信息,可以丢到/dev/null中】**-
文件测试:
-e FILE:测试文件是否存在;e.g. [ -e /etc/innitab ] -f FILE:测试文件是否为普通文件; -d FILE:测试指定路径是否为目录; -r FILE:测试当前用户对指定文件是否有读取权限; -w FILE:测试当前用户对指定文件是否有写入权限; -x FILE:测试当前用户对指定文件是否有执行权限;
例如:给定一个文件,比如/etc/passwd,判断这个文件中是否有空白行;如果有,则显示其空白行数;否则,显示没有空白行。
#!/bin/bash #先判断这个文件是否存在,不存在就退出 FILE=/etc/passwd if [ ! -e $FILE ];then echo "No such file" exit 1 fi #存在再继续判断空白行 if grep "^$" $FILE &> /dev/null;then echo "Blank rows number is: `grep "^$" $FILE | wc -l`" else echo "No blank line" fi
字符测试:
等值比较:[ $A = $B ]
不等值比较:[ $A != $B ]
测试是否为空:-n
测试指定字符串是否不空:-z-
如何组合两个以上的条件?
-a 与关系; -o:或关系; !非关系
e.g. 表示1 <= # <= 3: `if [# -ge 1 -a # -le 3]` 或者 `if [# -ge 1] && [ $# -le 3]`
-
控制结构:
-
单分支if语句
#几个必须条件:必须有分号,必须有then,但是then可以另起一行,结尾必须是fi且单独一行 if 判断条件; then statement1 statement2 ... fi
-
双分支if语句
if 判断条件; then statement1 statement2 ... else statement3 statement4 fi
例如:判断当前系统上是否有用户的默认shell为bash; 如果有,就显示其中一个的用户名;否则,就显示没有这类用户
【借用/etc/passwd可以查看用户名、UID、GID、SHELL等】#!/bin/bash #先看抓取‘结尾的bash’的状态结果是否为0,如果有bash,继续 #另外判断完后,这个信息不用输出,所以丢到/dev/null中 grep '/
/dev/null RETVAL=$? #retval意思是‘返回值’ if [ $RETVAL -eq 0 ]; then #返回值为0,即正确,有结果 AUSER=`grep '\>bash$' /etc/passwd | head -1 | cut -d: -f1` #使用反引号``是赋给变量运行结果 echo "$AUSER does exist" #双引号弱引用 else echo "No such user" fi -
多分支if语句:
if 判断条件1; then statement1 ... elif 判断条件2; then statement2 ... elif 判断条件3; then statement3 ... else statement4 ... fi
-
case 语句:号称比if多分枝更易懂的结构
case SWITCH in value1) statement ;; value2) statement ;; *) statement ;; esac
-
-
for循环:
for 变量 in 列表; do 循环体 done 遍历完成之后,退出; # 如何生成列表: 1. {1..100} 2. `seq [起始数 [步进长度]] 结束数` # 起始、步长可省略 e.g. seq 1 2 10 => 1 3 5 7 9 # 声明 SUM 是整型 declare -i SUM=0 [-x # 声明是环境变量] #计算从 1 加到 100 #!/bin/bash # declare -i SUM=0 for I in {1..100}; do #SUM=$[$SUM+$I] #或 SUM+=$I done echo "The sum is $SUM."
e.g.1 for循环脚本,依次向/etc/passwd中的每个用户问好
#!/bin/bash # LINES=`wc -l /etc/passwd | cut -d' ' -f1` for i in `seq 1 $LINES`;do echo "Hello, `head -n $i /etc/passwd | tail -1 | cut -d: -f1`" done
e.g.2 计算1000以内奇数之和、偶数之和:
#第一种写法 #!/bin/bash # declare -i ODDSUM=0 for i in `seq 1 2 1000`;do ODDSUM=$[$ODDSUM+$i] done echo "the sum of odd is:$ODDSUM" declare -i EVENSUM=0 for i in `seq 1 2 1000`;do EVENSUM=$[$EVENSUM+$i] done echo "the sum of even is:$EVENSUM" #第二种写法 #!/bin/bash # declare -i EVENSUM=0 declare -i ODDSUM=0 for I in {1..1000};do if [ $[$I%2] -eq 0];then let EVENSUM+=$I else let ODDSUM+=$I fi done echo"Odd sum is $ODDSUM" echo "Even sum is $EVENSUM"
结果就是:the sum of odd is:250000
the sum of even is: 250500
-
while循环
适用于循环次数未知的情况
while CONDITION; do statement ... done
例如:计算100以内所有正整数的和 #!/bin/bash declare -i I=1 declare -i SUM=0 while [ $I -le 100 ]; do SUM+=$I let I++ done echo $SUM
-
Shell中进行算术运算【前三种最为常见】
let 算术运算表达式
let C=$A+$B
-
$ [算术运算表达式]
C=$[$A+$B]
-
$((算术运算表达式))
C=$(($A+$B))
-
expr 算术运算表达式,
表达式中各操作数及运算符之间要有空格,而且要使用命令引用C=
expr $A + $B
随机数
echo $RANDOM
RANDOM: 0-32768
-
exit 提前结束脚本
exit #
状态值0代表执行成功,其他值代表执行失败。
如果脚本没有明确定义退出状态码,那么,最后执行的一条命令的退出码即为脚本的退出状态码(所以需要自己指定一个状态码,例如exit 1) 测试脚本是否有语法错误:
bash -n 脚本
但是测试结果是模糊的,不能作为最终判断依据
bash -x 脚本
可以清楚看到脚本中哪个地方不符合我们预期,
对于大段脚本尤其适用!
-
之前学过了变量的四种类型-->
本地变量(局部变量)、环境变量、位置变量、特殊变量上面使用的主要有本地变量、特殊变量(
$?、$#
).
位置变量如何在脚本中应用呢?位置变量最大的作用就是将命令后的各个参数引入进脚本,
化身成$1, $2...
这种
e.g. 给脚本传递两个参数(整数);显示此两者之和,之积#!/bin/bash #如果获取的参数不是两个,那么就退出不再进行 if [ $# -ne 2 ]; then echo "Usage:test.sh ARG1 ARG2 [...]" exit 1 fi #####以上就是使用命令时出现的使用帮助,就是这么来的##### #接下来如果是两个,就求和、求乘积【注意echo的弱引用】 echo "The sum is:$[$1+$2]." echo "The prod is:$[$1*$2]."
欢迎关注我们的公众号~_~
我们是两个农转生信的小硕,打造生信星球,想让它成为一个不拽术语、通俗易懂的生信知识平台。需要帮助或提出意见请后台留言或发送邮件到[email protected]