美文网首页藏兵谷Linux基础
全宇宙最全的bash 脚本常见用法总结!

全宇宙最全的bash 脚本常见用法总结!

作者: 婉妃 | 来源:发表于2019-05-29 17:17 被阅读279次

    前言

    bash外部流程和程序的纯替代品集合。该bash脚本语言是更强大的比人们认识和大多数任务能够不依赖于外部程序来完成。

    调用外部进程bash是昂贵的,过度使用将导致明显的减速。使用内置方法(如果适用)编写的脚本和程序将更快,需要更少的依赖性,并且可以更好地理解语言本身。

    本书的内容为解决编写程序和脚本时遇到的问题提供了参考bash。示例是函数格式,展示了如何将这些解决方案合并到代码中。

    STRINGS

    修剪字符串中的前导和尾随空格

    这是一个替代品sedawkperl和其他工具。
    下面的函数通过查找所有前导和尾随空格并
    从字符串的开头和结尾删除它来工作。使用:内置代替临时变量。

    示例功能:

    trim_string() {
        # Usage: trim_string "   example   string    "
        : "${1#"${1%%[![:space:]]*}"}"
        : "${_%"${_##*[![:space:]]}"}"
        printf '%s\n' "$_"
    }
    
    

    用法示例:

    $ trim_string "    Hello,  World    "
    Hello,  World
    
    $ name="   John Black  "
    $ trim_string "$name"
    John Black
    
    

    修剪字符串中的所有空格并截断空格

    这是一个替代品sedawkperl和其他工具。
    下面的函数通过滥用单词拆分来创建一个
    没有前导/尾随空格和截断空格的新字符串。

    示例功能:

    # shellcheck disable=SC2086,SC2048
    trim_all() {
        # Usage: trim_all "   example   string    "
        set -f
        set -- $*
        printf '%s\n' "$*"
        set +f
    }
    
    

    用法示例:

    $ trim_all "    Hello,    World    "
    Hello, World
    
    $ name="   John   Black  is     my    name.    "
    $ trim_all "$name"
    John Black is my name.
    
    

    在字符串上使用正则表达式

    的结果bash的正则表达式匹配可以用于代替sed用于
    大量的用例。

    CAVEAT:这是为数不多的平台相关bash功能之一。
    bash将使用用户系统上安装的任何正则表达式引擎。
    如果要兼容,请坚持使用POSIX正则表达式功能。

    CAVEAT:此示例仅打印第一个匹配组。使用
    多个捕获组时,需要进行一些修改。

    示例功能:

    regex() {
        # Usage: regex "string" "regex"
        [[ $1 =~ $2 ]] && printf '%s\n' "${BASH_REMATCH[1]}"
    }
    
    

    用法示例:

    $ # Trim leading white-space.
    $ regex '    hello' '^\s*(.*)'
    hello
    
    $ # Validate a hex color.
    $ regex "#FFFFFF" '^(#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3}))$'
    #FFFFFF
    
    $ # Validate a hex color (invalid).
    $ regex "red" '^(#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3}))$'
    # no output (invalid)
    
    

    脚本中的示例用法:

    is_hex_color() {
        if [[ $1 =~ ^(#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3}))$ ]]; then
            printf '%s\n' "${BASH_REMATCH[1]}"
        else
            printf '%s\n' "error: $1 is an invalid color."
            return 1
        fi
    }
    
    read -r color
    is_hex_color "$color" || color="#FFFFFF"
    
    # Do stuff.
    
    

    在分隔符上拆分字符串

    这是一个另类cutawk和其他工具。

    示例功能:

    split() {
       # Usage: split "string" "delimiter"
       IFS=$'\n' read -d "" -ra arr <<< "${1//$2/$'\n'}"
       printf '%s\n' "${arr[@]}"
    }
    
    

    用法示例:

    $ split "apples,oranges,pears,grapes" ","
    apples
    oranges
    pears
    grapes
    
    $ split "1, 2, 3, 4, 5" ", "
    1
    2
    3
    4
    5
    
    # Multi char delimiters work too!
    $ split "hello---world---my---name---is---john" "---"
    hello
    world
    my
    name
    is
    john
    
    

    将字符串更改为小写

    CAVEAT:需要bash4+

    示例功能:

    lower() {
        # Usage: lower "string"
        printf '%s\n' "${1,,}"
    }
    
    

    用法示例:

    $ lower "HELLO"
    hello
    
    $ lower "HeLlO"
    hello
    
    $ lower "hello"
    hello
    
    

    将字符串更改为大写

    CAVEAT:需要bash4+

    示例功能:

    upper() {
        # Usage: upper "string"
        printf '%s\n' "${1^^}"
    }
    
    

    用法示例:

    $ upper "hello"
    HELLO
    
    $ upper "HeLlO"
    HELLO
    
    $ upper "HELLO"
    HELLO
    
    

    修剪字符串中的引号

    示例功能:

    trim_quotes() {
        # Usage: trim_quotes "string"
        : "${1//\'}"
        printf '%s\n' "${_//\"}"
    }
    
    

    用法示例:

    $ var="'Hello', \"World\""
    $ trim_quotes "$var"
    Hello, World
    
    

    从字符串中删除所有模式实例

    示例功能:

    strip_all() {
        # Usage: strip_all "string" "pattern"
        printf '%s\n' "${1//$2}"
    }
    
    

    用法示例:

    $ strip_all "The Quick Brown Fox" "[aeiou]"
    Th Qck Brwn Fx
    
    $ strip_all "The Quick Brown Fox" "[[:space:]]"
    TheQuickBrownFox
    
    $ strip_all "The Quick Brown Fox" "Quick "
    The Brown Fox
    
    

    从字符串中剥离第一次出现的模式

    示例功能:

    strip() {
        # Usage: strip "string" "pattern"
        printf '%s\n' "${1/$2}"
    }
    
    

    用法示例:

    $ strip "The Quick Brown Fox" "[aeiou]"
    Th Quick Brown Fox
    
    $ strip "The Quick Brown Fox" "[[:space:]]"
    TheQuick Brown Fox
    
    

    从字符串开始剥离图案

    示例功能:

    lstrip() {
        # Usage: lstrip "string" "pattern"
        printf '%s\n' "${1##$2}"
    }
    
    

    用法示例:

    $ lstrip "The Quick Brown Fox" "The "
    Quick Brown Fox
    
    

    从字符串末尾剥去图案

    示例功能:

    rstrip() {
        # Usage: rstrip "string" "pattern"
        printf '%s\n' "${1%%$2}"
    }
    
    

    用法示例:

    $ rstrip "The Quick Brown Fox" " Fox"
    The Quick Brown
    
    

    百分比编码字符串

    示例功能:

    urlencode() {
        # Usage: urlencode "string"
        local LC_ALL=C
        for (( i = 0; i < ${#1}; i++ )); do
            : "${1:i:1}"
            case "$_" in
                [a-zA-Z0-9.~_-])
                    printf '%s' "$_"
                ;;
    
                *)
                    printf '%%%02X' "'$_"
                ;;
            esac
        done
        printf '\n'
    }
    
    

    用法示例:

    $ urlencode "https://github.com/dylanaraps/pure-bash-bible"
    https%3A%2F%2Fgithub.com%2Fdylanaraps%2Fpure-bash-bible
    
    

    解码百分比编码的字符串

    示例功能:

    urldecode() {
        # Usage: urldecode "string"
        : "${1//+/ }"
        printf '%b\n' "${_//%/\\x}"
    }
    
    

    用法示例:

    $ urldecode "https%3A%2F%2Fgithub.com%2Fdylanaraps%2Fpure-bash-bible"
    https://github.com/dylanaraps/pure-bash-bible
    
    

    检查字符串是否包含子字符串

    使用测试:

    if [[ $var == *sub_string* ]]; then
        printf '%s\n' "sub_string is in var."
    fi
    
    # Inverse (substring not in string).
    if [[ $var != *sub_string* ]]; then
        printf '%s\n' "sub_string is not in var."
    fi
    
    # This works for arrays too!
    if [[ ${arr[*]} == *sub_string* ]]; then
        printf '%s\n' "sub_string is in array."
    fi
    
    

    使用案例陈述:

    case "$var" in
        *sub_string*)
            # Do stuff
        ;;
    
        *sub_string2*)
            # Do more stuff
        ;;
    
        *)
            # Else
        ;;
    esac
    
    

    检查字符串是否以子字符串开头

    if [[ $var == sub_string* ]]; then
        printf '%s\n' "var starts with sub_string."
    fi
    
    # Inverse (var does not start with sub_string).
    if [[ $var != sub_string* ]]; then
        printf '%s\n' "var does not start with sub_string."
    fi
    
    

    检查字符串是否以子字符串结尾

    if [[ $var == *sub_string ]]; then
        printf '%s\n' "var ends with sub_string."
    fi
    
    # Inverse (var does not end with sub_string).
    if [[ $var != *sub_string ]]; then
        printf '%s\n' "var does not end with sub_string."
    fi
    
    

    阵列

    反转数组

    启用extdebug允许访问BASH_ARGV数组,该数组
    反向存储当前函数的参数。

    示例功能:

    reverse_array() {
        # Usage: reverse_array "array"
        shopt -s extdebug
        f()(printf '%s\n' "${BASH_ARGV[@]}"); f "$@"
        shopt -u extdebug
    }
    
    

    用法示例:

    $ reverse_array 1 2 3 4 5
    5
    4
    3
    2
    1
    
    $ arr=(red blue green)
    $ reverse_array "${arr[@]}"
    green
    blue
    red
    
    

    删除重复的数组元素

    创建临时关联数组。设置关联数组
    值并发生重复赋值时,bash会覆盖该键。这
    允许我们有效地删除数组重复。

    CAVEAT:需要bash4+

    示例功能:

    remove_array_dups() {
        # Usage: remove_array_dups "array"
        declare -A tmp_array
    
        for i in "$@"; do
            [[ $i ]] && IFS=" " tmp_array["${i:- }"]=1
        done
    
        printf '%s\n' "${!tmp_array[@]}"
    }
    
    

    用法示例:

    $ remove_array_dups 1 1 2 2 3 3 3 3 3 4 4 4 4 4 5 5 5 5 5 5
    1
    2
    3
    4
    5
    
    $ arr=(red red green blue blue)
    $ remove_array_dups "${arr[@]}"
    red
    green
    blue
    
    

    随机数组元素

    示例功能:

    random_array_element() {
        # Usage: random_array_element "array"
        local arr=("$@")
        printf '%s\n' "${arr[RANDOM % $#]}"
    }
    
    

    用法示例:

    $ array=(red green blue yellow brown)
    $ random_array_element "${array[@]}"
    yellow
    
    # Multiple arguments can also be passed.
    $ random_array_element 1 2 3 4 5 6 7
    3
    
    

    循环一个数组

    每次printf调用时,都会打印下一个数组元素。当
    打印到达最后一个数组元素时,它
    再次从第一个元素开始。

    arr=(a b c d)
    
    cycle() {
        printf '%s ' "${arr[${i:=0}]}"
        ((i=i>=${#arr[@]}-1?0:++i))
    }
    
    

    在两个值之间切换

    这与上面的工作方式相同,这只是一个不同的用例。

    arr=(true false)
    
    cycle() {
        printf '%s ' "${arr[${i:=0}]}"
        ((i=i>=${#arr[@]}-1?0:++i))
    }
    
    

    LOOPS

    循环一系列数字

    替代seq

    # Loop from 0-100 (no variable support).
    for i in {0..100}; do
        printf '%s\n' "$i"
    done
    
    

    循环遍历可变数字范围

    替代seq

    # Loop from 0-VAR.
    VAR=50
    for ((i=0;i<=VAR;i++)); do
        printf '%s\n' "$i"
    done
    
    

    循环数组

    arr=(apples oranges tomatoes)
    
    # Just elements.
    for element in "${arr[@]}"; do
        printf '%s\n' "$element"
    done
    
    

    循环遍历带索引的数组

    arr=(apples oranges tomatoes)
    
    # Elements and index.
    for i in "${!arr[@]}"; do
        printf '%s\n' "${arr[i]}"
    done
    
    # Alternative method.
    for ((i=0;i<${#arr[@]};i++)); do
        printf '%s\n' "${arr[i]}"
    done
    
    

    循环遍历文件的内容

    while read -r line; do
        printf '%s\n' "$line"
    done < "file"
    
    

    循环遍历文件和目录

    不要用ls

    # Greedy example.
    for file in *; do
        printf '%s\n' "$file"
    done
    
    # PNG files in dir.
    for file in ~/Pictures/*.png; do
        printf '%s\n' "$file"
    done
    
    # Iterate over directories.
    for dir in ~/Downloads/*/; do
        printf '%s\n' "$dir"
    done
    
    # Brace Expansion.
    for file in /path/to/parentdir/{file1,file2,subdir/file3}; do
        printf '%s\n' "$file"
    done
    
    # Iterate recursively.
    shopt -s globstar
    for file in ~/Pictures/**/*; do
        printf '%s\n' "$file"
    done
    shopt -u globstar
    
    

    文件处理

    CAVEAT: bash在版本中不能正确处理二进制数据< 4.4

    将文件读取为字符串

    替代cat命令。

    file_data="$(<"file")"
    
    

    将文件读取到数组(按行

    替代cat命令。

    # Bash <4
    IFS=$'\n' read -d "" -ra file_data < "file"
    
    # Bash 4+
    mapfile -t file_data < "file"
    
    

    获取文件的前N行

    替代head命令。

    CAVEAT:需要bash4+

    示例功能:

    head() {
        # Usage: head "n" "file"
        mapfile -tn "$1" line < "$2"
        printf '%s\n' "${line[@]}"
    }
    
    

    用法示例:

    $ head 2 ~/.bashrc
    # Prompt
    PS1='➜ '
    
    $ head 1 ~/.bashrc
    # Prompt
    
    

    获取文件的最后N行

    替代tail命令。

    CAVEAT:需要bash4+

    示例功能:

    tail() {
        # Usage: tail "n" "file"
        mapfile -tn 0 line < "$2"
        printf '%s\n' "${line[@]: -$1}"
    }
    
    

    用法示例:

    $ tail 2 ~/.bashrc
    # Enable tmux.
    # [[ -z "$TMUX"  ]] && exec tmux
    
    $ tail 1 ~/.bashrc
    # [[ -z "$TMUX"  ]] && exec tmux
    
    

    获取文件中的行数

    替代wc -l

    示例函数(bash 4):

    lines() {
        # Usage: lines "file"
        mapfile -tn 0 lines < "$1"
        printf '%s\n' "${#lines[@]}"
    }
    
    

    示例函数(bash 3):

    此方法使用的内存少于mapfile方法,并在bash3中工作,但对于较大的文件,它的速度较慢。

    lines_loop() {
        # Usage: lines_loop "file"
        count=0
        while IFS= read -r _; do
            ((count++))
        done < "$1"
        printf '%s\n' "$count"
    }
    
    

    用法示例:

    $ lines ~/.bashrc
    48
    
    $ lines_loop ~/.bashrc
    48
    
    

    计算目录中的文件或目录

    这是通过将glob的输出传递给函数然后计算参数的数量来实现的。

    示例功能:

    count() {
        # Usage: count /path/to/dir/*
        #        count /path/to/dir/*/
        printf '%s\n' "$#"
    }
    
    

    用法示例:

    # Count all files in dir.
    $ count ~/Downloads/*
    232
    
    # Count all dirs in dir.
    $ count ~/Downloads/*/
    45
    
    # Count all jpg files in dir.
    $ count ~/Pictures/*.jpg
    64
    
    

    创建一个空文件

    替代touch

    # Shortest.
    >file
    
    # Longer alternatives:
    :>file
    echo -n >file
    printf '' >file
    
    

    提取两个标记之间的线条

    示例功能:

    extract() {
        # Usage: extract file "opening marker" "closing marker"
        while IFS=$'\n' read -r line; do
            [[ $extract && $line != "$3" ]] &&
                printf '%s\n' "$line"
    
            [[ $line == "$2" ]] && extract=1
            [[ $line == "$3" ]] && extract=
        done < "$1"
    }
    
    

    用法示例:

    # Extract code blocks from MarkDown file.
    $ extract ~/projects/pure-bash/README.md '```sh' '```'
    # Output here...
    
    

    文件路径

    获取文件路径的目录名称

    替代dirname命令。

    示例功能:

    dirname() {
        # Usage: dirname "path"
        printf '%s\n' "${1%/*}/"
    }
    
    

    用法示例:

    $ dirname ~/Pictures/Wallpapers/1.jpg
    /home/black/Pictures/Wallpapers/
    
    $ dirname ~/Pictures/Downloads/
    /home/black/Pictures/
    
    

    获取文件路径的基本名称

    替代basename命令。

    示例功能:

    basename() {
        # Usage: basename "path"
        : "${1%/}"
        printf '%s\n' "${_##*/}"
    }
    
    

    用法示例:

    $ basename ~/Pictures/Wallpapers/1.jpg
    1.jpg
    
    $ basename ~/Pictures/Downloads/
    Downloads
    
    

    变量

    使用变量分配和访问变量

    $ hello_world="value"
    
    # Create the variable name.
    $ var="world"
    $ ref="hello_$var"
    
    # Print the value of the variable name stored in 'hello_$var'.
    $ printf '%s\n' "${!ref}"
    value
    
    

    或者,在bash4.3+上:

    $ hello_world="value"
    $ var="world"
    
    # Declare a nameref.
    $ declare -n ref=hello_$var
    
    $ printf '%s\n' "$ref"
    value
    
    

    根据另一个变量命名变量

    $ var="world"
    $ declare "hello_$var=value"
    $ printf '%s\n' "$hello_world"
    value
    
    

    ESCAPE序列

    与流行的看法相反,使用原始逃逸序列没有问题。使用tput与手动打印相同的ANSI序列的摘要。更糟糕的是,tput实际上并不便携。有许多tput变体,每个变体都有不同的命令和语法(尝试tput setaf 3使用FreeBSD系统)。原始序列很好。

    文字颜色

    注意:需要RGB值的序列仅适用于真彩色终端仿真器。

    序列 它有什么作用?
    \e[38;5;<NUM>m 设置文本前景色。 0-255
    \e[48;5;<NUM>m 设置文本背景颜色。 0-255
    \e[38;2;<R>;<G>;<B>m 将文本前景色设置为RGB颜色。 RGB
    \e[48;2;<R>;<G>;<B>m 将文本背景颜色设置为RGB颜色。 RGB

    文字属性

    序列 它有什么作用?
    \e[m 重置文本格式和颜色。
    \e[1m 粗体。
    \e[2m 微弱的文字。
    \e[3m 斜体文字。
    \e[4m 下划线文字。
    \e[5m 慢慢眨眼。
    \e[7m 交换前景色和背景色。

    光标移动

    序列 它有什么作用?
    \e[<LINE>;<COLUMN>H 将光标移动到绝对位置。 linecolumn
    \e[H 将光标移动到原位(0,0)。
    \e[<NUM>A 将光标向上移动N行。 num
    \e[<NUM>B 将光标向下移动N行。 num
    \e[<NUM>C 将光标向右移动N列。 num
    \e[<NUM>D 将光标向左移动N列。 num
    \e[s 保存光标位置。
    \e[u 恢复光标位置。

    删除文本

    序列 它有什么作用?
    \e[K 从光标位置删除到行尾。
    \e[1K 从光标位置删除到行首。
    \e[2K 擦除整个当前行。
    \e[J 从当前行删除到屏幕底部。
    \e[1J 从当前行删除到屏幕顶部。
    \e[2J 清除屏幕。
    \e[2J\e[H 清除屏幕并将光标移动到0,0

    参数扩展

    间接

    参数 它有什么作用?
    ${!VAR} 根据值访问变量VAR
    ${!VAR*} 展开为IFSVAR。开头的变量名称的分隔列表。
    ${!VAR@} 展开为IFSVAR。开头的变量名称的分隔列表。如果是双引号,则每个变量名称都会扩展为单独的单词。

    替换

    参数 它有什么作用?
    ${VAR#PATTERN} 从字符串的开头删除模式的最短匹配。
    ${VAR##PATTERN} 从字符串的开头删除模式的最长匹配。
    ${VAR%PATTERN} 从字符串末尾删除模式的最短匹配。
    ${VAR%%PATTERN} 从字符串末尾删除模式的最长匹配。
    ${VAR/PATTERN/REPLACE} 用字符串替换第一个匹配。
    ${VAR//PATTERN/REPLACE} 用字符串替换所有匹配项。
    ${VAR/PATTERN} 删除第一场比赛。
    ${VAR//PATTERN} 删除所有比赛。

    长度

    参数 它有什么作用?
    ${#VAR} 字符变量的长度。
    ${#ARR[@]} 元素中的数组长度。

    扩张

    参数 它有什么作用?
    ${VAR:OFFSET} N从变量中删除第一个字符。
    ${VAR:OFFSET:LENGTH} N字符到N字符获取子字符串。
    ${VAR:10:10}:从char 10到char 获取子字符串20
    ${VAR:: OFFSET} N从变量中获取第一个字符。
    ${VAR:: -OFFSET} N从变量中删除最后一个字符。
    ${VAR: -OFFSET} N从变量中获取最后一个字符。
    ${VAR:OFFSET:-OFFSET} 削减第一个N字符和最后一个N字符。 bash 4.2+

    案例修改

    参数 它有什么作用? 警告
    ${VAR^} 大写第一个字符。 bash 4+
    ${VAR^^} 大写所有字符。 bash 4+
    ${VAR,} 小写第一个字符。 bash 4+
    ${VAR,,} 小写所有字符。 bash 4+

    默认值

    参数 它有什么作用?
    ${VAR:-STRING} 如果VAR为空或未设置,请使用STRING其值。
    ${VAR-STRING} 如果VAR未设置,请使用STRING其值。
    ${VAR:=STRING} 如果VAR为空或未设置,请将值设置VARSTRING
    ${VAR=STRING} 如果VAR未设置,请将值设置VARSTRING
    ${VAR:+STRING} 如果VAR不为空,则使用STRING其值。
    ${VAR+STRING} 如果VAR已设置,则使用STRING其值。
    ${VAR:?STRING} 如果为空或未设置,则显示错误。
    ${VAR?STRING} 如果未设置则显示错误。

    BRACE EXPANSION

    范围

    # Syntax: {<START>..<END>}
    
    # Print numbers 1-100.
    echo {1..100}
    
    # Print range of floats.
    echo 1.{1..9}
    
    # Print chars a-z.
    echo {a..z}
    echo {A..Z}
    
    # Nesting.
    echo {A..Z}{0..9}
    
    # Print zero-padded numbers.
    # CAVEAT: bash 4+
    echo {01..100}
    
    # Change increment amount.
    # Syntax: {<START>..<END>..<INCREMENT>}
    # CAVEAT: bash 4+
    echo {1..10..2} # Increment by 2.
    
    

    字符串列表

    echo {apples,oranges,pears,grapes}
    
    # Example Usage:
    # Remove dirs Movies, Music and ISOS from ~/Downloads/.
    rm -rf ~/Downloads/{Movies,Music,ISOS}
    
    

    有条件的表达

    文件条件

    表达 它有什么作用?
    -a file 如果文件存在。
    -b file 如果文件存在并且是块特殊文件。
    -c file 如果文件存在并且是字符特殊文件。
    -d file 如果文件存在且是目录。
    -e file 如果文件存在。
    -f file 如果文件存在且是常规文件。
    -g file 如果文件存在且其set-group-id位已设置。
    -h file 如果文件存在并且是符号链接。
    -k file 如果文件存在且其粘滞位已设置
    -p file 如果文件存在并且是命名管道(FIFO)。
    -r file 如果文件存在且可读。
    -s file 如果文件存在且其大小大于零。
    -t fd 如果文件描述符是打开的并且引用了终端。
    -u file 如果文件存在且其set-user-id位已设置。
    -w file 如果文件存在且可写。
    -x file 如果文件存在且可执行。
    -G file 如果文件存在且由有效组ID拥有。
    -L file 如果文件存在并且是符号链接。
    -N file 如果文件存在且自上次读取后已被修改。
    -O file 如果文件存在并且由有效用户ID拥有。
    -S file 如果文件存在且是套接字。

    文件比较

    表达 它有什么作用?
    file -ef file2 如果两个文件都引用相同的inode和设备编号。
    file -nt file2 如果filefile2使用修改时间)更新或file存在file2而不存在。
    file -ot file2 如果file早于file2使用修改时间)或file2存在file而不存在。

    可变条件

    表达 它有什么作用?
    -o opt 如果启用了shell选项。
    -v var 如果变量具有指定的值。
    -R var 如果variable是名称引用。
    -z var 如果字符串的长度为零。
    -n var 如果字符串的长度不为零。

    变量比较

    表达 它有什么作用?
    var = var2 等于。
    var == var2 等于(同义词=)。
    var != var2 不等于。
    var < var2 小于(以ASCII字母顺序排列。
    var > var2 大于(以ASCII字母顺序排列。

    算术运算符

    分配

    运营商 它有什么作用?
    = 初始化或更改变量的值。

    算术

    运营商 它有什么作用?
    + 加成
    - 减法
    * 乘法
    /
    **
    %
    += 加 - 等于(增加变量。
    -= 减去等于(减少变量。
    *= 时间相等(乘以变量。
    /= Slash-Equal(除以变量。
    %= Mod-Equal(除去变量的剩余部分。

    按位

    运营商 它有什么作用?
    << 按位左移
    <<= 左移平等
    >> 按位右移
    >>= 右移平等
    & 按位AND
    &= 按位AND-Equal
    ` ` 按位OR
    ` =` 按位OR-等于
    ~ 按位NOT
    ^ 按位异或
    ^= 按位XOR-Equal

    合乎逻辑

    运营商 它有什么作用?
    !
    &&
    ` ` 要么

    运营商 它有什么作用?
    , 逗号分隔符 ((a=1,b=2,c=3))

    算术

    设置变量的语法更简单

    # Simple math
    ((var=1+2))
    
    # Decrement/Increment variable
    ((var++))
    ((var--))
    ((var+=1))
    ((var-=1))
    
    # Using variables
    ((var=var2*arr[2]))
    
    

    三元测试

    # Set the value of var to var2 if var2 is greater than var.
    # var: variable to set.
    # var2>var: Condition to test.
    # ?var2: If the test succeeds.
    # :var: If the test fails.
    ((var=var2>var?var2:var))
    
    

    TRAPS

    陷阱允许脚本在各种信号上执行代码。在pxltrm用bash编写的像素艺术编辑器)中,陷阱用于在窗口大小调整时重绘用户界面。另一个用例是在脚本退出时清理临时文件。

    应该在脚本开头附近添加陷阱,以便捕获任何早期错误。

    注意:有关信号的完整列表,请参阅trap -l

    在脚本退出时做一些事情

    # Clear screen on script exit.
    trap 'printf \\e[2J\\e[H\\e[m' EXIT
    
    

    忽略终端中断(CTRL + C,SIGINT)

    trap '' INT
    
    

    对窗口调整大小做出反应

    # Call a function on window resize.
    trap 'code_here' SIGWINCH
    
    

    在每个命令之前做点什么

    trap 'code_here' DEBUG
    
    

    当shell函数或源文件完成执行时执行某些操作

    trap 'code_here' RETURN
    
    

    性能

    禁用Unicode

    如果不需要unicode,则可以禁用它以提高性能。结果可能会有所不同,但是neofetch和其他程序有明显改善。

    # Disable unicode.
    LC_ALL=C
    LANG=C
    
    

    已过时的语法

    家当

    #!/usr/bin/env bash而不是#!/bin/bash

    • 前者搜索用户PATH以查找bash二进制文件。
    • 后者假设它始终安装/bin/可能导致问题。
    # Right:
    
        #!/usr/bin/env bash
    
    # Wrong:
    
        #!/bin/bash
    
    

    命令替换

    $()而不是

    # Right.
    var="$(command)"
    
    # Wrong.
    var=`command`
    
    # $() can easily be nested whereas `` cannot.
    var="$(command "$(command)")"
    
    

    功能声明

    不要使用function关键字,它会降低与旧版本的兼容性bash

    # Right.
    do_something() {
        # ...
    }
    
    # Wrong.
    function do_something() {
        # ...
    }
    
    

    内部变量

    获取bash二进制文件的位置

    "$BASH"
    
    

    获取当前正在运行的bash进程的版本

    # As a string.
    "$BASH_VERSION"
    
    # As an array.
    "${BASH_VERSINFO[@]}"
    
    

    打开用户首选的文本编辑器

    "$EDITOR" "$file"
    
    # NOTE: This variable may be empty, set a fallback value.
    "${EDITOR:-vi}" "$file"
    
    

    获取当前函数的名称

    # Current function.
    "${FUNCNAME[0]}"
    
    # Parent function.
    "${FUNCNAME[1]}"
    
    # So on and so forth.
    "${FUNCNAME[2]}"
    "${FUNCNAME[3]}"
    
    # All functions including parents.
    "${FUNCNAME[@]}"
    
    

    获取系统的主机名

    "$HOSTNAME"
    
    # NOTE: This variable may be empty.
    # Optionally set a fallback to the hostname command.
    "${HOSTNAME:-$(hostname)}"
    
    

    获取操作系统的体系结构

    "$HOSTTYPE"
    
    

    获取操作系统/内核的名称

    这可用于为不同的操作系统添加条件支持,
    而无需调用uname

    "$OSTYPE"
    
    

    获取当前的工作目录

    这是pwd内置的替代品。

    "$PWD"
    
    

    获取脚本运行的秒数

    "$SECONDS"
    
    

    获取伪随机整数

    每次$RANDOM使用时,之间的不同整数032767被返回。此变量不应用于与安全性相关的任何内容(包括加密密钥等)。

    "$RANDOM"
    
    

    有关终端的信息

    获取行和列中的终端大小(来自脚本

    在纯bash中编写脚本和stty/ tput无法
    调用时,这很方便。

    示例功能:

    get_term_size() {
        # Usage: get_term_size
    
        # (:;:) is a micro sleep to ensure the variables are
        # exported immediately.
        shopt -s checkwinsize; (:;:)
        printf '%s\n' "$LINES $COLUMNS"
    }
    
    

    用法示例:

    # Output: LINES COLUMNS
    $ get_term_size
    15 55
    
    

    以像素为单位获取终端大小

    CAVEAT:这在某些终端仿真器中不起作用。

    示例功能:

    get_window_size() {
        # Usage: get_window_size
        printf '%b' "${TMUX:+\\ePtmux;\\e}\\e[14t${TMUX:+\\e\\\\}"
        IFS=';t' read -d t -t 0.05 -sra term_size
        printf '%s\n' "${term_size[1]}x${term_size[2]}"
    }
    
    

    用法示例:

    # Output: WIDTHxHEIGHT
    $ get_window_size
    1200x800
    
    # Output (fail):
    $ get_window_size
    x
    
    

    获取当前光标位置

    在纯bash中创建TUI时,这很有用。

    示例功能:

    get_cursor_pos() {
        # Usage: get_cursor_pos
        IFS='[;' read -p $'\e[6n' -d R -rs _ y x _
        printf '%s\n' "$x $y"
    }
    
    

    用法示例:

    # Output: X Y
    $ get_cursor_pos
    1 8
    
    

    转换

    将十六进制颜色转换为RGB

    示例功能:

    hex_to_rgb() {
        # Usage: hex_to_rgb "#FFFFFF"
        #        hex_to_rgb "000000"
        : "${1/\#}"
        ((r=16#${_:0:2},g=16#${_:2:2},b=16#${_:4:2}))
        printf '%s\n' "$r $g $b"
    }
    
    

    用法示例:

    $ hex_to_rgb "#FFFFFF"
    255 255 255
    
    

    将RGB颜色转换为十六进制

    示例功能:

    rgb_to_hex() {
        # Usage: rgb_to_hex "r" "g" "b"
        printf '#%02x%02x%02x\n' "$1" "$2" "$3"
    }
    
    

    用法示例:

    $ rgb_to_hex "255" "255" "255"
    #FFFFFF
    
    

    CODE GOLF

    更短的for循环语法

    # Tiny C Style.
    for((;i++<10;)){ echo "$i";}
    
    # Undocumented method.
    for i in {1..10};{ echo "$i";}
    
    # Expansion.
    for i in {1..10}; do echo "$i"; done
    
    # C Style.
    for((i=0;i<=10;i++)); do echo "$i"; done
    
    

    更短的无限循环

    # Normal method
    while :; do echo hi; done
    
    # Shorter
    for((;;)){ echo hi;}
    
    

    更短的功能声明

    # Normal method
    f(){ echo hi;}
    
    # Using a subshell
    f()(echo hi)
    
    # Using arithmetic
    # This can be used to assign integer values.
    # Example: f a=1
    #          f a++
    f()(($1))
    
    # Using tests, loops etc.
    # NOTE: ‘while’, ‘until’, ‘case’, ‘(())’, ‘[[]]’ can also be used.
    f()if true; then echo "$1"; fi
    f()for i in "$@"; do echo "$i"; done
    
    

    if语法更短

    # One line
    # Note: The 3rd statement may run when the 1st is true
    [[ $var == hello ]] && echo hi || echo bye
    [[ $var == hello ]] && { echo hi; echo there; } || echo bye
    
    # Multi line (no else, single statement)
    # Note: The exit status may not be the same as with an if statement
    [[ $var == hello ]] &&
        echo hi
    
    # Multi line (no else)
    [[ $var == hello ]] && {
        echo hi
        # ...
    }
    
    

    case设置变量的简单语句

    :内置的可以用来避免重复variable=在一个case语句。该$_变量存储的最后一个命令的最后一个参数。:总是成功,所以它可以用来存储变量值。

    # Modified snippet from Neofetch.
    case "$OSTYPE" in
        "darwin"*)
            : "MacOS"
        ;;
    
        "linux"*)
            : "Linux"
        ;;
    
        *"bsd"* | "dragonfly" | "bitrig")
            : "BSD"
        ;;
    
        "cygwin" | "msys" | "win32")
            : "Windows"
        ;;
    
        *)
            printf '%s\n' "Unknown OS detected, aborting..." >&2
            exit 1
        ;;
    esac
    
    # Finally, set the variable.
    os="$_"
    
    

    其他

    使用read作为替代的sleep命令

    令人惊讶的是,sleep是一个外部命令而不是bash内置命令。

    CAVEAT:需要bash4+

    示例功能:

    read_sleep() {
        # Usage: sleep 1
        #        sleep 0.2
        read -rst "${1:-1}" -N 999
    }
    
    

    用法示例:

    read_sleep 1
    read_sleep 0.1
    read_sleep 30
    
    

    检查程序是否在用户的PATH中

    # There are 3 ways to do this and either one can be used.
    type -p executable_name &>/dev/null
    hash executable_name &>/dev/null
    command -v executable_name &>/dev/null
    
    # As a test.
    if type -p executable_name &>/dev/null; then
        # Program is in PATH.
    fi
    
    # Inverse.
    if ! type -p executable_name &>/dev/null; then
        # Program is not in PATH.
    fi
    
    # Example (Exit early if program is not installed).
    if ! type -p convert &>/dev/null; then
        printf '%s\n' "error: convert is not installed, exiting..."
        exit 1
    fi
    
    

    使用获取当前日期 strftime

    Bash printf有一个内置的获取日期的方法,可用于代替date命令。

    CAVEAT:需要bash4+

    示例功能:

    date() {
        # Usage: date "format"
        # See: 'man strftime' for format.
        printf "%($1)T\\n" "-1"
    }
    
    

    用法示例:

    # Using above function.
    $ date "%a %d %b  - %l:%M %p"
    Fri 15 Jun  - 10:00 AM
    
    # Using printf directly.
    $ printf '%(%a %d %b  - %l:%M %p)T\n' "-1"
    Fri 15 Jun  - 10:00 AM
    
    # Assigning a variable using printf.
    $ printf -v date '%(%a %d %b  - %l:%M %p)T\n' '-1'
    $ printf '%s\n' "$date"
    Fri 15 Jun  - 10:00 AM
    
    

    获取当前用户的用户名

    CAVEAT:需要bash4.4+

    $ : \\u
    # Expand the parameter as if it were a prompt string.
    $ printf '%s\n' "${_@P}"
    black
    
    

    生成UUID V4

    CAVEAT:生成的值不具有加密安全性。

    示例功能:

    uuid() {
        # Usage: uuid
        C="89ab"
    
        for ((N=0;N<16;++N)); do
            B="$((RANDOM%256))"
    
            case "$N" in
                6)  printf '4%x' "$((B%16))" ;;
                8)  printf '%c%x' "${C:$RANDOM%${#C}:1}" "$((B%16))" ;;
    
                3|5|7|9)
                    printf '%02x-' "$B"
                ;;
    
                *)
                    printf '%02x' "$B"
                ;;
            esac
        done
    
        printf '\n'
    }
    
    

    用法示例:

    $ uuid
    d5b6c731-1310-4c24-9fe3-55d556d44374
    
    

    进度条

    这是一种绘制进度条的简单方法,无需
    在函数本身中使用for循环。

    示例功能:

    bar() {
        # Usage: bar 1 10
        #            ^----- Elapsed Percentage (0-100).
        #               ^-- Total length in chars.
        ((elapsed=$1*$2/100))
    
        # Create the bar with spaces.
        printf -v prog  "%${elapsed}s"
        printf -v total "%$(($2-elapsed))s"
    
        printf '%s\r' "[${prog// /-}${total}]"
    }
    
    

    用法示例:

    for ((i=0;i<=100;i++)); do
        # Pure bash micro sleeps (for the example).
        (:;:) && (:;:) && (:;:) && (:;:) && (:;:)
    
        # Print the bar.
        bar "$i" "10"
    done
    
    printf '\n'
    
    

    获取脚本中的函数列表

    get_functions() {
        # Usage: get_functions
        IFS=$'\n' read -d "" -ra functions < <(declare -F)
        printf '%s\n' "${functions[@]//declare -f }"
    }
    
    

    绕过shell别名

    # alias
    ls
    
    # command
    # shellcheck disable=SC1001
    \ls
    
    

    绕过shell函数

    # function
    ls
    
    # command
    command ls
    
    

    在后台运行命令

    这将运行给定命令并使其保持运行,即使在终端或SSH连接终止后也是如此。忽略所有输出。

    bkr() {
        (nohup "$@" &>/dev/null &)
    }
    
    bkr ./some_script.sh # some_script.sh is now running in the background
    
    

    参考

    https://en.wikibooks.org/wiki/Bash_Shell_Scripting

    后记

    谢谢阅读

    相关文章

      网友评论

        本文标题:全宇宙最全的bash 脚本常见用法总结!

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