zoukankan      html  css  js  c++  java
  • bash cookbook

    image

    简介

    GNU Bash,又名 Bourne Again Shell。它最初发布于 1989 年,并且轻松成长为 Linux 世界中使用最广泛的 shell,甚至常见于其他一些类 Unix 系统当中。

    shell解析命令行的过程以及eval命令

    变量

    shell中的变量都是全局变量,函数中的变量需要使用 local 将其变成局部变量,防止污染函数外的变量。

    不过从严格意义上,Bash没有变量类型。Bash中的变量,在运行的时候会被展开成其对应的值(字符串)。

    静态变量

    在执行过程中不能改变的变量

    readonly passwd_file=”/etc/passwd”
    readonly group_file=”/etc/group”
    

    变量操作

    1. 大小写切换

    ^大写,,小写, ~大小写切换
    重复一次只匹配一个字母,重复两次则应用于所有字母。

    HI=HellO
    echo "$HI" # HellO
    echo ${HI^} # HellO
    echo ${HI^^} # HELLO
    echo ${HI,} # hellO
    echo ${HI,,} # hello
    echo ${HI~} # hellO
    echo ${HI~~} #hELLo
    
    1. 替换运算符
    ${var:-word}    # 如果var存在且非null,返回它的值;否则返回word
    ${var:=word}    # 如果var存在且非null,返回它的值;否则将word赋值给var,并返回var的值 
    ${var:?word}    # 如果var存在且非null,返回它的值;否则显示var:word
    ${var:+word}    # 如果var存在且非null,返回word;否则返回null
    

    冒号(:)可省略

    1. 模式匹配运算符
    ${var#pattern}    匹配前缀(最小匹配),并返回余下内容
    ${var##pattern}   匹配前缀(最大匹配),并返回余下内容
    ${var%pattern}    匹配结尾(最小匹配),并返回余下内容
    ${var%%pattern}   匹配结尾(最大匹配),并返回余下内容
    

    pattern为正则表达式匹配

    数组

    Bash 提供了一维数组变量。任何变量都可以作为一个数组;内建命令 declare 可以显式地定义数组。数组的大小没有上限,也没有限制在连续对成员引用和赋值时有什么要求。数组以整数为下标,从 0 开始。

    1. 定义和初始化数组
    declare -a array            # 显示声明了数组array
    array[key]=value            # array[0]=one
    array=(value1 value2...)    # value的形式都是[subscript]=string,下标和等号可以省略,示例如下。
    array=([0]=value1 [2]=value3 [3]=value[4])
    
    # 关联数组的另一种定义方式
    mydict=(["name"]=guess ["old"]=18 ["favourite"]=coconut ["my description"]="I am a student")
    

    从上面来看数组的定义也是非常灵活多变的,能够满足我们大部分的需求,跟其其它语言最大的区别就是shell中的数组大小没有上限,也可以理解为数组是动态的。

    1. 数组的访问
      数组的任何元素都可以用${array[subscript]}来引用,花括号是必须的,以避免和路径扩展冲突。
      如果 subscript 是@或是*,它扩展为array的所有成员。

    这两种下标只有在双引号中才不同。在双引号中,"${name[*]}"扩展为一个词,由所有数组成员的值组成,用特殊变量IFS的第一个字符分隔数组成员;"${array[@]}"将array的每个成员扩展为一个词。 如果数组没有成员,${name[@]} 扩展为空串。

    示例--"${name[*]}""${array[@]}"的不同

    #!/bin/bash
    
    arr=("one" "two")
    for i in "${arr[*]}"
    do
        echo ${i}
    done
    
    for i in "${arr[@]}"
    do
        echo ${i}
    done
    

    输出如下

    one two
    
    one
    two
    
    1. 数组的删除
      用unset来进行数组的删除
    unset array[2] # 删除第三个成员
    unset array    # 删除整个数组
    
    1. 数组的长度
    ${#arr[@]}
    ${#arr[*]}
    ${#arr}    #错误的。这个获取的是数组第一个成员的长度。
    
    1. 数组的”切片”操作
      获取数组的“子串“用${arr[@]:n:m}来表示,如果没有:m那么就获取从下标n开始到最后一个元素的“字串“,示例如下:
    #!/bin/bash
    
    arr=(one two three four)
    
    echo ${arr[@]:2}
    echo ${arr[@]:1:3}
    echo ${arr[@]:0}
    

    输出如下

    three four
    two three four
    one two three four
    
    1. 关联数组
      shell中还可以声明一个关联数组,普通数组只能使用整数作为数组的索引,而关联数组则使用字符串作为数组的索引。这个关联数组有点像其它语言中的字典。在mac下的bash 中的declare不含这个-A这个参数。
    #!/bin/bash
    
    declare -A array
    
    array["age"]=29
    array["name"]=Yang
    
    # 输出数组的value
    echo "${array[@]}"
    
    # 遍历数组的值
    for a in "${array[@]}"; do
            echo "${a}"
    done
    
    # 通过下标获取元素值
    echo "${array[age]}"
    
    # 输出数组的key
    echo "${!array[@]}"
    
    # 遍历数组的key
    for a in "${!array[@]}"; do
            echo "${a}"
    done
    

    输出

    Yang 29
    Yang
    29
    29
    name age
    name
    

    应用

    1. 判断一个指定的字符串是否在该数组中

      if echo "${ARR[@]}" | grep -w "item_1" &>/dev/null; then
      	echo "Found"
      fi
      

    判断某个元素是否在数组内的几种方法

    四则运算

    1. bash支持的算数运算
    +	 - 	* 	/
    

    有些场景中,乘法符号需要转义

    1. 算数运算实现方式
    let a=a+b			  计算结果无法直接获取,需要赋值后才能使用
    var=$[算数表达式]		计算结果可以直接使用,建议使用
    var=$((算数表达式))	同上
    var=$(expr arg1 arg2 arg3)
    
    1. 浮点数计算

    使用shell内置命令bc

    # 进制转换
    echo "obase=2; ibase=2; 1+1" | bc
    
    # 保留精度
    echo "scale=2;1/2" | bc
    
    # 多行计算
    # v1=$(bc << EOF
    > v2=1
    > v3=2
    > v2+v3
    > EOF
    > )
    # echo $v1
    3
    
    1. 增强型赋值
    += 	-=	 *= 	/= 	%=
    
    let var=var1+=1
    let var++	# 自增
    let var--	# 自减
    

    条件测试

    分类

    • 整数测试
    • 文件测试
    • 字符测试

    真返回值为true或者false

    条件比较测试表达式有以下三种

    [ expression ]
    [[ expression ]]
    test expression
    

    整数测试

    -eq:=
    -ne:!=
    -gt:>
    -lt:<
    -ge:>=
    -le:<=
    

    文件测试

    1. 存在性测试
    -e file	是否存在
    -f file	是否为普通文件
    -d file	是否为目录
    
    1. 权限测试
    -r file	指定文件对当前用户是否可读
    -w file	指定文件对当前用户是否可写
    -x file	指定文件对当前用户是否可执行
    -u file	当前用户是否是文件的属主
    -g file	当前用户是否是文件的属组
    
    1. 文件大小测试
    -s file	文件存在且非空
    

    字符测试

    [[ ]]或者[ ]都可以

    1. 等值比较

    =或者==注意:等号两端要有空格

    1. 不等比较

    !=注意:等号两端要有空格

    1. 是否为空测试
    -z string:测试字符串是否为空,空为真;
    -n string:测试字符串是否不为空,不空为真;
    ~= 左侧的字符串能否被右侧的PATTERN所匹配
    

    组合条件测试

    主要分两类

    COMMAND1 && COMMAND2
    COMMAND1 || COMMAND2
    ! COMMAND1
    
    [ EXPRESSION1 -a EXPRESSION2 ]
    [ EXPRESSION1 -o EXPRESSION2 ]
    [ ! EXPRESSION1 ]
    

    示例,传递一个用户名参数给脚本,判断此用户的用户名跟其基本组的组名是否一致,并将结果显示出来。

    #!/bin/bash
    if [ $# -ne 1 ]; then
        echo "Please enter a argument."
        exit 1
    elif ! id $1 &> /dev/null; then
        echo "No such user."
        exit 2
    elif [ $1 == $(id $1 -g -n) ]; then
        echo "Same."
    else
        echo "Diffrent."
    fi
    

    选择语句

    case SWITCH in
    value1)
        statement1
        ...
        ;;
    value2)
        statement2
        ...
        ;;
    *)
        statement3
        ...
        ;;
    esac
    

    循环语句

    循环需要有进入条件和退出条件

    for--有限循环

    # 形式1
    for 变量 in 列表;do
        循环体
    done
    
    # 形式2
    for (( expr1 ; expr2 ; expr3 )); do 
      循环体
    done
    

    生成整数列表

    {1..100}
    `seq [起始数] [步进长度] 结束数`
    

    示例

    for i in `seq 1 $a`; do echo $i; done
    

    while--无线循环

    条件满足则执行循环

    while CONDITION; do
      循环体
    done
    

    示例

    while的特殊用法一,死循环

    while :; do
    	循环体
    done
    

    while的特殊用法二,按行读取文件

    while read LINE; do
    	循环体
    done < /PATH/TO/SOMEFILE
    

    until

    满足条件则结束循环

    until CONDITION; do
      循环体
    done
    

    continue

    提前结束本轮循环,进入下一轮循环

    函数

    • 通过位置传递参数
    • 通过 echo 返回值
    • 通过return 返回状态码
    1. 定义

      function func_name(){
          ...函数体...
      }
      
    2. 直接通过函数名调用,函数名后不用加括号。

    格式化输出 echo printf

    echo

    -n 不换行输出
    -e 支持扩展
    

    printf

    使用printf可以输出更规则更格式化的结果。它引用于C语言的printf命令,但是有些许区别。
    printf可以指定字符串的宽度、实现左对齐(使用减符号-)、右对齐(默认的)、格式化小数输出等。

    使用printf最需要注意的两点是:

    • printf默认不在结尾加换行符,它不像echo一样,所以要手动加“ ”换号;
    • printf只是格式化输出,不会改变任何结果,所以在格式化浮点数的输出时,浮点数结果是不变的,仅仅只是改变了显示的结果。
    > printf "%-5s %-10s %-4s
    " No Name Mark     # 三个%分别对应后面的三个参数
    > printf "%-5s %-10s %-4.2f
    " 1 Sarath 80.34 # 减号“-”表示左对齐
    > printf "%-5s %-10s %-4.2f
    " 2 James 90.998 # 5s表示第一个参数占用5个字符
    > printf "%-5s %-10s %-4.2f
    " 3 Jeff 77.564
    

    其他

    位置参数 $@ $* $#

    $* 表示从1开始所有位置的参数,如果扩展发生在双引号内,即"$*",则扩展包含每个参数值的单词,每个参数值用特殊表量IFS的第一个字符分割;也就是说,"$*"等价于"$1c$2c...",其中,c时特殊变量IFS的第一个字符。如果变量IFS没有定义,则参数之间默认用空格分割。

    $@也扩展为从1开始的所有位置参数。但当它的扩展发生在双引号内时,每个参数都扩展为分割的单词。即:"$@"等价于"$1"、"$2" ...。参数@与*之间的区别会在for循环中体现出来。循环时用 $@

    如果命令运行失败让脚本退出执行

    set -o errexit
    set -e
    

    若有用未设置的变量即让脚本退出执行

    set -o nounset 
    set -u
    

    BASH中用 read 实现“按任意键继续”

    read -s -n1 -p "按任意键继续 ... "
    

    参数说明
    -s 指输入的字符屏幕上不可件,应该说可见,但由于和终端的背景色相同,故不可见
    -n 1 表示仅接收1个字符,按回车键也属于一个字符
    -p 是指提示符

  • 相关阅读:
    P3146 [USACO16OPEN]248
    P2590 [ZJOI2008]树的统计
    P3379 【模板】最近公共祖先(LCA)
    P2253 好一个一中腰鼓!
    数组中出现次数超过一半的数字
    字符串的排列
    二叉搜索树与双向链表
    二叉搜索树的后序遍历序列
    从上往下打印二叉树
    顺时针打印矩阵
  • 原文地址:https://www.cnblogs.com/hiyang/p/12611836.html
Copyright © 2011-2022 走看看