美文网首页
shell编程 2022-09-06

shell编程 2022-09-06

作者: 9_SooHyun | 来源:发表于2022-09-05 20:02 被阅读0次

    shell哲学

    shell中一切皆为表达式
    shell没有数据类型的概念,所有的变量值本质上都以字符串进行存储
    The shell does not care about types of variables' data; they may store strings, integers, real numbers - anything you like
    In truth, these are all stored as strings, but routines which expect a number, a boolean, etc can treat them as such.
    至于具体的类型,Shell解释器会根据上下文去确定。eg1.

    #!/bin/sh
    # RESULT是个字符串
    RESULT="false"
    # RESULT此时为bool
    if $RESULT; then
        echo "Is true."
    else
        echo "Is false."
    fi
    

    eg2.

    [root@VM-165-116-centos ~]# a="0"                              
    [root@VM-165-116-centos ~]# if [ "$a" != 0 ]; then echo hhh; fi 
    [root@VM-165-116-centos ~]# if [ "$a" == 0 ]; then echo hhh; fi
    hhh
    [root@VM-165-116-centos ~]# if [ "$a" -eq 0 ]; then echo hhh; fi
    hhh
    [root@VM-165-116-centos ~]# 
    [root@VM-165-116-centos ~]# ((a++))
    [root@VM-165-116-centos ~]# echo $a
    1
    

    sh在解析字符的时候,“;” 等同 回车

    根据表达式的定义,任何表达式都必须有一个值。因此shell一切皆表达式的设计原则,确定了shell在执行任何东西(注意是任何东西,不仅是命令)的时候都会有一个返回值。在shell编程中,这个返回值也限定了取值范围:0-255。0为真(true),非0为假(false)。例如,我们可以使用关键字$?来查看上一个执行命令的返回值

    ➜  ~ echo hhh
    hhh
    ➜  ~ echo $?
    0
    ➜  ~
    

    Accessing Values

    We can access the value stored in a variable by prefixing its name with the dollar sign $. When the shell sees a $, it performs the following actions:

    • Reads the next word to determine the name of the variable.

    • Retrieves the value for the variable. If a value isn't found, the shell uses the empty string "" as the value.

    • Replaces the $ and the name of the variable with the value of the variable.

    基本语法

    quote

    Strong Quoting with the Single Quotes

    When you need to quote several character at once, you could use several backslashes:

    % echo a\ \ \ \ \ \ \ b
    

    (There are 7 spaces between 'a' and 'b'.) This is ugly but works. It is easier to use pairs of quotation marks to indicate the start and end of the characters to be quoted:

    % echo 'a       b'
    

    Inside the single quotes, you can include almost all meta-characters:

    % echo 'What the *heck* is a $ doing here???'
    What the *heck* is a $ doing here???
    

    The above example uses asterisks, dollar signs, and question marks meta-characters. The single quotes should be used when you want the text left alone. Note - If you are using the C shell, the "!" character may need a backslash before it. It depends on the characters next to it. If it is surrounded by spaces, you don't need to use a backslash.

    strong quotes 所见即所得,像golang的`xx`,python的```xx```

    Weak Quotes with the Double Quotes

    Sometimes you want a weaker type of quoting: one that doesn't expand meta-characters like "*" or "?," but does expand variables and does command substitution. This can be done with the double quote characters:

    % echo "Is your home directory $HOME?"
    Is your home directory /home/barnett?
    % echo "Your current directory is `pwd`"
    Your current directory is /home/barnett
    # This next example won't work in the C shell and Bourne shell
    % echo "Your current directory is $(pwd)"
    Your current directory is /home/barnett
    

    字符串

    比较

    对比字符串只能使用==、<、>、!=、-z、-n
    使用[ == ]对比字符串时,需要在首尾附加额外字符以避免报错。如if [ "1"x == "ab"x ]没有了x ,并且1是"",会翻译成if [ == "ab" ],会报语法错误。或者使用[[ ]],就不需要x了。
    使用<或者>时,如果是用[ ],需要用转义符"",如>
    -z, is_zero,判断长度为0
    -n, 判断长度不为0

    截取,替换

    ${strvar:startindex:lenth} 截取
    ${strvar/substring/replacement} 使用replacement, 来代替第一个匹配的substring
    ${strvar//substring/replacement} 使用replacement, 代替所有匹配的substring

    数字

    比较

    对比数字使用既能使用-eq、-ne、-gt、-ge、-lt、-le,也能使用==、<、>、!=

    算数运算

    最简单的做法是,把符合C语言语法的表达式放到(())中。这时,(())中的变量无需带上$

    ➜  ~ echo $((1+2))
    3
    ➜  ~ ((i=1+3))
    ➜  ~ echo $i
    4
    ➜  ~ ((i++))
    ➜  ~ echo $i
    5
    ➜  ~
    

    if 分支结构

    if list; then list; elif list; then list; ... else list; fi
    

    其中,list是若干个使用管道,;&&&||这些符号串联起来的shell命令序列
    可以简单的理解为,if后面跟的就是个shell命令

    if语法中后面最常用的命令是[]。请注意,[]是一个命令——test命令(有对应的二进制文件),也就是说[]类似test的语法糖。这也是shell编程容易犯错的地方之一,如果在一开始接触shell编程的时将[]当成if语句的语法结构而不是命令,写[]的时候就经常会里面不写空格,即:

    # 正确的写法
    if [ $ret -eq 0 ]
    # 错读的写法
    if [$ret -eq 0]
    
    # 另外,以下写法等价
    if [ $ret -eq 0 ] 
    if test $ret -eq 0
    

    test expression可以进行数值、字符和文件三个方面的测试
    test 判断 expression 成立时,退出状态为 0,否则为非 0 值
    test命令也可以简写为[],它的语法为[ expression ]

    while/until 循环结构

       while list-1; do list-2; done
       until list-1; do list-2; done
    

    以下脚本可用于检测目标机器是否ping通

    #!/bin/sh
    
    IPADDR='8.8.8.8'
    INTERVAL=1
    
    while true
    do
        # if ping failed, the command `ping -c 1 $IPADDR &> /dev/null` return 1
        while ping -c 1 $IPADDR &> /dev/null
        do
            echo "$IPADDR ping ok!"
            sleep $INTERVAL
        done
    
        echo "$IPADDR ping error! " 1>&2
    
        # loop sleep until ping succeed
        until ping -c 1 $IPADDR &> /dev/null
        do
            sleep $INTERVAL
        done
    
        echo "$IPADDR ping ok!"
    done
    

    case 分支结构

    case word in [ [(] pattern [ | pattern ] ... ) list ;; ] ... esac
    

    eg.

    case $1 in
            pattern)
            list
            ;;
            pattern)
            list
            ;;
    
            ...
    esac
    
    

    for 循环结构

    for name [ [ in [ word ... ] ] ; ] do list ; done
    

    eg.

    ➜  ~ for i in 1 2 3; do echo $i ; done
    1
    2
    3
    ➜  ~ a=(1 2 3) && for i in ${a[@]}; do echo $i ; done
    1
    2
    3
    ➜  ~
    

    利用@或*,可以将数组扩展成列表
    然后使用#来获取数组元素的个数,格式如下:

    ${#array_name[@]}
    ${#array_name[*]}
    两种形式是等价的
    

    获取命令行/脚本的入参

    使用getopt

    getopt是一个外部命令,支持短选项和长选项

    Each short/long option character in shortopts may be followed by one colon to indicate it has a required argument, and by two colons to indicate it has an optional argument.

    A simple short option is a '-' followed by a short option character.

    • If the option has a required argument, it may be written directly after the option character or as the next parameter (i.e. separated by whitespace on the command line). such as -p3306 or -p 3306
    • If the option has an optional argument, it must be written directly after the option character if present. such as -p3306

    A long option normally begins with '--' followed by the long option name.

    • If the option has a required argument, it may be written directly after the long option name, separated by '=', or as the next argument (i.e. separated by whitespace on the command line). like –-clong=arg or –-clong arg
    • If the option has an optional argument, it must be written directly after the long option name, separated by '=', if present (if you add the '=' but nothing behind it, it is interpreted as if no argument was present; this is a slight bug). like –-clong=arg
    #!/bin/sh
    
    # 处理参数,规范化参数。:表示该opt需要parameter
    ARGS=`getopt -o a:s: --long agentnames:,serverips:,idcfuncname: -- "$@"`
    if [ $? != 0 ];then
        echo "Parse args err. Terminating..."
        exit 1
    fi
    # 重新排列参数顺序,组织成($1, $2, ...)
    eval set -- "${ARGS}"
    # 通过shift和while循环处理参数
    while true
    do
        # $1 表示 option 名,$2 表示option的value
        case $1 in
            # 安装的目标agent
            -a | --agentnames)
                agent_list=($2)
                shift
                ;;
    
            # server ip,包含主备两个
            -s | --serverips)
                # should contain a server master ip and a server slave ip
                server_ips=($2)
                num=${#server_ips[@]}
                if [ ${num} -ne 2 ]; then 
                    echo "the given server ip num is ${num}, not eq 2"
                    exit 1
                fi
                # 使用环境变量 SERVER_IP 配置 server ip
                export SERVER_IP="$2"
                shift
                ;;
            
            # 不同的功能类型可能需要前置执行不同的策略
            --idcfuncname)
                idcfuncname=$2
                case ${idcfuncname} in 
                    ABC )
                        echo ${idcfuncname}
                        ;;
                esac
                shift
                ;;
           
            --)
                shift
                break
                ;;
            *)
                echo "Internal error!"
                exit 1
                ;;
        esac
    shift
    done
    # echo "agent_list: ${agent_list[@]}"
    # echo "server_ips: ${SERVER_IP}"
    # echo "idcfuncname: ${idcfuncname}"
    

    字符串运算

    替换
    # string中第一个old替换为new
    ${string/old/new} 
    
    # string中所有old替换为new
    ${string//old/new}
    
    # string从下标n到结尾的子串
    ${string:n} 
    
    # string从下标n开始长度为m的子串
    ${string:n:m}   
    
    # string从下标0开始长度为m的子串
    ${string::m}    
    

    数组

    普通数组

    a=()         # 空数组
    a=(1 2 3)    # 元素为1,2,3的数组
    echo ${#a[*]}  # 数组长度
    echo ${a[2]}   # 下标为2的元素值(下标从0开始)
    a[1]=0         # 给下标为1的元素赋值
    
    # 遍历数组
    for i in ${a[*]}
    do
        echo ${i}
    done
    
    unset a        # 清空数组
    

    换行符处理

    如果命令执行结果有多行内容,存入变量并打印时换行符会丢失。给echo 加上引号能够保持原样

    [root@VM-centos ~]# echo $(free)  
    total used free shared buff/cache available Mem: 16132456 3998044 1187960 296480 10946452 11708584 Swap: 0 0 0
    [root@VM-centos ~]# echo "$(free)"
                  total        used        free      shared  buff/cache   available
    Mem:       16132456     3987904     1193392      296480    10951160    11707856
    Swap:             0           0           0
    [root@VM-165-116-centos ~]# 
    
    

    并发

    使用&wait可以实现并发执行任务
    wait is a BASH built-in command. From man bash:

        wait [n ...]
            Wait  for each specified process and return its termination sta-
            tus.  Each n may be a process ID or a job  specification;  if  a
            job  spec  is  given,  all  processes in that job's pipeline are
            waited for.  If n is not given, all currently active child  pro-
            cesses  are  waited  for,  and  the return status is zero.  If n
            specifies a non-existent process or job, the  return  status  is
            127.   Otherwise,  the  return  status is the exit status of the
            last process or job waited for.
    

    eg.

    workhard &
    [1] 27408
    workharder &
    [2] 27409
    wait %1 %2 (or wait, or wait 27408 27409)
    

    wait waits for the child process of the current shell to be done

    https://stackoverflow.com/questions/13296863/difference-between-wait-and-sleep

    nohup 和 & 的区别

    &使得命令在后台以子进程方式运行,但是一旦当前shell terminated,所有的这些子进程也会挂,因为它们会收到当前shell发来的SIGHUP信号。而nohup可以捕捉SIGHUP signal,这样这些子进程就永远收不到SIGHUP,即使当前shell terminated它们也一无所知地正常运行下去

    & would run the process in background using a subshell. If the current shell is terminated (say by logout), all subshells are also terminated so the background process would also be terminated.

    when running a command using & and exiting the shell afterwards, the shell will terminate the sub-command with the hangup signal (kill -SIGHUP <pid>). This can be prevented using nohup, as it catches the signal and ignores it so that it never reaches the actual application.

    In case you're using bash, you can use the command shopt | grep hupon to find out whether your shell sends SIGHUP to its child processes or not. If it is off, processes won't be terminated, which means sub-shells can still be alive while shell is terminated

    refers: https://stackoverflow.com/questions/15595374/whats-the-difference-between-nohup-and-ampersand

    相关文章

      网友评论

          本文标题:shell编程 2022-09-06

          本文链接:https://www.haomeiwen.com/subject/kmqwnrtx.html