shell 脚本是一种解释型语言。所以需要配套解释器来解释你的脚本。
一、解释器选择
在脚本的第一行,就要注明你的脚本需要使用什么解释器来运行,比如#!/bin/bash,这个意思就是用/bin/bash来解析脚本。以下是常用的。
#!/bin/bash <- 推荐
#!/bin/sh
#!/bin/ksh
#!/usr/bin/env bash <- 推荐
最后一种#!/usr/bin/env bash,通过/usr/bin/env运行程序,用户不需要去寻找程序在系统中的位置(因为在不同的系统,命令或程序存放的位置可能不同),只要程序在你的$PATH中。通过/usr/bin/env运行程序另一个好处是,它会根据你的环境寻找并运行默认的版本,提供灵活性。
当然也可以自己定义一个脚本语言,用自己定义的解释器去解释他,比如你的解释器叫做xshell,放在/usr/local/bin下面。所以使用的时候就是#!/usr/local/bin/xshell。
二、运行和调试
一个脚本文件如何去运行呢,可以使用如下的方式运行脚本。
1、当前路径下运行
./test.sh
2、绝对路径运行
/tmp/test.sh
3、使用 . 运行
. test.sh
4、使用sh
sh test.sh
5、使用source
source test.sh
source、./ 、sh、. 这几种方式都可以运行脚本,但是这几种方式有一些区别。source和.和sh是在当前bash环境下读取并执行FileName中的命令。该filename文件可以无”执行权限”,sh更灵活带有很多参数,source常用来更新系统配置文件。./是打开一个子shell来读取并执行脚本中命令。
脚本写完后需要调试,调试方式如下。
1、在脚本中, 不推荐
#! /bin/bash -x
2、在运行的时候
sh -x test.sh
脚本调试中不推荐使用第一种方式,因为生产和测试的代码肯定是一样的,第一种方式还要去修改脚本代码,这种很容易忘了去修改回来,导致运行的时候都是调试信息。调试信息中前面带+或者++的表示执行的命令,不带的表示输出结果。
三、脚本基本语法
3.1、变量
定义变量,key=value,主要分成两种动态变量和静态变量,静态变量就是数值是暂时不会变化的,习惯上字符串使用“”,数值的就不用引号;动态变量,值得是执行后才知道结果的值(常用于包含命令),使用反引号。
#静态变量
me="woshihaha"
#动态变量
date=`date`
echo ${me}
echo ${date}
3.2、传递参数
传递的参数以$为开头。
字符 | 含义 | 讲解 |
---|---|---|
$n | 参数n,n从1开始 | $1就是参数一,$2就是参数2 |
$0 | 当前脚本的文件名 | |
$# | 参数的个数 | |
$* | 所有的参数 | |
$@ | 所有的参数 | |
$? | 上个命令的退出状态,或函数的返回值。 | |
$$ | 当前Shell进程ID。对于 Shell 脚本,就是这些脚本所在的进程ID。 |
$* 和 $@ 都表示所有参数,但是两者之间是有区别的。
脚本名称叫test.sh 入参三个: 1 2 3
运行test.sh 1 2 3后
$*为"1 2 3"(一起被引号包住)
$@为"1" "2" "3"(分别被包住)
3.3、数组
数组都是一维的。 定义方式key=(value1 value2 value3 value4)
1、访问数组内元素,从0开始
echo ${arr[0]}
2、输出所有元素
echo ${arr[*]}
3、取数组的个数
echo ${#arr[*]}
3.4、判断
用if判断,基本方法:
if [ "$a" == "$b" ]; then
elif[ "$a" == "CCC" ]; then
else
fi
当然如下这样子也是可以的
if [ "$a" == "$b" ]
then
else
fi
需要注意的是[]的内部两边是要有空格。等于号两边也要有空格。
if判断有一堆的参数,如下
参数 | 含义 | 样式 |
---|---|---|
-z | 是否为空串 | [ -z $myvar ] |
-f filename | 如果 filename为常规文件,则为真 | [ -f “${A}”/start-dfs.sh ] |
-gt | 大于 | if [ $# -gt 1 ]=>参数大于1 |
-e | 当pathname指定的文件或目录存在时返回真 | [ -e $myvar ] |
-ge | 大于等于 | [ $# -ge 1 ] |
-n | 非空串 | |
-eq | 等于 | [ $# -eq 1 ] |
3.5、循环
循环主要用到两种,for循环和while循环。
for x in 1 2 3 4 5
do
echo $x
done
for循环除了用in,还可以这样for ((i=1;i<10;i++)),这种方式很像Java代码。while循环:
i=1
while(($i<10))
do
echo $i
let "i++"
done
3.6 case
像其他语言switch一样,有条件的选择。
case "$promer" in
"A")
echo "A"
;;
"B"|"C")
echo "A"
;;
*)
echo "A"
;;
esac
四、常用方法
4.1 获取当前脚本所在路径
获取当前脚本的所在的路径有两个问题需要注意下。
第一,pwd确实是用于获取绝对路径,但是如果你的shell脚本是test.sh,运行的时候不是本地运行,而是在上层目录运行的(如:shell/test.sh),这个时候的pwd就是上层父目录的地址。
第二,$0,这个在不同的调用时候有不同的含义。
建议的写法:
BASEPATH=$(cd `dirname $0`; pwd)
解析:
dirname $0 <- 去除文件名中的非目录部分,仅显示与目录有关的内容,即文件所在当前目录
cd <- 进入这个目录
pwd <- 获取该目录的路径
4.2 变量配置
可以进行变量的条件替换,既只有某种条件发生时才进行替换。
变量配置方式 | str 没有配置 | str 为空字符串 | str 已配置非为空字符串 |
---|---|---|---|
var=${str-expr} | var=expr | var= | var=$str |
var=${str:-expr} | var=expr | var=expr | var=$str |
var=${str+expr} | var= | var=expr | var=expr |
var=${str:+expr} | var= | var= | var=expr |
var=${str=expr} | str=expr var=expr |
str 不变 var= |
str 不变 var=$str |
var=${str:=expr} | str=expr var=expr |
str=expr var=expr |
str 不变 var=$str |
var=${str?expr} | expr 输出至 stderr | var= | var=$str |
var=${str:?expr} | expr 输出至 stderr | expr 输出至 stderr | var=$str |
4.3、分割字符串
字符串要按照某种特殊字符进行分割,如下:
s="1,2,3,4,5"
OLD_IFS="$IFS"
IFS=","
arr=($s)
IFS="$OLD_IFS"
for x in ${arr[*]}
do
echo $x
done
讲解:IFS这个是hash内部的域分隔符,需要保留之前的值,在字符串再转数组的时候,自动使用IFS将字符串切分。
或者,如下使用的替换“,”为“ ”方式,然后转化成数组。
arr2=(${s//,/ })
for x in ${arr2[*]}
do
ehco $x
done