Shell 命令学习
下一篇: 几个 iptables 常用的配置
Shell脚本是非常强的大一个脚本语言,但是不用会手生,所以在此记录Shell脚本的相应关键点,也做查字典用^_^变量。
	变量
	变量定义
先来简单的看一下变量定义的规则
- 
		在Shell中,使用变量之前不需要事先声明,只是通过使用它们来创建它们;
- 在默认情况下,所有变量都被看做是字符串,并以字符串来存储;
- 
		Shell变量是区分大小写的;
- 在赋值变量的时候等号两端不能有空格-_-
	定义了变量之后,一定要加上$符号才能使用
| 1 2 3 4 5 6 7 8 9 10 11 12 | #! /bin/bash VAR1=HELLO VAR2=MY NAME VAR3="MY AGE" VAR4 = IS echo VAR1 #error 能输出 但不是输出该变量 echo $VAR1 #ok 正常读取变量并打印 echo $VAR2 #error 定义变量的值 用空格隔开了 echo $VAR3 #ok 作为一整个字符串 echo $VAR4 #error 变量定义的时候等号两端有空格 | 
输出的结果为
./test.sh: line 2: NAME: command not found
./test.sh: line 4: VAR4: command not found
VAR1
HELLO
MY AGE
	关于shell脚本的执行:shell基本一般是以.sh为后缀,然后在*unix系统下一般都是直接使用./[当前shell文件名] 的方式来执行,也可以使用全部经/[shell文件名]的方式来执行,并且需要注意的是 被执行的shell文件一定是有含有可执行权限了的,可以使用chmod命令来修改
	还有另一个点就是在调用变量的时候 ,如果在双引号中直接使用$name任然可以识别,但是如果在单引号是就无法适用$name的方式来调用变量
	read读取输入值
	这个功能就像java中的readline来读取,使用方法为
| 1 2 3 4 5 6 7 | #! /bin/bash echo "whats your name?" read NAME #在这里读取输入值到NAME变量中 ,这里如果不输入会停留在屏幕上 echo "webcome back" $NAME exit 0 | 
可以看到熟悉的结果为
whats your name?
tom
webcome back tom
	环境变量
	Shell脚本还提供能一些实用的环境变量
- 
		$HOME:为当前用户所在的目录
- 
		$PATH:当前用户所能方法的PATH变量
- 
		$#:传递参数额个数 类似java中的args.length
- 
		$$:Shell脚本的进程号,脚本程序通常会用它来生成一个唯一的临时文件。
| 1 2 3 4 5 6 7 8 9 | #! /bin/bash echo "当前用户所在的目录为" $HOME echo "当前的执行目录为" $(pwd) #这个是访问当前的脚本的目录很实用 echo "当前用户所能访问的PATH为" $PATH echo "当前参数的参数个数为" $# #这儿参数的格式是使用空格隔开的哦 echo "当前Shell脚本的进程号为" $$ exit 0 | 
可以到看的结果是
yans-MacBook-Pro:Downloads yanyl$ ./hi.sh  hello world
当前用户所在的目录为 /Users/yanyl
当前的执行目录为 /Users/yanyl/Downloads
当前用户所能访问的PATH为 /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/yanyl/Program/apache-maven-3.2.5/bin:/Users/yanyl/Program/scala-2.10.4//bin
当前参数的参数个数为 2
当前Shell脚本的进程号为 43746
	假如需要进入当前目录的父目录,可以使用$(dirname $(pwd))
	参数变量
	刚刚看到可以使用read关键字可以来读取输入变量,但是我们可能更加常用的是参数变量,也就是$#的个数,它的规则如下
- 
		$#表示参数变量的个数
- 
		$0表示当前的脚本名称
- 
		$1,$2…$n表示依次能读取到的变量 但是如果参数变量不够,$i会被赋值为空
| 1 2 3 4 5 6 7 8 9 | #! /bin/bash echo "当前输入的参数变量的长度为" $# echo "当前执行的Shell脚本为" $0 echo "当前输入的第一个参数为" $1 echo "当前输入的第二个参数为" $2 echo "当前的输入的第三个参数为" $3 #现在如果只传2个参数 这里将不会报错 exit 0 | 
可以看到的结果为
yans-MacBook-Pro:Downloads yanyl$ ./hi.sh  hello world
当前输入的参数变量的长度为 2
当前执行的Shell脚本为 ./hi.sh
当前输入的第一个参数为 hello
当前输入的第二个参数为 world
当前的输入的第三个参数为
	可以看到在Shell脚本中去读取参数变量还是很方便的,这样配合下面的条件判断以及循环就可以做很多事情了
	读取返回码
一般的程序/命令在执行结束时都会返回一个 返回码,比如
- 
		java的system.exit(-1)
- 
		python的sys.exit(-1)
- 
		还有上面Shell脚本中的最后一行exit 0
如果你不显式指定返回码,一般默认为0,表示正常退出,但是有时候显式的指定返回码是一个好习惯哦
	这些程序在Shell中执行的,可以使用$?来读取上一个程序执行下来的脚本码
| 1 2 3 4 5 6 7 8 9 | #! /bin/bash du -s #执行的返回码一般为0 echo du -s的返回码为 $? duu -s #这个命令故意输错 echo duu -s的返回码为 $? exit 0 | 
可以看到正确的结果为
28494656    .
du -s的返回码为 0
./hi.sh: line 6: duu: command not found
duu -s的返回码为 127
	返回码配上if判断,就可以使用shell脚本自由得在各个语言以及命令中穿梭啦^_^
	数学运算
	在上一小节中说道,Shell中变量一般都是当字符串来处理,那我遇到数字运算该咋办呢??
可以先看
| 1 2 3 4 5 6 7 8 | #! /bin/bash a=1+2 b=$a+3 echo $a echo $b exit 0 | 
结果却看到
1+2
1+2+3
	那在Shell中解决这个问题大概有这么几种方法
	let关键字
| 1 2 3 4 5 6 7 8 | #! /bin/bash let a=1+2 let b=$a+3 echo $a echo $b exit 0 | 
输出的结果为
3
6
这个关键词大致需要注意以下几个点:
- 
		let只支持整数运算
- 
		当let后面的运算部分有bash关键字时,需加双引号
- 幂次方可以使用**符号
	使用(())
| 1 2 3 4 5 6 7 8 | #! /bin/bash ((a=1+2)) ((b=$a+3)) echo $a echo $b exit 0 | 
结果还是正确的
3
6
	(())的用法与let完全相同
	使用$[]
上面的效果需要这么写
| 1 2 | a=$[1+2] b=$[$a+3] | 
其余与上面两种限制大致相同
	使用expr
关于这个方式是这么写的
| 1 2 | a=`expr 1 + 2` b=`expr $a \* 3` #需要转义 | 
需要额外注意的有:
- 运算符两端需要加空格 一定要记住。。。很容易失误
- 
		对于|、&、<、<=、>=、>、*运算符号需要加上\进行转义
	使用bc
这个终于是可以用于浮点数的运算了
| 1 2 3 4 5 6 7 8 | #! /bin/bash a=3.1415926 b=`echo "$a*2"|bc` echo $a echo $b exit 0 | 
可以看到结果
3.1415926
6.2831852
	据说这里还有一个scale来设置精度,但是我设置了感觉木有效果-_-
	条件判断
	if 语法
	在Shell脚本中有两种书写if判断的语法
- 
使用 test关键字1 2 3 4 5 6 7 8 9 10 11 #! /bin/bash # if test expression1 operation expression2 if test 5 -gt 4; #这个最后的结尾可以加上:或者; then echo "ok,5>4" else echo "oh,no" fi #这个结束符号必须得加 exit 0 输出为 ok,5>4
- 
使用 [和]关键字1 2 3 4 5 6 7 8 9 10 11 #! /bin/bash # if [ expression1 operation expression2 ] if [ 5 -lt 4 ]; #注意[和]两端必须留空格 同时表达式两端都需要有空格 then echo "ok,5>4" else echo "oh,no" fi exit 0 输出为 oh,no
	如果还更加复杂的判断你可以使用elif继续增加条件表达式,但是别忘了加then哦
	判断表达式
	在Shell中有三种判断表达式
	字符串比较
| 字符串比较 | 结果 | 
|---|---|
| string1 = string2 | 如果两个字符串相同,也可用 ==结果就为真 | 
| string1 != string2 | 如果两个字符串不同,结果就为真 | 
| -n string | 如果字符串不为空,则结果为真 | 
| -z string | 如果字符串为一个空串( null),则结果为真 | 
	这里需要注意下,-n 和 -z string比较时必须用双引号(“”)将变量引起来
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | #! /bin/bash a=5 if [ -n "$a" ] #注意要空括号来包住哦 then echo exists else echo null fi if [ -n "$c" ] then echo exists else echo null fi exit 0 | 
结果为
exists
null
	算术比较
| 算术比较 | 结果 | 
|---|---|
| expression1 -eq expression2 | 如果两个表达式相等,则结果为真 | 
| expression1 -ne expression2 | 如果两个表达式不等,则结果为真 | 
| expression1 -gt expression2 | 如果 expression1大于expression2,则为真 | 
| expression1 -ge expression2 | 如果 expression1大于等于expression2,则为真 | 
| expression1 -lt expression2 | 如果 expression1小于expression2,则为真 | 
| expression1 -le expression2 | 如果 expression1小于等于expression2,则为真 | 
| !expression | 表达式为假,则结果就为真;反之亦然 | 
	关于上面比较符号的快速记法如下:eq=equal,gt=great than,lt=less than,然后组合拼凑即可,如果觉得这样还是很难记,就可以像我一样,将这些符号记录下来,需要的时候来查表-_-
	文件条件测试
| 文件条件测试 | 结果 | 
|---|---|
| -d file | 如果文件是一个目录,则为真 | 
| -f file | 如果文件是一个普通文件,则为真;也可以用来测试文件是否存在 | 
| -r file | 如果文件可读,则结果为真 | 
| -s file | 如果文件大小不为0,则结果为真 | 
| -w file | 如果文件可写,则结果为真 | 
| -x file | 如果文件可执行,则结果为真 | 
这,真的是一个利民的测试
	循环结构
	for 循环
	先来看一种经典C语法版的for
| 1 2 3 4 5 6 7 | #! /bin/bash for ((i=0;i<5;i++)) do echo $i done exit 0 | 
看输出,
0
1
2
3
4
还支持在外部控制步长
| 1 2 3 4 5 6 7 8 | #! /bin/bash for ((i=0;i<5;)) do echo $i i=$[$i+2] done exit 0 | 
0
2
4
	是不是感觉基本功能都有呀,就是写某些东西写起来奇怪点
	是不是有一种莫名的熟悉感
	另一种就是类似foreach的情况了,他的格式是这样的
| 1 2 3 4 | for variable in values do statements done | 
	其中values 可能有的情况为:
- 
使用 linux命令输出的行作为迭代的输入:ls,seq,cat之类均可,其实就可以完成很强大的文件读取功能1 2 3 4 5 6 7 #! /bin/bash for i in `head -n 5 words.dit`;do #words.dit 这是一个通用词表 每行一个词 echo $i done exit 0 可以看到通用词典中前5个词 阿 阿巴丹 阿巴岛 阿巴鸟 阿巴伊达
- 
使用 $*可以来表示遍历传入的参数列表1 2 3 4 5 6 7 #! /bin/bash for i in $*;do echo $i done exit 0 来看个结果 yans-MacBook-Pro:Downloads yanyl$ ./hi.sh my name is tom my name is tom
- 
还可以使用带空格的字符串 来进行按空格分隔输出 1 2 3 4 5 6 7 8 #! /bin/bash a="yello red green" for i in $a;do echo $i done exit 0 这样在一定程度上可以看成一个简易的数组 
	这里需要注意的是包含条件以及循环逻辑是双重括号,以及开始结果的do和Done
	while 循环
	另一个常用的就是while循环了
他的结构是
| 1 2 3 4 | while condition do statements done | 
这个也是蛮好理解的,可以来看一个demo
| 1 2 3 4 5 6 7 8 9 10 11 12 13 | #! /bin/bash echo "please ent your password:" read pwd while [ "$pwd"x != "root"x ] #这里加x是为了防止啥也不输入直接回车产生的报错 do echo "error,please try again:" read pwd done echo "welcome here" exit 0 | 
看一下结果
please ent your password:
sha
error,please try again:
error,please try again:
root
welcome here
很有意思的一个哈~
	until语句
	这个语句与while的结构完全一样,只是使用了until关键字来代替了while,然后在条件为true的时候停止,正好与while相反
	函数
	Shell这么叼,能没有函数吗
| 1 2 3 4 | [function] functon_name() { statements } | 
上面是定义函数的结构,大致有以下几个要点
- 
		前面的function关键字可有可无,不过感觉还是加上去比较好,这样在代码里面比较好辨识
- 
		函数名后面的括号中不能带参数 取的参数是用过$1,$2…$n这样的方式来取的
- 调用的时候直接写函数名 不需要加括号
- 
		如果想传递参数的话 直接在调用后来加上参数列表 用空格隔开 (就是Shell的传参一样)
- 
		使用local关键字来定义函数体里面的局部变量
- 所以在函数调用必须在函数定义之后
先看一个小的demo
| 1 2 3 4 5 6 7 8 9 10 | #! /bin/bash function sayhi() { echo hi $1 } sayhi tom #前面的sayhi是函数的调用 后面的tom是传参 exit 0 | 
可以看到输出
hi tom
	函数的返回值
	关于Shell的返回值方式有两种
- 
输出给主程序,他的结构为: 1 2 3 4 5 6 function function_name() { echo $something #通过输出的方式来返回值 } a=`function_name` 这种方式接收返回值 看到的demo可以是这样的 1 2 3 4 5 6 7 8 9 10 11 12 Press ENTER or type command to continue #! /bin/bash function sum() { echo $[$1+$2] } a=`sum 1 2` echo the sum is $a exit 0 最终输出结果为 the sum is 3
- 
使用 return作为返回码来返回值1 2 3 4 5 6 7 function function_name() { return $ret #这里进行返回码的返回 } function_name $? #在这里接收返回值 一样再来一个demo 1 2 3 4 5 6 7 8 9 10 11 #! /bin/bash function sum() { return $[$1+$2] } sum 1 2 echo the sum is $? exit 0 可以看到输出为 the sum is 3
	case语句
	这里的case的与传统的switch有点像,但是又像scala中的match模式匹配的强大,
他的结构是这样的
| 1 2 3 4 5 | case variable in pattern [ | pattern] ...) statements;; pattern [ | pattern] ...) statements;; ... esac | 
来看这个强大的demo
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #! /bin/bash function match() { case $1 in root ) echo this is password ;; h* ) echo hi $1 ;; #使用通配符 yes | YES ) echo agree with me ;; #可以进行或操作 * ) echo everything is here;; #你可以理解为switch中的default esac } match root match hello match YES match Yes exit 0 | 
来看一下结果
this is password
hi hello
agree with me
everything is here
注意,这里一旦匹配中了一个之后就马上会停止匹配
	外部命令/文件/语言的调用
	Shell的另一个强大之处就是可以无缝的和外部的命令,文件,语言结合,去调用组织他们
- 
		外部命令:一般情况下可以直接写外部命令,如果要赋值的话得使用``括起来
- 
		外部文件:比如资源配置文件,profile文件之类的,可以直接使用source关键字的来执行
- 
		外部语言:比如java,python可以直接使用他们的java调用jar,java文件,也可以直接使用关键字来执行python文件
	总结
- 
		Shell很好很强大,得学习!!!
- 注意变量的字符串格式以及需要数学运算时的语法
- 
		注意变量赋值时等号两端一定不能有空格以及再取值时一定要加$
- 
		平常的控制结束符号别忘了,比如fi,doen,esac等
- 忘了的时候来查查这个文件
原文:http://kubicode.me/2015/11/04/Linux/Shell-Command-List/
下一篇: 几个 iptables 常用的配置