如何书写shell脚本

关于Linux基础命令,可以查看另一篇博文 Linux Shell脚本攻略笔记

以下内容,主要是,了解书写shell脚本所需要的大部分知识,主要内容来自于书籍和网络

目的是,能快速书写出需要的shell脚本

开始

version 0.1 2014-01-12 基本内容, 完成度30%


资源

google shell style guide

Linux Shell脚本攻略笔记

Linux Shell编程实战技巧

Bash编程易犯的错误 1234

关于shell脚本编程的10个最佳实践

Bash Pitfalls


第一部分 一些概念

标准IO

文件描述符0 标准输入 默认键盘1 标准输出 默认终端2 标准错误 默认终端

重定向

> 输出重定向>> 追加到输出重定向< 输入重定向<< 追加到输入重定向ls -l > /tmp/acmd >/dev/null 2>&1 #输出到垃圾桶

管道

前后连接两个命令ls -l | grep test

引号

双引号:可以除了字符$`\外地任何字符或字符串单引号:忽略任何引用值,将引号里的所有字符作为一个字符串 $var 不能被解析反引号:设置系统命令输出到变量

shell脚本识别三种基本命令:内建命令,shell函数和外部命令

基本的命令查找:shell会沿着查找路径$PATH来寻找命令

echo $PATH可以在.profile文件中修改export PATH=$PATH:$HOME/bin

and/or

expression1 && expression2 && expression3只有前面一条命令执行成功,才执行下一条expression1执行成功,才执行expression2串联的expression1 || expression2 || expression3执行命令,直到有一条成功为止

第二部分 shell脚本

首行声明使用bash(声明脚本执行解释器)

#!/bin/bash# do somethingexit 0/n

运行

sh xx.shbash xx.sh #大部分情况下两个一样,某些命令只有bash有,只能用这个orchmod u+x xx.sh./xx.sh

调试

#查看运行时,每个命令回显,执行之后回显sh -x xx.sh#执行之前回显sh -v xx.sh#检查语法错误,不执行sh -n xx.sh#如果使用了未定义的变量,给出错误信息sh -u xx.sh#调试部分脚本echo "Hello $USER,"set -xecho "Today is $(date %Y-%m-%d)"set +x

判断执行结果

N=$?  #0 <= N <= 2550 无错误,正常执行结束非0 异常    1-125命令不成功退出    126命令成功,但文件无法执行    127命令找不到    >128命令因收到信号而死亡

获取目录名和文件名

# To find base directoryAPP_ROOT=`dirname "$0"`# To find the file namefilename=`basename "$filepath"`# To find the file name without extensionfilename=`basename "$filepath" .html`e.g.BASEDIR=$(dirname $0)cd $BASEDIRCURRENT_DIR=`pwd`

日期

TODAY=`date +%Y%m%d`DAY_1_AGO=`date -d "$TODAY 1 days ago" +%Y%m%d`常用接受日期/使用默认日期处理if [ -n "$1" ]then    TODAY="$1"else    TODAY=`date +%Y%m%d`fi

crontab调度

查看crontab -l编辑crontab -e格式* * * * * command_path字段      含义     范围1        分钟         0-592        小时         0-233        日期         1-314        月份         1-125        星期几,0=周日   0-66        具体命令,可以是调用脚本*任意时刻n1,n2  分割,n1和n2*/n  每隔n单位n1-n2   时段,一个时段内0 */2 * * * sh run.sh     每隔两小时20 7 * * * sh run.sh 每天7:200 1,5 * * * sh run.sh 每天1点和5点* * * * * sh run.sh 每分钟执行一次

第三部分 变量1.变量赋值

varname="value"varname=`expression`注意,等号两边必须不能包含空格

2.分类

四种变量:环境变量、本地变量、位置变量、特定变量参数环境变量可作用于所有子进程本地变量在用户现在的shell 生命期的脚本中使用,仅存在于当前进程位置变量:作为程序参数特定变量:特殊作用

3.环境变量

设置MYVAR="test"expirt MYVARorexport MYVAR="test"只读MYVAR="test"readonly MYVARorreadonly MYVAR="test"显示export -penv #查看所有环境变量$MYVAR #获取消除unset MYVAR

4.本地变量

设置LOCAL_VAR="test"orLOCAL_VAR="test"readonly LOCAL_VAR #设置只读还可以使用declare命令定义

5.位置变量

$0 脚本名称$# 传递到脚本参数个数$$ shell脚本运行当前进程ID$? 退出状态$N N>=1,第n个参数

6.字符串处理

长度

${#VARIABLE_NAME} 可以给出字符串的长度。if [ ${#authy_api_key} != 32 ]then    return $FAILfi

拼接字符串

echo "$x$y"

字符串切片

${变量名:起始:长度}得到子字符串$ test='I love china'$ echo ${test:5}e china$ echo ${test:5:10}e chinastr="hello world"echo ${str:6}  # ${var:offset:length}

字符串替换

${变量/查找/替换值} 一个“/”表示替换第一个,”//”表示替换所有,当查找中出现了:”/”请加转义符”\/”表示echo ${str/foo/bar} #首个echo ${str//foo/bar} #所有

正则匹配

if [[ $str =~ [0-9]+\.[0-9]+ ]]

7.数值处理

自增

a=1a=`expr a + 1`ora=1let a++let a+=2

let

no1=4no2=5let result=no1+no2

expr

result=`expr 3 + 4`result=$(expr $no1 + 5)

其他

result=$[ no1 + no2 ]result=$[ $no + 5 ]result=$(( no1 + 5 ))

浮点数

echo "4 * 0.56" | bc设定精度echo "scale=2;3/8" | bc进制转换echo "obase=2;100" | bc平方echo "sqrt(100)" | bc

数组和map


第四部分 控制流1.条件测试

语法

test condition[ condition ] #注意两边加空格$? #获取判断结果,0表示condition=true

条件测试中的逻辑

-a 与-o 或!  非&&||if [ -n "$str" -a -f "$file" ]if [ -n "$str" ] && [ -f "$file" ]

字符串测试

=   两字符串相等!=  两字符串不等-z  空串 [zero]-n  非空串 [nozero][ -z "$EDITOR" ][ "$EDITOR" = "vi" ]

数值测试

-eq  数值相等(equal)-ne  不等(not equal)-gt  A>B(greater than)-lt  A<B(less than)-le  A<=B(less、equal)-ge  A>=B(greater、equal)N=130[ "$N" -eq 130 ]

文件测试

-d目录-f 普通文件(Regular file)-e 文件存在-z 文件长度=0-s 文件长度大于0,非空-b 块专用文件-c 字符专用文件-L 符号链接-r Readable(文件、目录可读)-w Writable(文件、目录可写)-x Executable(文件可执行、目录可浏览)-g 如果文件的set-group-id位被设置则结果为真-u 文件有suid位设置

2.分支if-else/case

if-else语法

if condition1then    //do thing aelif condition2then    //do thing belse    //do thing cfiorif condition; then# do somethingfi

case语法

case $VAR in    1)        echo "abc"        ;;    2|3|4)        echo "def"        ;;    *)        echo "last"        ;;esac

3.循环for/while/until

for语法

for VARIABLE in 1 2 3 4 5 .. Ndo    //commandsdonefor OUTPUT in $(Linux-Or-Unix-Command-Here)do    //commands on $OUTPUTdone#bashfor (( EXP1; EXP2; EXP3 ))do    //commandsdone例子for i in 1 2 3 4 5; do    echo $idonefor i in `seq 1 5`; do    echo $idone#!/bin/bashecho "Bash version"for i in $(seq 1 2 20)do   echo "Welcome $i times"donefor i in {1..5}; do    echo $idone#!/bin/bashecho "Bash version"for i in {0..10..2}do    echo "Welcome $i times"donefor ((i=1; i<=10; i++)); do    echo $idone#无限循环#!/bin/bashfor (( ; ; ))do   echo "infinite loops [ hit CTRL+C to stop]"done

while

while conditiondo    //do somethingdoneCOUNTER=0while [ $COUNTER -lt 5 ]do    COUNTER=`expr $COUNTER + 1`    echo $COUNTERdone无限循环while [ 1 ]do    //done

until

#执行命令,直到条件为真,至少执行一次,可以用来做监控,condition每次都回去检查until conditiondo    //do somethingdone

break/continue

break允许跳出循环,通常在进行一些列处理后退出循环或case语句若多重循环,可指定跳出的循环个数,如跳出两重循环  break 2continue不会跳出循环,只是跳过此循环步命令是程序在本循体内忽略下面的语句,从循环头开始执行

第五部分 函数1.函数定义

function func_name() {}func_name() {    //do some thing}

注意

函数名,在脚本中必须唯一函数必须,先定义,后使用

return

function equal() {    return 1}equalecho $? #got 1

2.参数传递

#位置参数function copyfile() {    cp $1 $2    return $?}调用copyfile /tmp/a /tmp/bor获取返回值result=`copyfile /tmp/a /tmp/b`

位置参数

$1 - $9,当参数超过10个时,需要使用${10}$# 参数个数$* 将所有参数视为一个字符串="$1 $2 ..."$@ 将所有参数视为个体="$1" "$2" "$3"

3.返回值和退出状态

#返回值function func_a() {    return 1}result=`func_a`if [ result != 0 ]then    echo "Error"fi#退出状态function func_b() {    //do something}func_bif [ $? -eq 0 ]then    echo "Success"else    echo "Error"fi#更简洁if func_b; then    echo "Success"else    echo "Error"fifunc_b && echo "Success" || echo "Error"

第四部分 高级

bash中参数展开-展开运算符

${varname:-word} 如果变量未定义,返回默认值.  ${noexist:-0}返回0${varname:=word} 如果变量未定义,设置变量为默认值  ${noexists:=0}; echo ${noexists}; 得到0${varname:?message} 若未定义,显示varname:message并退出当前的命令或脚本${varname:+word} 若存在且非null,返回word,否则返回null

模式匹配

${variable##pattern}${variable%pattern}

第五部分 其他

读文件

while read -r line; do    echo $linedone < file保留首尾字符while IFS= reaad -r line; do    echo $linedone

一些内置命令

:空命令,类似python的pass.相当于source\用于跨行命令echo输出,类似printlnexecexit n脚本以n作为退出码退出export设置或显示环境变量expr简单计算x=`expr $x + 1`x=$(expr $x + 1)letd=111let d=$d+1; echo $d112printf格式化输出return函数返回setshift所有参数变量左移一个位置unset从环境变量中删除变量或函数

BP:

使用$() 代替反引号``$(()) 代替expr运算符

bash

GNU Bash 主页http://www.gnu.org/software/bash/GNU Bash 手册http://www.gnu.org/software/bash/manual/

更多的特性

$((3 + 4))          而不需要 expr 3 + 4, 算术展开/usr/{bin,local/bin}  而不需要 /usr/bin /usr/local/bin${str/src/dst}       而不需要 echo $str | sed ”s/$src/$dst/”

更方便的语法

for (( expr1; expr2; expr3 )); do        commandsdonefor (( i = 0; i < 100; i++ )); do … doneecho a{b,c,d}e  ==> abe ace ade

表达式求值

$[]    []$中间可以加表达式  eg: echo $[$a+$b]$(())   (())中间可以加表达式。Eg: total=$(($a*$b))

正则

bash的正则表达式str='hello, world'if [[ $str =~ '\s+world$' ]]; then    echo match!fiif echo "$str" | grep -E '[ ]+world$'; then    echo match!fi

获取软连接指向的真实文件名

#注:有些系统没有这个命令readlink /usr/bin/python

增加debug

function debug() {    if [[ $DEBUG ]]    then        echo ">>> $*"    fi}# For any debug messagedebug "Trying to find config file"还有来自于一些很酷的Geeks的单行debug函数:function debug() { ((DEBUG)) && echo ">>> $*"; }function debug() { [ "$DEBUG" ] && echo ">>> $*"; }

将执行日志全部写到某个文件

exec >>"$LOGPATH"/xx.log.$TODAY 2>&1#begin of code

如何书写shell脚本

相关文章:

你感兴趣的文章:

标签云: