bash shell豆知識

1. Replace a substring in file
 `(20, 'm')` in file: get_session.sql to `(35, 'm')`. 20 can be any number. 
     ```shell
    sed -i "s/([0-9]\+,'m');/(35,'m');/" get_session.sql && cat get_session.sql
     ```
2. Write multipule lines to a file by command
    ```shell
    cat <<EOL > get_session.sql
    set linesize 300;
    set serveroutput on;
    exec session_infos(20,'m');
    exit;
    EOL
    ```
3. Positional Parameters

    ```bash
    #: Description: Positional Parameters - print Filename and the first command-line argument
                  
    printf "Filename: %s, First param: %s!\n" "$0" "$1"
    ```
    >$ hello Tom
    Filename: /home/tom/bin/hello, First param: Tom!

4. Printing to a Variable
    ```bash
    printf -v filename "staff_%03d.csv" 12
    echo $filename
    # output: staff_012.csv It's the same as:
    echo "staff_$(printf '%03d' 12)"
    ```
5. Exit status
    >  The exit code is stored in the special parameter `$?`. If the command executed successfully (or true), the value of `$?` is zero. 
6. Test
    ```bash
    test -f /etc/abc  ## true if a regular file
    [ -x "$HOME/go" ]   ## true if you can execute the file, the spaces are necessary
    [ 2 -eq 1 ]  ## test whether integers equal
    [ "$q" != "$b" ] ## test whether two strings equal
    [ -z "" ]  ## return successfully if their arguments are empty
    [ -n "" ]  ## return successfully if their arguments are nonempty
    test "$str1" \< "$str2" ## < or > must be escaped to prevent them from being interpreted as redirection operators
    [ -e "b.txt" -a "$a" -gt 9 ] ## Check if b.txt exists and a is greater than 9
    [ -f b.txt -o -f c.txt ] ## b.txt or c.txt is a regular file
    if (( total > max )); then : ...; fi ## (( … )): Evaluate an Arithmetic Expression
    mkdir "$HOME/bin" && cd "$HOME/bin" || exit 1  ## create dir and cd. If failed, exit 1
    ```
7. case
    ```bash
    ## check if the first argument is integer. two ;; are necessary
    case $1 in         
        *[!0-9]*) echo false;;   ## if it contains any char except 0-9
        *) echo true;;
    esac
    ```
8. while loop
    ```bash
    # print n while N <=10
    n=1
    while [ $n -le 10 ]
    do
     echo $n
     n=$(( $n + 1 ))
    done
    
    # read from session.txt line by line, print the line if conatains "id"
    while IFS= read -r line
    do 
        if [[ $line == *"id"* ]]; then
            echo $line
        fi
    done < session.txt
    ```
9. for loop
    ```bash
    # print odd number while N <=5
    for (( n=1; n<=5; ++n)); do
        if ((n%2 == 0));then
            continue
        fi
        echo $n
    done
    
    # iterate a list
    for fruit in Apple Banana Orange; do
        printf "%s\n" "$fruit"
    done
    ```
10. Brace Expansion
    ```bash
    # generate filename p001.txt ~ p100.txt
    for num in p{001..100}.txt; do 
        echo $num;
    done
    ```
11. Arithmetic Expansion
    > When the shell encounters $(( expression )), it evaluates expression and places the result on the command line; expression is an arithmetic expression.
    ```bash
    echo "$(( 1 + 1 ))" "$(( 2 * 3 ))" "$(( 16 / 3 ))" "$(( 16%5 ))" "$(( 6 - 9 ))" "$(( (2+3)*4 ))"
    # 2 6 5 1 -3 20
    ```

12. Command Substitution
    >Command substitution replaces a command with its output. 
    New format: $( command )
    old format: \`command\`

    ```bash
     # current date as YYYY-MM-DD
     cur_date=$(date +%Y-%m-%d)
     echo "$cur_date"
     # count the lines in variant txt
     txt=$(cat access.log)
     num=$(echo -n "$txt" | wc -l)  # must quote the %txt and add -n to remove newline by echo
     echo $num
     num=$(printf "%s" "$t" | wc -l)
     echo $num
    ```
13. Process Substitution
    >Process substitution creates a temporary file name for a command or list of commands. You can use it anywhere a file name is expected. The form <(command) makes the output of command available as a file name;
    ```bash
    line_count=$(wc -l <(cat hw.txt) | cut -d ' ' -f 1) # first:line number, second: file path
    echo $line_count
    ```
14. Variables
    > Variables set in a subshell are not visible to the script that called it. Subshells include command substitution, as in $(command) or \`command\`; all elements of a pipeline; and code enclosed in parentheses, as in ( command ).

15. Parameter Expansion
    ```bash
    var=
    echo "${var:-default_value}"
    ```
    if a var is unset or empty then expands to default_value.
    ```bash
    var=
    echo "${var-default_value}"
    unset var
    echo "${var-default_value}"
    ``` 
    if a var is unset then expands to default_value.

    >`echo "${var:+alternate_value}"` use alternate only if $var is set and is not empty
    `echo "${var+alternate_value}"` use alternate if $var is set

     `${var:=default}` and `${var=default}`: Assign Default Values
     The `${var:=default}` expansion behaves in the same way as `${var:-default}` except that it also assigns the default value to the variable

    `${var:?message}` and `${var?message}`: If var is empty or not set, message will be printed to the standard error, and the script will exit with a status of 1. 

    ```bash
    #!/bin/bash
    : ${1?An argument is required}
    : ${2?Two arguments are required}
    echo "Both arguments are set: \$1 = $1, \$2 = $2"
    ```
    >In Bash, the : character is a built-in command that essentially does nothing and returns a status of 0 (true). It is often used as a placeholder in scripts where a command is syntactically required but no operation is needed.

    **String operation**
    ```bash
    # ${#var}: Length of Variable’s Contents
    read -p "Please input the password(8 digit or more):" passwd
    [ ${#passwd} -lt 8 ] && echo "Password is tool short" && exit 1
    
    # ${var%PATTERN}: Remove the Shortest Match from the End
    var=hello
    echo "${var%l*}"  # hel
    # ${var%%PATTERN}: Remove the Longest Match from the End
    echo "${var%%l*}"  # he
    
    # ${var#PATTERN}: Remove the Shortest Match from the Beginning
    var=hello
    echo "${var#*l}"  # lo
    # ${var##PATTERN}: Remove the Longest Match from the Beginning
    echo "${var##*l}"  # o
    
    # ${var//PATTERN/STRING}: Replace All Instances of PATTERN with STRING
    var=helplok
    echo "${var//l?/v}"  # hevvk the question mark matches any single character
    # With a single slash, only the first matching character is replaced.
    echo "${var/l?/v}" # hevlok
    
    # ${var:OFFSET:LENGTH}: Return a Substring of $var
    var=hello
    echo "${var:1:2}" # el
    echo "${var:1}" # ello
    # A negative OFFSET is counted from the end of the string. 
    #  If a literal minus sign is used (as opposed to one contained in a variable),
    # it must be preceded by a space to prevent it from being interpreted as a default expansion
    echo "${var: -2}" # lo
    echo "${var: -3: -1}" # ll , end excluded

    # ${var^PATTERN}: Convert the first character to Uppercase
    var=hello
    echo "${var^}" #Hello
    # ${var^^PATTERN}: Convert all the matched character to Uppercase
    echo "${var^^}" #HELLO
    echo "${var^^[a-j]}" # HEllo
    # ${var,PATTERN}, ${var,,PATTERN}: Convert to Lowercase
    var=HELLO
    echo "${var,}" #hELLO
    echo "${var,,}" #hello
    ```
16. Arrays
    ```bash
    # Create a array
    FRUITS=( Apple Banana Orange Grape )
    # get the second element
    echo "${FRUITS[1]}"
    # Assign new value
    FRUITS[1]=Melon
    echo "${FRUITS[1]}"
    # Append new values
    FRUITS+=(WaterMelon Pear)
    # print all
    echo "${FRUITS[@]}"
    # print length
    echo "${#FRUITS[@]}"
    # print 2~3 elements
    echo "${FRUITS[@]:1:2}"
    
    # Associative Arrays
    declare -A weekMap
    weekMap["m"]="Monday"
    weekMap["t"]="Tuesday"
    echo "${weekMap[t]}"
    echo "${weekMap[@]}"
    ```
17. String Manipulation
    ```bash
    # Concatenation
    a="123"
    a+="abc"
    ```