精华内容
下载资源
问答
  • sh脚本学习总结

    2015-07-10 10:43:38
    开关过程中,免不了要操作linux系统,这是我在学习sh脚本过程中的总结。
  • sh脚本基础学习

    千次阅读 2018-07-23 10:32:18
    1、脚本语言的语法没必要记住,了解就行 ...4、脚本应该 与测试的 java的 class 文件在同一个目录,该脚本(父脚本)里启动了另外一个脚本,在这个子脚本里可以使用父脚本的变量,执行完子脚本后,...

    1、脚本语言的语法没必要记住,了解就行

    2、不懂的时候,查看帮助文档,例如 : man ; 自己总结的文档,简单的语法,命令

    3、我这里使用的是 subline text 写的,可能有些格式不一样,比如 if [ [ ] ] 这个语句

    4、脚本应该 与测试的 java的 class 文件在同一个目录,该脚本(父脚本)里启动了另外一个脚本,在这个子脚本里可以使用父脚本的变量,执行完子脚本后,回到父脚本,可以使用在子脚本里定义的变量。

    5、简单的语法介绍,差不多是一本 语法手册

    # 表示注解
    ~ 代表使用者的 home 目录:cd ~;也可以直接在符号后 # echo ~-/etc/httpd/logs

    ; “连续指令”执行,例子:cd ~/backup ; mkdir startup ;cp ~/.* startup/.

    ;; 连续分号 (Terminator) 专用在 case 的选项,担任 Terminator 的角色。

    case \"$fop\" inhelp)
      echo \"Usage: Command -help -version filename\";;version)
    ;;esac 

    . 一个 dot 代表当前目录,两个 dot 代表上层目录。

    ‘string’ 单引号 (single quote) 被单引号用括住的内容,将被视为单一字串。在引号内的代表变数的$符号,没有作用,也就是说,他被视为一般符号处理,防止任何变量替换。

    heyyou=home

    echo ‘$heyyou’ # 输出 $heyyou ,防止任何变量替换

    “string” 双引号 (double quote) 被双引号用括住的内容,将被视为单一字串。它防止通配符扩展,但允许变量扩展。这点与单引数的处理方式不同。

    heyyou=home

    echo  "$heyyou"  #输出 home

    command 倒引号 (backticks)

    fdv=date +%F

    echo “Today $fdv” 在倒引号内的 date +%F 会被视为指令,执行的结果会带入 fdv 变数中。

    , 逗点 (comma,标点中的逗号) 这个符号常运用在运算当中当做\”区隔\”用途。如下例

      #!/bin/bash
      let t1 = ((a = 5 + 3, b = 7 - 1, c = 15 / 3))
      echo t1= $t1, a = $a, b = $b

    \ 倒斜线 在交互模式下的escape 字元,有几个作用;放在指令前,有取消 aliases的作用;放在特殊符号前,则该特殊符号的作用消失;放在指令的最末端,表示指令连接下一行。

    | 管道 (pipeline) pipeline 是 UNIX 系统,基础且重要的观念。连结上个指令的标准输出,做为下个指令的标准输入。 who | wc -l 善用这个观念,对精简 script 有相当的帮助。

    ! 惊叹号(negate or reverse) 通常它代表反逻辑的作用,譬如条件侦测中,用 != 来代表\”不等于\”

    冒号 在 bash 中,这是一个内建指令:”什么事都不干”,但返回状态值 0。(2)连接符

    ? 问号 (wild card) 在文件名扩展(Filename expansion)上扮演的角色是匹配一个任意的字元,但不包含 null 字元。 # ls a?a1 善用她的特点,可以做比较精确的档名匹配。

    • 星号 (wild card) 相当常用的符号。在文件名扩展(Filename expansion)上,她用来代表任何字元,包含 null 字元。 。

    ** 次方运算 两个星号在运算时代表 “次方” 的意思。

    $ 钱号(dollar sign)

    变量替换(Variable Substitution)的代表符号。 vrs=123echo \”vrs = $vrs\” # vrs = 123 另外,在 Regular Expressions 里被定义为 \”行\” 的最末端 (end-of-line)。这个常用在grep、sed、awk 以及 vim(vi) 当中。

    bash 变 量 的 正 规 表 达 式 b a s h 对 {} 定义了不少用法。以下是取自线上说明的表列

    $ {parameter:-word} # 设置默认值

    ${parameter:=word} # 赋值

    ${parameter:?word}

    ( ) 指令群组 (command group) 用括号将一串连续指令括起来,这种用法对 shell 来说,称为指令群组。如下面的例子:(cd ~ ; vcgh=pwd ;echo $vcgh),指令群组有一个特性,shell会以产生 subshell来执行这组指令。因此,在其中所定义的变数,仅作用于指令群组本身。我们来看个例子 # cat ftmp-01#!/bin/basha=fsh(a=incg ; echo -e \”\n $a \n\”)echo $a#./ftmp-01incgfsh 除了上述的指令群组,括号也用在 array 变数的定义上;另外也应用在其他可能需要加上escape字元才能使用的场合,如运算式。

    (( )) 这组符号的作用与 let 指令相似,用在算数运算上,是 bash 的内建功能。所以,在执行效率上会比使用 let指令要好许多。 #!/bin/bash(( a = 10 ))echo -e \”inital value, a = $a\n\”(( a++))echo \”after a++, a = $a\”

    { } 大括号 (Block of code) 有时候 script 当中会出现,大括号中会夹着一段或几段以\”分号\”做结尾的指令或变数设定。 # cat ftmp-02#!/bin/basha=fsh{a=inbc ; echo -e \”\n$a \n\”}echo \$a#./ftmp-02inbcinbc 这种用法与上面介绍的指令群组非常相似,但有个不同点,它在当前的 shell 执行,不会产生 subshell。 大括号也被运用在 \”函数\” 的功能上。广义地说,单纯只使用大括号时,作用就像是个没有指定名称的函数一般。因此,这样写 script也是相当好的一件事。尤其对输出输入的重导向上,这个做法可精简 script 的复杂度。

    此外,大括号还有另一种用法,如下 {xx,yy,zz,…} 这种大括号的组合,常用在字串的组合上,来看个例子 mkdir {userA,userB,userC}-{home,bin,data} 我们得到 userA-home, userA-bin, userA-data, userB-home, userB-bin,userB-data, userC-home, userC-bin,userC-data,这几个目录。这组符号在适用性上相当广泛。能加以善用的话,回报是精简与效率。像下面的例子 chown root /usr/{ucb/{ex,edit},lib/{ex?.?*,how_ex}} 如果不是因为支援这种用法,我们得写几行重复几次呀!

    [ ] 中括号 常出现在流程控制中,扮演括住判断式的作用。if [ \”$?\” != 0 ]thenecho \”Executes error\”exit1fi 这个符号在正则表达式中担任类似 \”范围\” 或 \”集合\” 的角色 rm -r 200[1234] 上例,代表删除 2001, 2002, 2003, 2004 等目录的意思。

    [[ ]] 这组符号与先前的 [] 符号,基本上作用相同,但她允许在其中直接使用 || 与&& 逻辑等符号。 #!/bin/bashread akif [[ ak>5|| a k > 5 | | ak< 9 ]]thenecho $akfi

    || 逻辑符号 这个会时常看到,代表 or 逻辑的符号。

    && 逻辑符号 这个也会常看到,代表 and 逻辑的符号。

    & 后台工作 单一个& 符号,且放在完整指令列的最后端,即表示将该指令列放入后台中工作。 tar cvfz data.tar.gz data > /dev/null&

    \<…\> 单字边界 这组符号在规则表达式中,被定义为\”边界\”的意思。譬如,当我们想找寻 the 这个单字时,如果我们用 grep the FileA 你将会发现,像 there 这类的单字,也会被当成是匹配的单字。因为 the 正巧是 there的一部份。如果我们要必免这种情况,就得加上 \”边界\” 的符号 grep ‘\’ FileA

    • 加号 (plus) 在运算式中,她用来表示 \”加法\”。 expr 1 + 2 + 3 此外在规则表达式中,用来表示\”很多个\”的前面字元的意思。 # grep ‘10\+9’ fileB109100910000910000931010009#这个符号在使用时,前面必须加上escape 字元。

    • 减号 (dash) 在运算式中,她用来表示 \”减法\”。 expr 10 - 2 此外也是系统指令的选项符号。 ls -expr 10 - 2 在 GNU 指令中,如果单独使用 - 符号,不加任何该加的文件名称时,代表\”标准输入\”的意思。这是 GNU指令的共通选项。譬如下例 tar xpvf - 这里的 - 符号,既代表从标准输入读取资料。 不过,在 cd 指令中则比较特别 cd - 这代表变更工作目录到\”上一次\”工作目录。
      % 除法 (Modulo) 在运算式中,用来表示 \”除法\”。 expr 10 % 2 此外,也被运用在关于变量的规则表达式当中的下列 ${parameter%word}${parameter%%word} 一个 % 表示最短的 word 匹配,两个表示最长的 word 匹配。
      = 等号 (Equals) 常在设定变数时看到的符号。 vara=123echo \” vara = $vara\” 或者像是 PATH 的设定,甚至应用在运算或判断式等此类用途上。

    == 等号 (Equals) 常在条件判断式中看到,代表 \”等于\” 的意思。 if [ vara== v a r a == varb ] …下略

    != 不等于 常在条件判断式中看到,代表 \”不等于\” 的意思。 if [ vara!= v a r a ! = varb ] …下略

    ^ 这个符号在规则表达式中,代表行的 \”开头\” 位置,在[]中也与\”!\”(叹号)一样表示“非”

    21、 输出/输入重导向 > >> < << :> &> 2&> 2<>>& >&2

    文件描述符(File Descriptor),用一个数字(通常为0-9)来表示一个文件。

    常用的文件描述符如下:

    文件描述符 名称 常用缩写 默认值

    0 标准输入 stdin 键盘

    1 标准输出 stdout 屏幕

    2 标准错误输出 stderr 屏幕

    我们在简单地用<或>时,相当于使用 0< 或 1>(下面会详细介绍)。

    • cmd > file 把cmd命令的输出重定向到文件file中。如果file已经存在,则清空原有文件,使用bash的noclobber选项可以防止复盖原有文件。

    • cmd >> file 把cmd命令的输出重定向到文件file中,如果file已经存在,则把信息加在原有文件後面。

    • cmd < file 使cmd命令从file读入

    • cmd << text 从命令行读取输入,直到一个与text相同的行结束。除非使用引号把输入括起来,此模式将对输入内容进行shell变量替换。如果使用<<- ,则会忽略接下来输入行首的tab,结束行也可以是一堆tab再加上一个与text相同的内容,可以参考後面的例子。

    • cmd <<< word 把word(而不是文件word)和後面的换行作为输入提供给cmd。

    • cmd <> file 以读写模式把文件file重定向到输入,文件file不会被破坏。仅当应用程序利用了这一特性时,它才是有意义的。

    • cmd >| file 功能同>,但即便在设置了noclobber时也会复盖file文件,注意用的是|而非一些书中说的!,目前仅在csh中仍沿用>!实现这一功能。

    filename 把文件\”filename\”截断为0长度.# 如果文件不存在, 那么就创建一个0长度的文件(与’touch’的效果相同).

    cmd >&n 把输出送到文件描述符n

    cmd m>&n 把输出 到文件符m的信息重定向到文件描述符n

    cmd >&- 关闭标准输出

    cmd <&n 输入来自文件描述符n

    cmd m<&n m来自文件描述各个n

    cmd <&- 关闭标准输入

    cmd <&n- 移动输入文件描述符n而非复制它。(需要解释)

    cmd >&n- 移动输出文件描述符 n而非复制它。(需要解释)

    注意: >&实际上复制了文件描述符,这使得cmd > file 2>&1与cmd 2>&1 >file的效果不一样。

    1、{} 大括号:

    用法一:通配符扩展

    代码:

    ls my_{finger,toe}s

    这条命令相当于如下命令的组合:

    代码:

    ls my_fingers my_toes
    
    mkdir {userA,userB,userC}-{home,bin,data}

    我们将得到 userA-home, userA-bin, userA-data, userB-home, userB-bin,userB-data,userC-home, userC-bin, userC-data,这几个目录

    用法二:可用于语句块的构造,语句之间用回车隔开。如果你想在某些使用单个语句的地方(比如在AND或OR列表中)使用多条语句,你可以把它们括在花括号{}中来构造一个语句块。

    代码:

    grep -v "$cdcatnum" $strack_file > $temp_file
    
    cat $temp_file > $strack_file
    
    echo
    
    cat -n file1

    (注:以上大括号中的四句命令够成了一个语句块)

    用法三:参数扩展

    代码:

    ${name:-default} 使用一个默认值(一般是空值)来代替那些空的或者没有赋值的变量name;

    ${name:=default}使用指定值来代替空的或者没有赋值的变量name;

    ${name:?message}如果变量为空或者未赋值,那么就会显示出错误信息并中止脚本的执行同时返回退出码1。

    ${#name} 给出name的长度

    ${name%word} 从name的尾部开始删除与word匹配的最小部分,然后返回剩余部分

    ${name%%word} 从name的尾部开始删除与word匹配的最长部分,然后返回剩余部分

    ${name#word} 从name的头部开始删除与word匹配的最小部分,然后返回剩余部分

    ${name##word} 从name的头部开始删除与word匹配的最长部分,然后返回剩余部分

    (注,name为变量名,word为要匹配的字符串)

    用法三在处理字符串和未知变量时,是很有用的。

    2、[] 中括号:

    用法一:通配符扩展:

    允许匹配方括号中任何一个单个字符

    代码:

    ls /[eh][to][cm]*

    相当于执行 ls /etc /home(若有/eom目录,就相当于会执行ls /etc /home /eom)

    注:在mkdir命令下不能扩展

    用法二:用于条件判断符号:

    []符号可理解为指向test命令的一个软链接,所以其用法可完全参照test,将test位置替换为[便可。

    代码:

    if [ "$?" != 0 ] 等价于 if test "$?" != 0
    
    then echo "Executes error"

    3、command 反引号:

    command与$(command)的含义相同,都是返回当前执行命令的结果

    代码:

    #!/bin/sh
    
    for file in $(ls f*.sh);do
    
        lpr $file
    
    done
    
    exit 0
    

    该例实现了扩展f*.sh给出所有匹配模式的文件的名字。

    4、’string’ 单引号 和 “string” 双引号

    双引号:如果想在定义的变量中加入空格,就必须使用单引号或双引号,

    单、双引号的区别在于双引号转义特殊字符而单引号不转义特殊字符

    代码:

    $ heyyou=home
    
    $ echo '$heyyou'
    
    $ $heyyou$没有转义)
    
    eg: $ heyyou=home
    
    $ echo "$heyyou"
    
    $ home (很明显,$转义了输出了heyyou变量的值)

    5、$# 它的作用是告诉你引用变量的总数量是多少;

    代码:

    $$ 它的作用是告诉你shell脚本的进程号;

    ∗ 以 一 个 单 字 符 串 显 示 所 有 的 脚 本 传 递 的 参 数 。 等 价 于 1 2 2 3…….;

    @ @ 与 *基本类似(参见序号7),但在数组赋值时有些不同;

    $? 前一个命令的退出码;

    $- 显示shell使用的当前选项;

    $! 最后一个后台运行的进程ID号。

    $@ 列出所有的参数,各参数用空格隔开

    $*: 列出所有的参数,各参数用环境变量IFS的第一个字符隔开

    6、$((…))语法:对括号内的表达式求值

    代码:

    #!/bin/sh
    
    x=0
    
    hile [ "$x" -ne 10 ];do
    
    echo $x
    
    x=$(($x+1))
    
    done
    
    exit 0

    7、shell中几种特殊的参数变量的引用

    代码:

    1 1 、 2、 3 3 … … {10}、 11 11 、 {12}…… :表示脚本传入的的各个参数,注意当需表示两位数以后的参数时数字要用花括号括起。

    8、命令列表:

    AND列表 statement1 && statement2 && statement3 && …:只有在前面所有的命令都执行成功的情况下才执行后一条命令

    OR列表 statement1 || statement2 || statement3 || …:允许执行一系列命令直到有一条命令成功为止,其后所有命令将不再被执行

    #!/bin/sh
    
    代码: 
    
    touch file_one
    
    rm -f file_two
    
    if [ -f file_one ] && echo "hello" && [ -f file_two ] && echo " there"
    
    then
    
    echo "in if"
    
    else
    
    echo "in else"
    
    fi
    
    exit 0
    

    上例的输出为:

    代码:

    hello
    
    in else

    关于AND列表与OR列表,在逻辑判断中很使用,下面就举一个其最常用的例子:

    代码:

    [ condition ] && command for true || command for false:

    当条件为真时,执行commandfor true ,当条件为假时,执行command for false

    9、: 冒号:内建空指令,返回值为0

    代码:

    $ :
    
    $ echo $?
    
    $ 0
    
    while: (该语句结构可实现一个无限循环)

    10、; 分号: 在 shell 中,担任”连续指令”功能的符号就是”分号”

    代码:

    cd ~/backup ; mkdir startup ; cp ~/.* startup/.

    11、# 井号:表示符号后面的是注解文字,不会被执行;

    代码:

    • 匹配文件名中的任何字符,包括字符串;

    ? 匹配文件名中的任何单个字符。

    ~ 代表使用者的 home 目录

    12、/ 倒斜线:

    放在指令前,有取消 aliases(别名) 的作用;

    放在特殊符号前,则该特殊符号的作用消失;

    放在指令的最末端,表示指令连接下一行(使得回车符无效,只起换行作用)

    13、! 感叹号:

    通常它代表反逻辑的作用,譬如条件侦测中,用 != 来代表”不等于”

    14、** 次方运算:两个星号在运算时代表 “次方” 的意思

    代码:

    let "sus=2**3"
    
    echo "sus = $sus"
    
    $ sus = 8 -
    

    详情可以查看 man test

    if[ -f “somefile” ] :判断是否是一个文件

    if[ -x “/bin/ls” ] :判断/bin/ls是否存在并有可执行权限

    if[ -n “ var] v a r ” ] : 判 断 var变量是否有值

    文件表达式
    if [ -f file ]
    if [ -d … ] 如果目录存在 directory 目录
    if [ -s file ] 如果文件存在且非空
    if [ -r file ] 如果文件存在且可读 read
    if [ -w file ] 如果文件存在且可写 write
    if [ -x file ] 如果文件存在且可执行 execute

    整数变量表达式
    if [ int1 -eq int2 ] 如果int1等于int2 equal
    if [ int1 -ne int2 ] 如果不等于
    if [ int1 -ge int2 ] 如果>= great than equal
    if [ int1 -gt int2 ] 如果>
    if [ int1 -le int2 ] 如果<= less th
    if [ int1 -lt int2 ] 如果<

    例子:

    pid=`ps -eo pid,args | grep apollo-charging-lncmcc-order | grep java | grep -v grep | awk '{print $1}'`
    if [ -n "$pid" ]
    then
        kill -3 ${pid}
        kill ${pid} && sleep 3
        if [  -n "`ps -eo pid | grep $pid`" ]
        then
            kill -9 ${pid}
        fi
        echo "kill pid: ${pid}"
    fi
    展开全文
  • sh脚本语言

    2019-06-28 15:35:46
    玩linux都知道sh脚本的方便,但如何编写sh脚本却是像我这样的新手和菜鸟的难题。能不能编写得出来是一回事,了不了解又是另一回事。抱着好好学习,天天向上的精神,大家有空就看看吧,没有坏处的。下面来抄来一大段...

    原文链接

    玩linux都知道sh脚本的方便,但如何编写sh脚本却是像我这样的新手和菜鸟的难题。能不能编写得出来是一回事,了不了解又是另一回事。抱着好好学习,天天向上的精神,大家有空就看看吧,没有坏处的。下面来抄来一大段sh脚本的语法说明。慢慢看:

    一、开头
    程序必须以下面的行开始(必须方在文件的第一行):
    #!/bin/sh
    符号#!用来告诉系统它后面的参数是用来执行该文件的程序。在这个例子中我们使用/bin/sh来执行程序。
    当编写脚本完成时,如果要执行该脚本,还必须使其可执行。
    要使编写脚本可执行:
    编译 chmod +x filename 这样才能用./filename 来运行

    二、注释
    在进行shell编程时,以#开头的句子表示注释,直到这一行的结束。

    三、变量
    在shell编程中,所有的变量都由字符串组成,并且不需要对变量进行声明。要赋值给一个变量,可以这样写:
    #!/bin/sh
    #对变量赋值:
    a=”hello world”
    # 现在打印变量a的内容:
    echo “A is:”
    echo $a
    有时候变量名很容易与其他文字混淆,比如:
    num=2
    echo “this is the $numnd”
    这并不会打印出”this is the 2nd”,而仅仅打印”this is the “,因为shell会去搜索变量numnd的值,但是这个变量时没有值的。可以使用花括号来告诉shell我们要打印的是num变量:
    num=2
    echo “this is the ${num}nd”
    这将打印: this is the 2nd

    四、环境变量
    由export关键字处理过的变量叫做环境变量。我们不对环境变量进行讨论,因为通常情况下仅仅在登录脚本中使用环境变量。

    五、Shell命令和流程控制
    在shell脚本中可以使用三类命令:
    1)Unix 命令:
    虽然在shell脚本中可以使用任意的unix命令,但是还是有一些相对更常用的命令。这些命令通常是用来进行文件和文字操作的。
    常用命令语法及功能
    echo “some text”: 将文字内容打印在屏幕上
    ls: 文件列表
    wc –l filewc -w filewc -c file: 计算文件行数计算文件中的单词数计算文件中的字符数
    cp sourcefile destfile: 文件拷贝
    mv oldname newname : 重命名文件或移动文件
    rm file: 删除文件
    grep ‘pattern’ file: 在文件内搜索字符串比如:grep ’searchstring’ file.txt
    cut -b colnum file: 指定欲显示的文件内容范围,并将它们输出到标准输出设备比如:输出每行第5个到第9个字符cut -b5-9 file.txt千万不要和cat命令混淆,这是两个完全不同的命令
    cat file.txt: 输出文件内容到标准输出设备(屏幕)上
    file somefile: 得到文件类型
    read var: 提示用户输入,并将输入赋值给变量
    sort file.txt: 对file.txt文件中的行进行排序
    uniq: 删除文本文件中出现的行列比如: sort file.txt | uniq
    expr: 进行数学运算Example: add 2 and 3expr 2 “+” 3
    find: 搜索文件比如:根据文件名搜索find . -name filename -print
    tee: 将数据输出到标准输出设备(屏幕) 和文件比如:somecommand | tee outfile
    basename file: 返回不包含路径的文件名比如: basename /bin/tux将返回 tux
    dirname file: 返回文件所在路径比如:dirname /bin/tux将返回 /bin
    head file: 打印文本文件开头几行
    tail file : 打印文本文件末尾几行
    sed: Sed是一个基本的查找替换程序。可以从标准输入(比如命令管道)读入文本,并将结果输出到标准输出(屏幕)。该命令采用正则表达式(见参考)进行搜索。不要和shell中的通配符相混淆。比如:将linuxfocus替换为 LinuxFocus :cat text.file | sed ’s/linuxfocus/LinuxFocus/’ >newtext.file
    awk: awk 用来从文本文件中提取字段。缺省地,字段分割符是空格,可以使用-F指定其他分割符。
    catfile.txt | awk -F, ‘{print $1 “,” $3}’这里我们使用“,”作为字段分割符,同时打印第一个和第三个字段。如果该文件内容如下: Adam Bor, 34, IndiaKerryMiller, 22, USA命令输出结果为:Adam Bor, IndiaKerry Miller, USA

    2) 概念: 管道, 重定向和 backtick
    这些不是系统命令,但是他们真的很重要。
    1.管道(|)—–将一个命令的输出作为另外一个命令的输入。
    grep “hello” file.txt | wc -l
    在file.txt中搜索包含有”hello”的行并计算其行数。
    在这里grep命令的输出作为wc命令的输入。当然您可以使用多个命令。
    2.重定向—–将命令的结果输出到文件,而不是标准输出(屏幕)。
    3.>—– 写入文件并覆盖旧文件
    4.>>—– 加到文件的尾部,保留旧文件内容。
    5.反短斜线—–使用反短斜线可以将一个命令的输出作为另外一个命令的一个命令行参数。
    命令:
    find . -mtime -1 -type f -print
    用来查找过去24小时(-mtime –2则表示过去48小时)内修改过的文件。如果您想将所有查找到的文件打一个包,则可以使用以下linux 脚本:
    #!/bin/sh
    # The ticks are backticks (`) not normal quotes (‘):
    tar -zcvf lastmod.tar.gz `find . -mtime -1 -type f -print`

    3) 流程控制
    1.if
    “if” 表达式 如果条件为真则执行then后面的部分:
    if ….; then
    ….
    elif ….; then
    ….
    else
    ….
    fi
    大多数情况下,可以使用测试命令来对条件进行测试。比如可以比较字符串、判断文件是否存在及是否可读等等…

    通常用” [ ] “来表示条件测试。注意这里的空格很重要。要确保方括号的空格。
    [ -f “somefile” ] :判断是否是一个文件
    [ -x “/bin/ls” ] :判断/bin/ls是否存在并有可执行权限
    [ -n “$var” ] :判断$var变量是否有值
    [ “$a” = “$b” ] :判断$a和$b是否相等
    执行man test可以查看所有测试表达式可以比较和判断的类型。
    直接执行以下脚本:
    #!/bin/sh
    if [ “$SHELL” = “/bin/bash” ]; then
    echo “your login shell is the bash (bourne again shell)”
    else
    echo “your login shell is not bash but $SHELL”
    fi
    变量$SHELL包含了登录shell的名称,我们和/bin/bash进行了比较。
    快捷操作符
    熟悉C语言的朋友可能会很喜欢下面的表达式:
    [ -f “/etc/shadow” ] && echo “This computer uses shadow passwors”
    这里 && 就是一个快捷操作符,如果左边的表达式为真则执行右边的语句。
    您也可以认为是逻辑运算中的与操作。上例中表示如果/etc/shadow文件存在则打印” This computer uses shadow passwors”。同样或操作(||)在shell编程中也是可用的。这里有个例子:
    #!/bin/sh
    mailfolder=/var/spool/mail/james
    [ -r “$mailfolder” ]‘ ‘{ echo “Can not read $mailfolder” ; exit 1; }
    echo “$mailfolder has mail from:”
    grep “^From ” $mailfolder
    该脚本首先判断mailfolder是否可读。如果可读则打印该文件中的”From” 一行。如果不可读则或操作生效,打印错误信息后脚本退出。这里有个问题,那就是我们必须有两个命令:
    ◆打印错误信息
    ◆退出程序
    我们使用花括号以匿名函数的形式将两个命令放到一起作为一个命令使用。一般函数将在下文提及。
    不用与和或操作符,我们也可以用if表达式作任何事情,但是使用与或操作符会更便利很多。

    2.case
    case :表达式可以用来匹配一个给定的字符串,而不是数字。
    case … in
    …) do something here ;;
    esac
    让我们看一个例子。 file命令可以辨别出一个给定文件的文件类型,比如:
    file lf.gz
    这将返回:
    lf.gz: gzip compressed data, deflated, original filename,
    last modified: Mon Aug 27 23:09:18 2001, os: Unix
    我们利用这一点写了一个叫做smartzip的脚本,该脚本可以自动解压bzip2, gzip 和zip 类型的压缩文件:
    #!/bin/sh
    ftype=`file “$1″`
    case “$ftype” in
    “$1: Zip archive”*)
    unzip “$1” ;;
    “$1: gzip compressed”*)
    gunzip “$1” ;;
    “$1: bzip2 compressed”*)
    bunzip2 “$1” ;;
    *) echo “File $1 can not be uncompressed with smartzip”;;
    esac
    您可能注意到我们在这里使用了一个特殊的变量$1。该变量包含了传递给该程序的第一个参数值。
    也就是说,当我们运行:
    smartzip articles.zip
    $1 就是字符串 articles.zip

    3. selsect
    select 表达式是一种bash的扩展应用,尤其擅长于交互式使用。用户可以从一组不同的值中进行选择。
    select var in … ; do
    break
    done
    …. now $var can be used ….
    下面是一个例子:

    #!/bin/sh
    echo “What is your favourite OS?”
    select var in “Linux” “Gnu Hurd” “Free BSD” “Other”; do
    break
    done
    echo “You have selected $var”
    下面是该脚本运行的结果:
    What is your favourite OS?
    1) Linux
    2) Gnu Hurd
    3) Free BSD
    4) Other
    #? 1
    You have selected Linux

    4.loop
    while-loop 将运行直到表达式测试为真。will run while the expression that we test for is true.
    关键字”break” 用来跳出循环。而关键字”continue”用来不执行余下的部分而直接跳到下一个循环。
    while …; do
    ….
    done

    for-loop表达式查看一个字符串列表 (字符串用空格分隔) 然后将其赋给一个变量:
    for var in ….; do
    ….
    done
    在下面的例子中,将分别打印ABC到屏幕上:
    #!/bin/sh
    for var in A B C ; do
    echo “var is $var”
    done
    下面是一个更为有用的脚本showrpm,其功能是打印一些RPM包的统计信息:
    #!/bin/sh
    # list a content summary of a number of RPM packages
    # USAGE: showrpm rpmfile1 rpmfile2 …
    # EXAMPLE: showrpm /cdrom/RedHat/RPMS/*.rpm
    for rpmpackage in $*; do
    if [ -r “$rpmpackage” ];then
    echo “=============== $rpmpackage ==============”
    rpm -qi -p $rpmpackage
    else
    echo “ERROR: cannot read file $rpmpackage”
    fi
    done
    这里出现了第二个特殊的变量$*,该变量包含了所有输入的命令行参数值。
    如果您运行showrpm openssh.rpm w3m.rpm webgrep.rpm
    此时 $* 包含了 3 个字符串,即openssh.rpm, w3m.rpm and webgrep.rpm.

    5. 引号
    在向程序传递任何参数之前,程序会扩展通配符和变量。这里所谓扩展的意思是程序会把通配符(比如*)替换成合适的文件名,它变量替换成变量值。为了防止程序作这种替换,您可以使用引号:让我们来看一个例子,假设在当前目录下有一些文件,两个jpg文件, mail.jpg 和tux.jpg。

    1.2 编译SHELL脚本
    #ch#!/bin/sh mod +x filename
    cho *.jpg ∪缓螅梢酝ü淙耄?./filename 来执行您的脚本。 【???】
    这将打印出”mail.jpg tux.jpg”的结果。
    引号 (单引号和双引号) 将防止这种通配符扩展:
    #!/bin/sh
    echo “*.jpg”
    echo ‘*.jpg’
    这将打印”*.jpg” 两次。
    单引号更严格一些。它可以防止任何变量扩展。双引号可以防止通配符扩展但允许变量扩展。
    #!/bin/sh
    echo $SHELL
    echo “$SHELL”
    echo ‘$SHELL’
    运行结果为:
    /bin/bash
    /bin/bash
    $SHELL
    最后,还有一种防止这种扩展的方法,那就是使用转义字符——反斜杆:
    echo *.jpg
    echo $SHELL
    这将输出:
    *.jpg
    $SHELL

    6. Here documents
    当要将几行文字传递给一个命令时,here documents(译者注:目前还没有见到过对该词适合的翻译)是一种不错的方法。对每个脚本写一段帮助性的文字是很有用的,此时如果我们四有那个 here documents就不必用echo函数一行行输出。 一个 “Here document” 以 shift by 2
    –) shift;break;; # end of options
    -*) echo “error: no such option $1. -h for help”;exit 1;;
    *) break;;
    esac
    done
    echo “opt_f is $opt_f”
    echo “opt_l is $opt_l”
    echo “first arg is $1”
    echo “2nd arg is $2”
    您可以这样运行该脚本:
    cmdparser -l hello -f — -somefile1 somefile2
    返回的结果是:
    opt_f is 1
    opt_l is hello
    first arg is -somefile1
    2nd arg is somefile2
    这个脚本是如何工作的呢?脚本首先在所有输入命令行参数中进行循环,将输入参数与case表达式进行比较,如果匹配则设置一个变量并且移除该参数。根据unix系统的惯例,首先输入的应该是包含减号的参数.

    第2部分 实例
    现在我们来讨论编写一个脚本的一般步骤。任何优秀的脚本都应该具有帮助和输入参数。并且写一个伪脚本(framework.sh),该脚本包含了大多数脚本都需要的框架结构,是一个非常不错的主意。这时候,在写一个新的脚本时我们只需要执行一下copy命令:
    cp framework.sh myscript
    然后再插入自己的函数。
    让我们再看两个例子:
    二进制到十进制的转换
    脚本 b2d 将二进制数 (比如 1101) 转换为相应的十进制数。这也是一个用expr命令进行数学运算的例子:
    #!/bin/sh
    # vim: set sw=4 ts=4 et:
    help()
    {
    cat <
    b2h — convert binary to decimal
    USAGE: b2h [-h] binarynum
    OPTIONS: -h help text
    EXAMPLE: b2h 111010
    will return 58
    HELP
    exit 0
    }
    error()
    {
    # print an error and exit
    echo “$1”
    exit 1
    }
    lastchar()
    {
    # return the last character of a string in $rval
    if [ -z “$1″ ]; then
    # empty string
    rval=””
    return
    fi
    # wc puts some space behind the output this is why we need sed:
    numofchar=`echo -n “$1” | wc -c | sed ‘s/ //g’ `
    # now cut out the last char
    rval=`echo -n “$1” | cut -b $numofchar`
    }
    chop()
    {
    # remove the last character in string and return it in $rval
    if [ -z “$1″ ]; then
    # empty string
    rval=””
    return
    fi
    # wc puts some space behind the output this is why we need sed:
    numofchar=`echo -n “$1” | wc -c | sed ‘s/ //g’ `
    if [ “$numofchar” = “1” ]; then
    # only one char in string
    rval=””
    return
    fi
    numofcharminus1=`expr $numofchar “-” 1`
    # now cut all but the last char:
    rval=`echo -n “$1” | cut -b 0-${numofcharminus1}`
    }
    while [ -n “$1” ]; do
    case $1 in
    -h) help;shift 1;; # function help is called
    –) shift;break;; # end of options
    -*) error “error: no such option $1. -h for help”;;
    *) break;;
    esac
    done
    # The main program
    sum=0
    weight=1
    # one arg must be given:
    [ -z “$1″ ] && help
    binnum=”$1″
    binnumorig=”$1”
    while [ -n “$binnum” ]; do
    lastchar “$binnum”
    if [ “$rval” = “1” ]; then
    sum=`expr “$weight” “+” “$sum”`
    fi
    # remove the last position in $binnum
    chop “$binnum”
    binnum=”$rval”
    weight=`expr “$weight” “*” 2`
    done
    echo “binary $binnumorig is decimal $sum”
    该脚本使用的算法是利用十进制和二进制数权值 (1,2,4,8,16,..),比如二进制”10″可以这样转换成十进制:
    0 * 1 + 1 * 2 = 2
    为了得到单个的二进制数我们是用了lastchar 函数。该函数使用wc –c计算字符个数,然后使用cut命令取出末尾一个字符。Chop函数的功能则是移除最后一个字符。

    文件循环程序
    或许您是想将所有发出的邮件保存到一个文件中的人们中的一员,但是在过了几个月以后,这个文件可能会变得很大以至于使对该文件的访问速度变慢。下面的 脚本rotatefile可以解决这个问题。这个脚本可以重命名邮件保存文件(假设为outmail)为outmail.1,而对于outmail.1就变成了outmail.2 等等等等…

    #!/bin/sh
    # vim: set sw=4 ts=4 et:
    ver=”0.1″
    help()
    {
    cat <
    rotatefile — rotate the file name
    USAGE: rotatefile [-h] filename
    OPTIONS: -h help text
    EXAMPLE: rotatefile out
    This will e.g rename out.2 to out.3, out.1 to out.2, out to out.1
    and create an empty out-file
    The max number is 10
    version $ver
    HELP
    exit 0
    }
    error()
    {
    echo “$1”
    exit 1
    }
    while [ -n “$1” ]; do
    case $1 in
    -h) help;shift 1;;
    –) break;;
    -*) echo “error: no such option $1. -h for help”;exit 1;;
    *) break;;
    esac
    done
    # input check:
    if [ -z “$1” ] ; then
    error “ERROR: you must specify a file, use -h for help”
    fi
    filen=”$1″
    # rename any .1 , .2 etc file:
    for n in 9 8 7 6 5 4 3 2 1; do
    if [ -f “$filen.$n” ]; then
    p=`expr $n + 1`
    echo “mv $filen.$n $filen.$p”
    mv $filen.$n $filen.$p
    fi
    done
    # rename the original file:
    if [ -f “$filen” ]; then
    echo “mv $filen $filen.1”
    mv $filen $filen.1
    fi
    echo touch $filen
    touch $filen
    这个脚本是如何工作的呢?在检测用户提供了一个文件名以后,我们进行一个9到1的循环。文件9被命名为10,文件8重命名为9等等。循环完成之后,我们将原始文件命名为文件1同时建立一个与原始文件同名的空文件。
    调试
    最简单的调试命令当然是使用echo命令。您可以使用echo在任何怀疑出错的地方打印任何变量值。这也是绝大多数的shell程序员要花费80%的时间来调试程序的原因。Shell程序的好处在于不需要重新编译,插入一个echo命令也不需要多少时间。
    shell也有一个真实的调试模式。如果在脚本”strangescript” 中有错误,您可以这样来进行调试:
    sh -x strangescript
    这将执行该脚本并显示所有变量的值。
    shell还有一个不需要执行脚本只是检查语法的模式。可以这样使用:
    sh -n your_script
    这将返回所有语法错误。

    展开全文
  • sh-简单学习脚本

    千次阅读 2017-05-25 22:03:34
    简易学习脚本语言,学写简单的程序启动脚本

    初识:

    1、脚本语言的语法没必要记住,了解就行

    2、不懂的时候,查看帮助文档,例如 : man ;  自己总结的文档,简单的语法,命令

    3、我这里使用的是 subline text 写的,可能有些格式不一样,比如 if [ [ ] ] 这个语句

    4、脚本应该 与测试的 java的 class 文件在同一个目录,该脚本(父脚本)里启动了另外一个脚本,在这个子脚本里可以使用父脚本的变量,执行完子脚本后,回到父脚本,可以使用在子脚本里定义的变量。

    5、简单的语法介绍,差不多是一本 语法手册    

    #  注解 

    ~ 代表使用者的 home 目录:cd ~;也可以直接在符号后  # echo ~-/etc/httpd/logs 

    ;  "连续指令”执行,例子:cd ~/backup ; mkdir startup ;cp ~/.* startup/. 

    ;; 连续分号 (Terminator)  专用在 case 的选项,担任 Terminator 的角色。  case \"$fop\" inhelp) echo \"Usage: Command -help -version filename\";;version) echo \"version 0.1\" ;;esac 


    .  一个 dot 代表当前目录,两个 dot 代表上层目录。  


    'string' 单引号 (single quote)  被单引号用括住的内容,将被视为单一字串。在引号内的代表变数的$符号,没有作用,也就是说,他被视为一般符号处理,防止任何变量替换。

      heyyou=home 

    echo '$heyyou'  #  输出  $heyyou ,防止任何变量替换


    "string" 双引号 (double quote)  被双引号用括住的内容,将被视为单一字串。它防止通配符扩展,但允许变量扩展。这点与单引数的处理方式不同。 

    heyyou=home

    echo  "$heyyou"  #输出 home 


    `command`  倒引号 (backticks) 

    fdv=`date +%F`

    echo  "Today $fdv"  在倒引号内的 date +%F 会被视为指令,执行的结果会带入 fdv 变数中。 


    , 逗点 (comma,标点中的逗号)  这个符号常运用在运算当中当做\"区隔\"用途。如下例  #!/bin/bashlet \"t1 = ((a = 5 + 3, b = 7 - 1, c = 15 / 3))\"echo \"t1= $t1, a = $a, b = $b\" 


    \\ 倒斜线  在交互模式下的escape 字元,有几个作用;放在指令前,有取消 aliases的作用;放在特殊符号前,则该特殊符号的作用消失;放在指令的最末端,表示指令连接下一行。


    | 管道 (pipeline)  pipeline 是 UNIX 系统,基础且重要的观念。连结上个指令的标准输出,做为下个指令的标准输入。  who | wc -l  善用这个观念,对精简 script 有相当的帮助。 


    ! 惊叹号(negate or reverse)  通常它代表反逻辑的作用,譬如条件侦测中,用 != 来代表\"不等于\" 

    : 冒号  在 bash 中,这是一个内建指令:"什么事都不干",但返回状态值 0。(2)连接符


    ? 问号 (wild card)  在文件名扩展(Filename expansion)上扮演的角色是匹配一个任意的字元,但不包含 null 字元。  # ls a?a1  善用她的特点,可以做比较精确的档名匹配。 


    * 星号 (wild card)  相当常用的符号。在文件名扩展(Filename expansion)上,她用来代表任何字元,包含 null 字元。  。 


    ** 次方运算  两个星号在运算时代表 "次方" 的意思。 


    $ 钱号(dollar sign) 

    变量替换(Variable Substitution)的代表符号。  vrs=123echo \"vrs = $vrs\" # vrs = 123  另外,在 Regular Expressions 里被定义为 \"行\" 的最末端 (end-of-line)。这个常用在grep、sed、awk 以及 vim(vi) 当中。 


    ${} 变量的正规表达式  bash 对 ${} 定义了不少用法。以下是取自线上说明的表列     

     $ {parameter:-word}  # 设置默认值

     ${parameter:=word}  # 赋值

     ${parameter:?word}  


    (   ) 指令群组 (command group)  用括号将一串连续指令括起来,这种用法对 shell 来说,称为指令群组。如下面的例子:(cd ~ ; vcgh=`pwd` ;echo $vcgh),指令群组有一个特性,shell会以产生 subshell来执行这组指令。因此,在其中所定义的变数,仅作用于指令群组本身。我们来看个例子  # cat ftmp-01#!/bin/basha=fsh(a=incg ; echo -e \"\\n $a \\n\")echo $a#./ftmp-01incgfsh  除了上述的指令群组,括号也用在 array 变数的定义上;另外也应用在其他可能需要加上escape字元才能使用的场合,如运算式。 


    (( ))  这组符号的作用与 let 指令相似,用在算数运算上,是 bash 的内建功能。所以,在执行效率上会比使用 let指令要好许多。  #!/bin/bash(( a = 10 ))echo -e \"inital value, a = $a\\n\"(( a++))echo \"after a++, a = $a\" 


    { } 大括号 (Block of code)  有时候 script 当中会出现,大括号中会夹着一段或几段以\"分号\"做结尾的指令或变数设定。  # cat ftmp-02#!/bin/basha=fsh{a=inbc ; echo -e \"\\n $a \\n\"}echo $a#./ftmp-02inbcinbc  这种用法与上面介绍的指令群组非常相似,但有个不同点,它在当前的 shell 执行,不会产生 subshell。  大括号也被运用在 \"函数\" 的功能上。广义地说,单纯只使用大括号时,作用就像是个没有指定名称的函数一般。因此,这样写 script也是相当好的一件事。尤其对输出输入的重导向上,这个做法可精简 script 的复杂度。 

    此外,大括号还有另一种用法,如下  {xx,yy,zz,...}  这种大括号的组合,常用在字串的组合上,来看个例子  mkdir {userA,userB,userC}-{home,bin,data}  我们得到 userA-home, userA-bin, userA-data, userB-home, userB-bin,userB-data, userC-home, userC-bin,userC-data,这几个目录。这组符号在适用性上相当广泛。能加以善用的话,回报是精简与效率。像下面的例子  chown root /usr/{ucb/{ex,edit},lib/{ex?.?*,how_ex}}  如果不是因为支援这种用法,我们得写几行重复几次呀! 


    [   ] 中括号  常出现在流程控制中,扮演括住判断式的作用。if [ \"$?\" != 0 ]thenecho \"Executes error\"exit1fi  这个符号在正则表达式中担任类似 \"范围\" 或 \"集合\" 的角色  rm -r 200[1234]  上例,代表删除 2001, 2002, 2003, 2004 等目录的意思。 


    [[ ]]  这组符号与先前的 [] 符号,基本上作用相同,但她允许在其中直接使用 || 与&& 逻辑等符号。  #!/bin/bashread akif [[ $ak > 5 || $ak< 9 ]]thenecho $akfi 


    || 逻辑符号  这个会时常看到,代表 or 逻辑的符号。 

    && 逻辑符号  这个也会常看到,代表 and 逻辑的符号。 


    & 后台工作  单一个& 符号,且放在完整指令列的最后端,即表示将该指令列放入后台中工作。  tar cvfz data.tar.gz data > /dev/null& 


    \\<...\\> 单字边界  这组符号在规则表达式中,被定义为\"边界\"的意思。譬如,当我们想找寻 the 这个单字时,如果我们用  grep the FileA  你将会发现,像 there 这类的单字,也会被当成是匹配的单字。因为 the 正巧是 there的一部份。如果我们要必免这种情况,就得加上 \"边界\" 的符号  grep '\\' FileA 


    + 加号 (plus)  在运算式中,她用来表示 \"加法\"。  expr 1 + 2 + 3  此外在规则表达式中,用来表示\"很多个\"的前面字元的意思。  # grep '10\\+9' fileB109100910000910000931010009#这个符号在使用时,前面必须加上escape 字元。 


    - 减号 (dash)  在运算式中,她用来表示 \"减法\"。  expr 10 - 2  此外也是系统指令的选项符号。  ls -expr 10 - 2  在 GNU 指令中,如果单独使用 - 符号,不加任何该加的文件名称时,代表\"标准输入\"的意思。这是 GNU指令的共通选项。譬如下例  tar xpvf -  这里的 - 符号,既代表从标准输入读取资料。  不过,在 cd 指令中则比较特别  cd -  这代表变更工作目录到\"上一次\"工作目录。 


    % 除法 (Modulo)  在运算式中,用来表示 \"除法\"。  expr 10 % 2  此外,也被运用在关于变量的规则表达式当中的下列  ${parameter%word}${parameter%%word}  一个 % 表示最短的 word 匹配,两个表示最长的 word 匹配。 


    = 等号 (Equals)  常在设定变数时看到的符号。  vara=123echo \" vara = $vara\"  或者像是 PATH 的设定,甚至应用在运算或判断式等此类用途上。 


    == 等号 (Equals)  常在条件判断式中看到,代表 \"等于\" 的意思。  if [ $vara == $varb ]  ...下略 


    != 不等于  常在条件判断式中看到,代表 \"不等于\" 的意思。  if [ $vara != $varb ]  ...下略 

    ^  这个符号在规则表达式中,代表行的 \"开头\" 位置,在[]中也与\"!\"(叹号)一样表示“非” 

    21、 输出/输入重导向  > >>   <   <<   :>   &>   2&>   2<>>&   >&2  

    文件描述符(File Descriptor),用一个数字(通常为0-9)来表示一个文件。 

    常用的文件描述符如下: 

    文件描述符     名称     常用缩写 默认值 

    0        标准输入 stdin    键盘 

    1        标准输出 stdout     屏幕 

    2      标准错误输出   stderr     屏幕 

    我们在简单地用<或>时,相当于使用 0< 或 1>(下面会详细介绍)。 


    * cmd > file  把cmd命令的输出重定向到文件file中。如果file已经存在,则清空原有文件,使用bash的noclobber选项可以防止复盖原有文件。 

    * cmd >> file  把cmd命令的输出重定向到文件file中,如果file已经存在,则把信息加在原有文件後面。 

    * cmd < file  使cmd命令从file读入 

    * cmd << text  从命令行读取输入,直到一个与text相同的行结束。除非使用引号把输入括起来,此模式将对输入内容进行shell变量替换。如果使用<<- ,则会忽略接下来输入行首的tab,结束行也可以是一堆tab再加上一个与text相同的内容,可以参考後面的例子。 

    * cmd <<< word  把word(而不是文件word)和後面的换行作为输入提供给cmd。 

    * cmd <> file  以读写模式把文件file重定向到输入,文件file不会被破坏。仅当应用程序利用了这一特性时,它才是有意义的。 

    * cmd >| file  功能同>,但即便在设置了noclobber时也会复盖file文件,注意用的是|而非一些书中说的!,目前仅在csh中仍沿用>!实现这一功能。 

    : > filename    把文件\"filename\"截断为0长度.# 如果文件不存在, 那么就创建一个0长度的文件(与'touch'的效果相同). 

    cmd >&n 把输出送到文件描述符n 

    cmd m>&n 把输出 到文件符m的信息重定向到文件描述符n 

    cmd >&- 关闭标准输出 

    cmd <&n 输入来自文件描述符n 

    cmd m<&n m来自文件描述各个n 

    cmd <&- 关闭标准输入 

    cmd <&n- 移动输入文件描述符n而非复制它。(需要解释) 

    cmd >&n- 移动输出文件描述符 n而非复制它。(需要解释) 

    注意: >&实际上复制了文件描述符,这使得cmd > file 2>&1与cmd 2>&1 >file的效果不一样。


    1、{} 大括号:

    用法一:通配符扩展 

    代码: 

    ls my_{finger,toe}s


    这条命令相当于如下命令的组合:

    代码: 

    ls my_fingers my_toes

    mkdir {userA,userB,userC}-{home,bin,data}


    我们将得到 userA-home, userA-bin, userA-data, userB-home, userB-bin,userB-data,userC-home, userC-bin, userC-data,这几个目录

    用法二:可用于语句块的构造,语句之间用回车隔开。如果你想在某些使用单个语句的地方(比如在AND或OR列表中)使用多条语句,你可以把它们括在花括号{}中来构造一个语句块。

    代码: 

    grep -v "$cdcatnum" $strack_file > $temp_file

    cat $temp_file > $strack_file

    echo

    cat -n file1


    (注:以上大括号中的四句命令够成了一个语句块)

    用法三:参数扩展

    代码: 

    ${name:-default} 使用一个默认值(一般是空值)来代替那些空的或者没有赋值的变量name;

    ${name:=default}使用指定值来代替空的或者没有赋值的变量name;

    ${name:?message}如果变量为空或者未赋值,那么就会显示出错误信息并中止脚本的执行同时返回退出码1。

    ${#name} 给出name的长度

    ${name%word} 从name的尾部开始删除与word匹配的最小部分,然后返回剩余部分

    ${name%%word} 从name的尾部开始删除与word匹配的最长部分,然后返回剩余部分

    ${name#word} 从name的头部开始删除与word匹配的最小部分,然后返回剩余部分

    ${name##word} 从name的头部开始删除与word匹配的最长部分,然后返回剩余部分


    (注,name为变量名,word为要匹配的字符串)

    用法三在处理字符串和未知变量时,是很有用的。


    2、[] 中括号: 

    用法一:通配符扩展:

    允许匹配方括号中任何一个单个字符

    代码: 

    ls /[eh][to][cm]*


    相当于执行 ls /etc /home(若有/eom目录,就相当于会执行ls /etc /home /eom)

    注:在mkdir命令下不能扩展

    用法二:用于条件判断符号:

    []符号可理解为指向test命令的一个软链接,所以其用法可完全参照test,将test位置替换为[便可。

    代码: 

    if [ "$?" != 0 ] 等价于 if test "$?" != 0

    then echo "Executes error"


    3、`command` 反引号:

    `command`与$(command)的含义相同,都是返回当前执行命令的结果 

    代码: 

    #!/bin/sh

    for file in $(ls f*.sh);do

        lpr $file

    done

    exit 0


    该例实现了扩展f*.sh给出所有匹配模式的文件的名字。


    4、'string' 单引号 和 "string" 双引号 

    双引号:如果想在定义的变量中加入空格,就必须使用单引号或双引号,

    单、双引号的区别在于双引号转义特殊字符而单引号不转义特殊字符

    代码: 

    $ heyyou=home

    $ echo '$heyyou'

    $ $heyyou ($没有转义)

    eg: $ heyyou=home

    $ echo "$heyyou"

    $ home (很明显,$转义了输出了heyyou变量的值)



    5、$# 它的作用是告诉你引用变量的总数量是多少; 

    代码: 

    $$ 它的作用是告诉你shell脚本的进程号;

    $* 以一个单字符串显示所有的脚本传递的参数。等价于$1 $2 $3.......;

    $@ 与$*基本类似(参见序号7),但在数组赋值时有些不同;

    $? 前一个命令的退出码;

    $- 显示shell使用的当前选项;

    $! 最后一个后台运行的进程ID号。

    $@ 列出所有的参数,各参数用空格隔开

    $*: 列出所有的参数,各参数用环境变量IFS的第一个字符隔开



    6、$((...))语法:对括号内的表达式求值 

    代码: 

    #!/bin/sh

    x=0

    hile [ "$x" -ne 10 ];do

    echo $x

    x=$(($x+1))

    done

    exit 0



    7、shell中几种特殊的参数变量的引用 

    代码: 

    $1、$2、$3……${10}、${11}、${12}…… :表示脚本传入的的各个参数,注意当需表示两位数以后的参数时数字要用花括号括起。


    8、命令列表: 

    AND列表 statement1 && statement2 && statement3 && …:只有在前面所有的命令都执行成功的情况下才执行后一条命令

    OR列表 statement1 || statement2 || statement3 || …:允许执行一系列命令直到有一条命令成功为止,其后所有命令将不再被执行

    #!/bin/sh

    代码: 

    touch file_one

    rm -f file_two

    if [ -f file_one ] && echo "hello" && [ -f file_two ] && echo " there"

    then

    echo "in if"

    else

    echo "in else"

    fi

    exit 0


    上例的输出为:

    代码: 

    hello

    in else


    关于AND列表与OR列表,在逻辑判断中很使用,下面就举一个其最常用的例子:

    代码: 

    [ condition ] && command for true || command for false:


    当条件为真时,执行commandfor true ,当条件为假时,执行command for false


    9、: 冒号:内建空指令,返回值为0 

    代码: 

    $ :

    $ echo $?

    $ 0

    while: (该语句结构可实现一个无限循环)



    10、; 分号: 在 shell 中,担任"连续指令"功能的符号就是"分号" 

    代码: 

    cd ~/backup ; mkdir startup ; cp ~/.* startup/.



    11、# 井号:表示符号后面的是注解文字,不会被执行; 

    代码: 

    * 匹配文件名中的任何字符,包括字符串;

    ? 匹配文件名中的任何单个字符。

    ~ 代表使用者的 home 目录

    12、/ 倒斜线:

    放在指令前,有取消 aliases(别名) 的作用;

    放在特殊符号前,则该特殊符号的作用消失;

    放在指令的最末端,表示指令连接下一行(使得回车符无效,只起换行作用)


    13、! 感叹号:

    通常它代表反逻辑的作用,譬如条件侦测中,用 != 来代表"不等于" 


    14、** 次方运算:两个星号在运算时代表 "次方" 的意思 

    代码: 

    let "sus=2**3"

    echo "sus = $sus"

    $ sus = 8 -



    ## 详情可以查看 man test 

    if[ -f somefile ] :判断是否是一个文件

    if[ -x /bin/ls ] :判断/bin/ls是否存在并有可执行权限

    if[ -n $var ] :判断$var变量是否有值

    • 文件表达式

    if [ -f  file ]  
    if [ -d ...   ]    如果目录存在 directory 目录
    if [ -s file  ]    如果文件存在且非空 
    if [ -r file  ]    如果文件存在且可读 read
    if [ -w file  ]    如果文件存在且可写 write
    if [ -x file  ]    如果文件存在且可执行  execute

    • 整数变量表达式

    if [ int1 -eq int2 ]    如果int1等于int2    equal
    if [ int1 -ne int2 ]    如果不等于    
    if [ int1 -ge int2 ]       如果>=  great than equal
    if [ int1 -gt int2 ]       如果>
    if [ int1 -le int2 ]       如果<=  less th
    if [ int1 -lt int2 ]       如果




    开始学习,从写一个简单的脚本开始,例如命令: java  MainShTest   --config=mycong  

    ZOOBIN="${BASH_SOURCE-$0}"
    echo "BASH_SOURCE-$0= "$ZOOBIN
    echo "所有参数:$@";
    echo "测试:$*"
    #ZOOBIN = "${BASH_SOURCE-$0}" ## 1.${BASH_SOURCE-$0}  
    # ## 获取脚本名称, 全路径 
    # echo $ZOOBIN;
    # ZOOBIN="$(dirname "${ZOOBIN}")"
    # echo $ZOOBIN;
    # 获取目录名
    dirnam="$(dirname "$ZOOBIN")" ## 定格写,()表示指令群组一起执行
    echo "dirnam= "$dirnam
    ZOOBINDIR="$(cd $dirnam; pwd)" ## 当前目录
    echo "pwd= "$ZOOBINDIR;
    if [[ -e "$dirnam/2.sh" ]]; then ## [[]] 判断表达式
    	#statements
      . "$dirnam"/2.sh
    else
      . echo "no"
    fi
    
    
    echo $USER;
    echo "访问上一个文件的变量:"$JAVA;
    ZOOBINDIR="${ZOOBINDIR:-/usr/bin}" ## :- 设置默认值
    echo $ZOOBINDIR;
    values="$#"; ## 变量的总数是多少
    echo $values; # 0
    
    
    until [[ $# -eq 0 ]]; do
    	#statements
    	echo "第一个参数:$1 , 参数个数:$# "
    	if [ "--config"=="$1" ]; then
    		#statements
    		echo "第一个参数:$1";
    		# shift;
    		# echo "--config=$1";
    		echo "--config=$2";
    	fi
    	shift ;
    done
    
    
    which_java="$(which java)";
    echo "which_java=$which_java"
    
    if [[ -n "$JAVA_HOME" ]] && [[ -x "$JAVA_HOME/bin/java" ]];  then
        JAVA="$JAVA_HOME/bin/java"
        echo JAVA;
    elif type -p java; then
        JAVA=java
    else
        echo "Error: JAVA_HOME is not set and java could not be found in PATH." 1>&2
        exit 1
    fi
    
    
    # JMXLOCALONLY="javalocal"
    ## 读取 某个目录下的 所有文件名, 用 : 冒号连接
    if [ "x$JMXLOCALONLY" = "X" ]
    then
        JMXLOCALONLY=false
        echo $JMXLOCALONLY
    else
    	echo "falseone"
    fi
    echo "ZooKeeper JMX enabled by default" >&2 ## &2  标准错误输出   stderr     屏幕
    #文件描述符     名称     常用缩写 默认值 	
    # 0       标准输入 stdin    键盘 	
    # 1        标准输出 stdout     屏幕 	
    # 2      标准错误输出   stderr     屏幕 
    
    
    ostype="$OSTYPE" 
    echo $ostype;
    case $ostype in
     	 *Dwin* )
    	echo "PIPEI WIN*"
    	grep=grep
     		;; ## 结束符合
     	 *) 
    	echo "匹配*"
    	grep="$(which grep)"
    	;;
     esac 
     echo "grep=$grep"
    
    
    # ##可以获取 jdk 的配置 jar 
    # classpath="/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/charsets.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/deploy.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/ext/cldrdata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/ext/dnsns.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/ext/jfxrt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/ext/localedata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/ext/nashorn.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/ext/sunec.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/ext/zipfs.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/javaws.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/jce.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/jfr.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/jfxswt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/management-agent.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/plugin.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/resources.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/rt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/lib/ant-javafx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/lib/dt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/lib/javafx-mx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/lib/jconsole.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/lib/packager.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/lib/sa-jdi.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/lib/tools.jar";
    # ## 加上 项目目录 ,脚本目录 和项目 的目录一致
    # ## 判断文件目录存在否
    # if [[ -d $ZOOBINDIR ]]; then
    # 	#statements
    # 	echo "文件目录存在:$ZOOBINDIR"
    # fi
    # classpath="$classpath:$ZOOBINDIR";
    # echo "classpath==$classpath"
    
    
    
    
    # echo "最后一个命令返回值:"$?
    PIDFIle="$ZOOBINDIR/pidfile.pid" # 保存 进程号的文件
    echo $! >$PIDFIle; # > 输出
    
    
    content=$(cat "$PIDFIle")
    echo "PIDFIle content:$content"
    echo "所有传给脚本的参数:$@" # 
    echo "$$";
    echo "$0"; ## 当前脚本的 名称 ,从这里可以获取 当前目录、父级目录
    
    
    case "`uname`" in # 在""里面 加 `` 命令才会执行 
        Linux)
    		bin_abs_path=$(readlink -f $(dirname $0))
    		echo "Linux::$bin_abs_path"
    		;;
    	*)
    		bin_abs_path=`cd $(dirname $0); pwd`
    		echo "其他abspath: $bin_abs_path "
    		;;
    esac
    
    
    case "$(uname)" in # 
        Linux)
    		bin_abs_path=$(readlink -f $(dirname $0))
    		echo "Linux::$bin_abs_path"
    		;;
    	*)
    		bin_abs_path=`cd $(dirname $0); pwd`
    			echo "其他abspath: $bin_abs_path "
    		;; ## 终结
    esac
    	  
    echo "found canal.pid , Please run stop.sh first ,then startup.sh" 2>&2
    
    
    grep_content="$(file `which java` | grep "64-bit" )"
    echo $grep_content; # 显示命令的内容,判断 Java 的 版本、
    echo $! > "$ZOOBINDIR/pidfile.pid";
    
    
    #项目类名
    MainCLass=MainSHTest
    $JAVA -classpath ".:$classpath" $MainCLass "--config=$config" 1>> "$ZOOBINDIR/test.out" 2>&1 &
    echo $!>"$ZOOBINDIR/pidfile.pid"; # $! 最后一个进程Id 号
    pid="$(cat "$ZOOBINDIR/pidfile.pid")"
    echo $pid;
    kill $pid;
    echo "kill $pid 命令返回值:$?"
    : echo $?;
    
    echo ${p:-word} # 若p 有值,直接输出,否则设置默认值
    echo ${p:=word2} # 若
    echo "再次输出",${p:-word} # 若p 有值,直接输出,否则设置默认值
    ZOOBINDIR="${ZOOBINDIR:-/usr/bin}" ## :- 设置默认值
    echo ${ZOOBINDIR:=word3}
    echo $ZOOBINDIR;







    展开全文
  • 主要介绍了linux编写.sh脚本并赋权限问题,文中给大家提到了Linux为sh脚本文件添加执行权限的方法,需要的朋友跟随脚本之家小编一起学习
  • 在《深度学习caffe–手写字体识别例程(一)》中,我们进行了手写字体识别例程的演练,其中第一步就是用get_mnist.sh脚本文件来获取mnist数据集,这篇文章我们来研究一下这个脚本文件到底做了什么。我们在caffe根...
  • spark学习-29-源代码解析从start-all.sh脚本开始.pdf
  • 关于shell脚本的文章,教程很多。...flash-all.sh脚本我选择的是HiKey版本的刷机文件,在Master分支的device/linaro/hikey/installer目录下可以找到。原始文件只有72行,所以这里加上我的注释都贴出来了#!/bin/

    关于shell脚本的文章,教程很多。但是一般属于工具书类型的,遇到不清楚的再去查。

    这里通过阅读Android的刷机脚本flash-all.sh来学习实际当中的shell是怎么写的。

    flash-all.sh脚本

    我选择的是HiKey版本的刷机文件,在Master分支的device/linaro/hikey/installer目录下可以找到。

    原始文件只有72行,所以这里加上我的注释都贴出来了

    #!/bin/bash
    # $# - 输入参数的个数
    # -eq - 与=类似,都是判断两个操作对象是否相同,但是前者针对整数,后者是字符串和数字都适用
    # 这个判断是:输入的参数个数是否为0,即有没有输入参数
    if [ $# -eq 0 ]
      then
        echo "Provide the right /dev/ttyUSBX specific to recovery device"
        exit    # 退出shell
    fi
    
    # -e - 判断文件是否存在
    # $1 - 第1个输入的参数
    # 这个判断是:第1个参数指定的文件不存在
    if [ ! -e "${1}" ]
      then
        echo "device: ${1} does not exist"
        exit
    fi
    DEVICE_PORT="${1}"
    PTABLE=ptable-aosp-8g.img
    # 如何输入的参数个数大于1,并且第2个参数为"4g",那么PTABLE变量为"ptable-aosp-4g.img"
    if [ $# -gt 1 ]
      then
        if [ "${2}" == '4g' ]
          then
            PTABLE=ptable-aosp-4g.img
        fi
    fi
    
    # 返回当前shell脚本所在的路径
    INSTALLER_DIR="`dirname ${0}`"
    FIRMWARE_DIR="${INSTALLER_DIR}"
    
    # for cases that not run "lunch hikey-userdebu"
    # -z - 变量的长度是否为0,即是否定义了变量${ANDROID_BUILD_TOP}
    # 如果没有定义,则以当前目录为基准进行变量赋值
    if [ -z "${ANDROID_BUILD_TOP}" ]; then
        ANDROID_BUILD_TOP=${INSTALLER_DIR}/../../../../
        ANDROID_PRODUCT_OUT="${ANDROID_BUILD_TOP}/out/target/product/hikey"
    fi
    
    # 如果${DIST_DIR}没有赋值,那么赋值
    if [ -z "${DIST_DIR}" ]; then
        DIST_DIR="${ANDROID_BUILD_TOP}"/out/dist
    fi
    
    #get out directory path
    # 如果参数大于1个,那么遍历
    # 从代码可以发现当前脚本支持2个参数:
    # --out 值是自定义各个img的目录
    # --use-compiled-binaries 如果有这个参数,表示使用编译生成的firmware
    while [ $# -ne 0 ]; do
        case "${1}" in
            --out) OUT_IMGDIR=${2};shift;; # ";"表示连续命令, ";;"表示本次循环结束,相当于高级语言中的continue
            --use-compiled-binaries) FIRMWARE_DIR="${DIST_DIR}";shift;;
        esac
        shift   # shift表示移除第1个参数,即原来的第3个参数变成第2个参数,以此类推
    done    # 完成while循环
    
    # 如果${FIRMWARE_DIR}是编译的目录,判断该目录下fip.bin与l-loader.bin是否存在
    if [[ "${FIRMWARE_DIR}" == "${DIST_DIR}" && ! -e "${DIST_DIR}"/fip.bin && ! -e "${DIST_DIR}"/l-loader.bin ]]; then
        echo "No binaries found at ${DIST_DIR}. Please build the bootloader first"
        exit
    fi
    
    # 如果${OUT_IMGDIR}没有定义,那么使用${ANDROID_PRODUCT_OUT}作为其值
    if [ -z "${OUT_IMGDIR}" ]; then
        if [ ! -z "${ANDROID_PRODUCT_OUT}" ]; then
            OUT_IMGDIR="${ANDROID_PRODUCT_OUT}"
        fi
    fi
    
    # 如果${OUT_IMGDIR}不存在
    if [ ! -d "${OUT_IMGDIR}" ]; then
        echo "error in locating out directory, check if it exist"
        exit
    fi
    
    echo "android out dir:${OUT_IMGDIR}"
    
    # 原本以为"烧写环境没有进行",但是看了代码才发现,这一步也进行了
    # 可参考:http://wiki.lemaker.org/HiKey(LeMaker_version):Quick_Start
    sudo python "${INSTALLER_DIR}"/hisi-idt.py --img1="${FIRMWARE_DIR}"/l-loader.bin -d "${DEVICE_PORT}"
    # set a unique serial number --- 对serialno这个参数不熟悉,之前没见过
    fastboot oem serialno
    fastboot flash ptable "${INSTALLER_DIR}"/"${PTABLE}"
    fastboot flash fastboot "${FIRMWARE_DIR}"/fip.bin
    fastboot flash nvme "${INSTALLER_DIR}"/nvme.img
    fastboot flash boot "${OUT_IMGDIR}"/boot.img
    fastboot flash system "${OUT_IMGDIR}"/system.img
    fastboot flash cache "${OUT_IMGDIR}"/cache.img
    fastboot flash userdata "${OUT_IMGDIR}"/userdata.img
    

    以上中文部分都是我的注释

    从代码可以发现当前脚本支持2个参数:
    –out, 值是自定义各个img的目录
    –use-compiled-binaries 如果有这个参数,表示使用编译生成的firmware

    脚本中用到的指令

    $#
    输入参数的个数

    -eq与==的区别
    两者都是用来判断两个操作对象是否相同,前者只适用于整数,而后者支持数字与字符串

    -gt
    大于

    INSTALLER_DIR=”`dirname ${0}`”
    计算表达式dirname ${0}的值,将其赋值给INSTALLER_DIR

    -z与-n
    -z是变量的字符串长度是否为0
    -n是变量的字符串长度不为0

    -d
    目录是否存在

    -e
    文件是否存在

    ;与;;
    ;表示连续命令
    ;;表示本次循环结束,相当于高级语言中的continue

    -ne
    -eq相反,不等于

    shift
    常用于case中,表示移除第1个参数,即原来的第3个参数变成第2个参数,以此类推

    有意思的if与case
    这两个的语法比较有意思,结束标记是原来的字符串反过来写,即fiesac

    这里列出来的语法非常有限,碰到不清楚的,大家可以自行查询

    参考

    1. shell语法简介
    2. linux shell 判断变量是否为空。-n -z 有什么不同呢?
    3. Shell编程中Shift的用法
    4. shell 基础 $(cd dirname $0;pwd)
    5. linux中shell变量#,@, 0, 1,$2的含义解释
    6. Bash shell 中 “-eq” 与 “=” 等价吗?
    展开全文
  • linux中的sh脚本语法

    2019-02-22 23:22:50
    想要玩好深度学习,除了学习python,linux常写的sh脚本语言 参考:http://tsov.net/sh-script-syntax/ 下面这个对于脚本语言的细节讲解更规范: https://www.cnblogs.com/yinheyi/p/6648242.html    ...
  • rsync.sh脚本文件

    2018-11-05 22:50:50
    rsync自动备份脚本,可以保存起进行学习
  • ubuntu16.04 .sh脚本自启动 (1) 左上角 search your computer中输入startup Applications 找到开机自启动选项点击打开. (2) 点击add -&gt; 在Name中写入你要启动的程序或脚本的名字(你自己定义) Command中写入...
  • 主要介绍了springboot启动脚本start.sh和停止脚本 stop.sh的详细教程,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
  • 1.新建a.sh脚本#/bin/bash set -x temp_rpm=$(rpm -qa | grep $1) for item in $temp_rpm do rpm -e $item --nodeps done 2.添加执行权限并运行(例如删除wine相关的软件包)chmod +x a.sh ./a.sh wine...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 72,625
精华内容 29,050
关键字:

sh脚本学习