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 常用的配置