日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

Bash技巧:介紹一個可以增刪改查鍵值對格式配置文件的Shell腳本

本篇文章介紹一個解析、以及增刪改查鍵值對格式配置文件的 bash shell 腳本。

該 shell 腳本處理的基本配置格式信息是:key|value

在腳本中,把 key 稱為 “鍵名”。把 value 稱為 “鍵值”。

把整個 key|value 稱為 “鍵值對”。

把中間的 | 稱為 “分隔符”。

默認的分隔符是 |。腳本里面提供了設置函數可以修改分隔符的值,以便自定義。

基于這個配置格式,可以配置下面的一些信息。

配置目錄路徑簡寫

配置一個目錄路徑簡寫,通過一個、或幾個字符,就可以快速 cd 到很深的目錄底下。

例如,在配置文件中有下面的信息:

am|frameworks/base/services/core/JAVA/com/Android/server/am/  
w|frameworks/base/wifi/java/android/net/wifi/

假設有一個 quickcd.sh 腳本可以解析這個配置信息。

在執行 quickcd.sh w 命令時,該腳本會基于 w 這個鍵名,獲取到 frameworks/base/wifi/java/android/net/wifi/ 這個鍵值。

然后腳本里面執行 cd frameworks/base/wifi/java/android/net/wifi/ 命令,進入到對應的目錄下。

這樣就不需要輸入多個字符,非常方便。

后面的文章會介紹在不同目錄之間快速來回 cd 的 quickcd.sh 腳本。

同時,所解析的配置信息保存在配置文件里面。

如果要新增、刪除配置項,修改配置文件自身即可,不需要改動腳本代碼。

這樣可以實現程序數據和程序代碼的分離,方便復用。

配置命令簡寫

配置一個命令簡寫,通過一個、或幾個字符,就可以執行相應的命令。

例如,在配置文件中有如下的信息:

l|adb logcat -b all -v threadtime
png|adb shell "screencap /sdcard/screen.png"

這里配置了 Android 系統的 adb 命令。

類似的,假設有一個 quickadb.sh 腳本可以解析這個配置信息。

執行 quickadb.sh l 命令,該腳本實際會執行 adb logcat -b all -v threadtime 命令。

這樣可以減少輸入,快速執行內容比較長的命令。

使用配置文件保存命令簡寫,可以動態添加、刪除命令,跟腳本代碼獨立開來。

后面的文章會介紹一個通過命令簡寫執行對應命令的 tinyshell.sh 腳本。

使用場景總結

總的來說,這里介紹的配置文件是基于鍵值對的形式。

常見的使用場景是,提供比較簡單的鍵名來獲取比較復雜的鍵值,然后使用鍵值來進行一些操作。

但是在實際輸入的時候,只需要輸入鍵名即可,可以簡化輸入,方便使用。

當然,實際使用并不局限于這些場景。

如果有其他基于鍵值對的需求,可以在對應的場景上使用。

腳本使用方法

這個解析配置文件的 shell 腳本是一個獨立的腳本,可以在其他腳本里面通過 source 命令進行調用。

假設腳本文件名為 parsecfg.sh,調用該腳本的順序步驟說明如下:

  • source parsecfg.sh:在調用者的腳本中引入 parsecfg.sh 腳本的代碼,以便后續調用 parsecfg.sh 腳本里面的函數。這里需要通過 source 命令來調用,才能共享 parsecfg.sh 腳本里面的函數、全局變量值。
  • (可選的)set_info_ifs separatorset_info_ifs:這是 parsecfg.sh 腳本里面的函數,用于設置分隔符。所給的第一個參數指定新的分隔符。默認分隔符是 |。如果需要解析的配置文件用的是其他分隔符,就需要先設置分隔符,再解析配置文件。如果使用默認的分隔符,可以跳過這個步驟。
  • open_config_file filenameopen_config_file:這是 parsecfg.sh 腳本里面的函數,用于解析配置文件。所給的第一個參數指定配置文件名。
  • (可選的)handle_config_option -l|-v|-i|-e|-a|-dhandle_config_option:這是 parsecfg.sh 腳本里面的函數,用于處理選項參數。‘-l’ 選項打印配置文件本身的內容。‘-v’ 選項以鍵值對的形式打印所有配置項的值。‘-i’ 選項后面要跟著一個參數,查詢該參數值在配置文件中的具體內容。‘-e’ 選項使用 vim 打開配置文件,以便手動編輯。‘-a’ 選項后面跟著一個參數,把指定的鍵值對添加到配置文件末尾。‘-d’ 選項后面跟著一個參數,從配置文件中刪除該參數所在的行。如果沒有需要處理的選項,可以跳過這個步驟。
  • 解析配置文件后,就可以調用 parsecfg.sh 腳本提供的功能函數來進行一些操作。get_key_of_entry 函數從 “key|value” 形式的鍵值對中獲取到 key 這個鍵名。get_value_of_entry 函數從 “key|value” 形式的鍵值對中獲取到 value” 這個鍵值。get_value_by_key 函數在配置文件中基于所給鍵名獲取到對應的鍵值。search_value_from_file 函數在配置文件中查找所給的內容,打印出匹配的行。delete_key_value 函數從配置文件中刪除所給鍵名對應的行。Append_key_value 函數把所給的鍵值對添加到配置文件的末尾。

parsecfg.sh 腳本代碼

列出 parsecfg.sh 腳本的具體代碼如下所示。

在這個代碼中,幾乎每一行代碼都提供了詳細的注釋,方便閱讀。

這篇文章的后面也會提供一個參考的調用例子,有助理解。

#!/bin/bash
# 這個腳本提供函數接口來解析、處理鍵值對格式的配置文件.
# 默認的配置格式為: key|value. 該腳本提供如下功能:
# 1.根據所提供的 key 獲取到對應的 value.
# 2.查看配置文件的內容.
# 3.使用 vim 打開配置文件,以供編輯.
# 4.提供函數來插入一個鍵值對到配置文件中.
# 5.提供函數從配置文件中刪除所給 key 對應的鍵值對.
# 上面的 | 是鍵名和鍵值之間的分隔符.腳本提供set_info_ifs()函數來設置新的值.

# 下面變量保存傳入的配置文件名.
PARSECFG_filepath=""

# 定義配置文件中鍵名和鍵值的分隔符. 默認分隔符是 '|'.
# 可以調用 set_info_ifs() 函數來修改分隔符的值,指定新的分隔符.
info_ifs="|"

######## 下面函數是當前腳本實現的功能函數 ########

# 從傳入的項中提取出鍵名,并把鍵名寫到標準輸出,以供調用者讀取.
# 下面echo的內容要用雙引號括起來.雙引號可以避免進行路徑名擴展等.
# 當所echo的內容帶有 '*' 時,不加雙引號的話, '*' 可能會進行路徑
# 名擴展,導致輸出結果發生變化. 后面的幾個函數也要參照這個處理.
get_key_of_entry()
{
    local entry="$1"
    # ${param%%pattern} 表達式刪除匹配的后綴,返回前面剩余的部分.
    echo "${entry%%${info_ifs}*}"
}

# 從傳入的項中提取出鍵值,并把鍵值寫到標準輸出,以供調用者讀取.
get_value_of_entry()
{
    local entry="$1"
    # ${param#pattern} 表達式刪除匹配的前綴,返回后面剩余的部分.
    echo "${entry#*${info_ifs}}"
}

# 該函數根據傳入的鍵名從 key_values 關聯數組中獲取對應鍵值.
# 如果匹配,將鍵值寫到標準輸出,調用者可以讀取該標準輸出來獲取鍵值.
# 該函數把查詢到的鍵值寫入到標準輸出的鍵值. 如果沒有匹配所提供
# 鍵名的鍵值,輸出會是空. 調用者需要檢查該函數的輸出是否為空.
get_value_by_key()
{
    # 所給的第一個參數是要查詢的鍵名.
    local key="$1"
    # 使用鍵名從鍵值對數組中獲取到鍵值,并輸出該鍵值.
    echo "${key_values["${key}"]}"
}

# 根據傳入的鍵名刪除配置文件中對應該鍵名的行.
delete_entry_by_key()
{
    # 所給的第一個參數是要刪除的鍵名,會刪除對應的鍵值對.
    local key="$1"
    # 這里要在${key}的前面加上^,要求${key}必須在行首.
    sed -i "/^${key}|/d" "${PARSECFG_filepath}"
    # 將關聯數組中被刪除鍵名對應的鍵值設成空.
    # key_values["${key}"]=""
    # 將鍵值設成空,這個鍵名還是存在于數組中.可以用 unset name[subscript]
    # 命令移除指定下標的數組元素.移除之后,這個數組元素在數組中已經不存在.
    # 注意用雙引號把整個數組元素括起來. unset 命令后面的參數會進行路徑名
    # 擴展.例如提供key_values[s]參數,如果當前目錄下有一個key_valuess文件,
    # 那么key_values[s]會對應 key_valuess,而不是對應數組下標為s的數組元素.
    # 為了避免這個問題,使用雙引號把整個數組元素括起來,不進行路徑名擴展.
    unset "key_values[${key}]"
}

# 根據傳入的鍵名,刪除它在配置文件中對應的行
delete_key_value()
{
    if [ $# -ne 1 ]; then
        echo "Usage: $FUNCNAME key_name"
        return 1
    fi
    local key="$1"

    # 如果所給的鍵名在配置文件中已經存在,get_value_by_key()函數輸出
    # 的內容不為空. 判斷該函數的輸出內容,不為空時才進行刪除.
    local value=$(get_value_by_key "${key}")
    if test -n "${value}"; then
        delete_entry_by_key "${key}"
    else
        echo "出錯: 找不到路徑簡寫 '${key}' 對應的行"
    fi
}

# 該函數先從傳入的鍵值對中解析出鍵名,然后執行get_value_by_key()
# 函數來判斷該鍵名是否已經在配置文件中,如果在,就刪除該鍵名對應的行.
# 最終,新傳入的鍵值對會被追加到配置文件的末尾.
append_key_value()
{
    if [ $# -ne 1 ]; then
        echo "Usage: $FUNCNAME key_value"
        return 1
    fi
    # 所給的第一個參數是完整的鍵值對.
    local full_entry="$1"

    # 從傳入的鍵值對中解析出鍵名
    local key_name=$(get_key_of_entry "${full_entry}")
    # 從配置文件中獲取該鍵名對應的值.如果能夠獲取到值,表示該鍵名已經存在
    # 于配置文件中,會先刪除這個鍵值對,再追加新傳入的鍵值對到配置文件末尾.
    local match_value=$(get_value_by_key "${key_name}")
    if test -n "${match_value}"; then
        echo "更新 ${key_name}${info_ifs}${match_value} 為: ${full_entry}"
        delete_entry_by_key "${key_name}"
    fi

    # 追加新的鍵值對到配置文件末尾
    echo "${full_entry}" >> "${PARSECFG_filepath}"
    # 將新項的鍵名和鍵值添加到 key_values 數組中,以便實時反應這個修改.
    key_values["${key_name}"]="$(get_value_of_entry "${full_entry}")"
}

# 使用 cat 命令將配置文件的內容打印到標準輸出上.
show_config_file()
{
    echo "所傳入配置文件的內容為:"
    cat "${PARSECFG_filepath}"
}

# 打印從配置文件中解析得到的鍵值對.
show_key_values()
{
    local key_name
    # ${!array[@]} 對應關聯數組的所有鍵. ${array[@]}對應關聯數組的所有值.
    # 下面先獲取關聯數組的鍵,再通過鍵名來獲取鍵值,并把鍵名和鍵值都打印出來.
    for key_name in "${!key_values[@]}"; do
        printf "key='e[32m${key_name}e[0m' t"
        printf "value='e[33m${key_values["${key_name}"]}e[0m'n"
    done
}

# 使用 vim 打開配置文件,以供編輯. 注意: 使用vim編輯文件后,文件所發生的改動不能
# 實時在腳本中反應出來,需要再次執行腳本,重新讀取配置文件才能獲取到所作的修改.
# 為了避免這個問題,在退出編輯后,主動調用open_config_file函數,重新解析配置文件.
edit_config_file()
{
    vim "${PARSECFG_filepath}"
    # 調用 open_config_file() 函數解析配置文件,重新為 key_values 賦值.
    open_config_file "${PARSECFG_filepath}"
}

# 在配置文件中查找指定的內容,看該內容是否在配置文件中.
search_value_from_file()
{
    # 如果查找到匹配的內容,grep命令會打印匹配的內容輸出,以便查看.
    grep "$1" "${PARSECFG_filepath}"
    if [ $? -ne 0 ]; then
        echo "配置文件中不包含所給的 '$1'"
        return 1
    fi
}

######## 下面函數是初始化時需要調用的函數 ########

# 該函數用于設置配置文件中鍵名和鍵值的分隔符.
# 所給的第一個參數會指定新的分隔符,并覆蓋之前設置的分隔符.
set_info_ifs()
{
    if [ $# -ne 1 ]; then
        echo "Usage: $FUNCNAME separator"
        return 1
    fi

    if [ -n "${PARSECFG_filepath}" ]; then
        # 如果配置文件名不為空,說明之前已經解析過配置文件.
        # 那么之前解析文件沒有使用新的分隔符,報錯返回.需要
        # 調用者修改代碼,先調用當前函數,再調用open_config_file()
        # 函數,以便使用新指定的分隔符來解析配置文件的內容.
        echo "出錯: 設置分隔符要先調用 set_info_ifs,再調用 open_config_file."
        return 2
    fi

    info_ifs="$1"
}

# 讀取配置文件,并將配置文件的內容保存到關聯數組中. 每次解析配置文件
# 之前,都要先調用該函數.后續直接通過關聯數組來獲取對應的值,不再多次
# 打開文件. 該函數接收一個參數,指定要解析的配置文件路徑名.
open_config_file()
{
    if [ $# -ne 1 ]; then
        echo "Usage: $FUNCNAME config_filename"
        return 1
    fi

    # 判斷所給的配置文件是否存在,且是否是文本文件.
    if [ ! -f "${1}" ]; then
        echo "ERROR: the file '${1}' does not exist"
        return 2
    fi
    # 存在配置文件,則把文件路徑名保存到 PARSECFG_filepath 變量.
    # 使用 readlink -f 命令獲取文件的絕對路徑,包括文件名自身.
    # 一般來說,所給的文件名是相對路徑.后續 cd 到其他目錄后,用
    # 所給的相對路徑會找不到這個文件, -l 選項無法查看文件內容.
    PARSECFG_filepath="$(readlink -f $1)"
    # 定義一個關聯數組,保存配置文件中的鍵值對. 要先重置key_values的定義,
    # 避免通過 source 命令調用該腳本時, key_values 所保存的值沒有被清空,
    # 造成混亂. 在函數內使用 declare 聲明變量,默認是局部變量,跟 local
    # 命令類似. 使用 declare -g 可以在函數內聲明變量為全局變量.
    unset key_values
    declare -g -A key_values

    local key value entryline

    # 逐行讀取配置文件,并從每一行中解析出鍵名和鍵值,保存到關聯數組
    # key_values中.后續直接通過鍵名來獲取鍵值.如果鍵名不存在,鍵值為空.
    while read entryline; do
        # 由于配置文件的鍵值中可能帶有空格,下面的${entryline}要用雙引號
        # 括起來,避免帶有空格時,本想傳入一個參數,卻被分割成了多個參數.
        # 例如${entryline}是service list,在不加引號時,get_value_of_entry()
        # 函數會接收到兩個參數,第一個參數是$1,對應service,第二個參數是$2,
        # 對應list,而get_value_of_entry()函數只獲取了第一個參數的值,這樣就
        # 會處理出錯.在傳遞變量值給函數時,變量值一定要用雙引號括起來.
        key=$(get_key_of_entry "${entryline}")
        value=$(get_value_of_entry "${entryline}")
        # 經過驗證,當 key_values[] 后面跟著等號'='時,所給的[]不會進行
        # 路徑名擴展,不需要像上面用 unset 命令移除數組元素那樣用雙引號
        # 把整個數組元素括起來以避免路徑名擴展.
        key_values["${key}"]="${value}"
        # 下面是預留的調試語句.在調試的時候,可以打開下面的注釋.
        # echo "entryline=${entryline}"
        # echo "key=${key}"
        # echo "value=${value}"
    done < "${PARSECFG_filepath}"
    # 查看關聯數組 key_values 的值.調試的時候,可以打開下面的注釋.
    # declare -p key_values
}

# 操作配置文件的功能選項.建議外部調用者通過功能選項來指定要進行的操作.
# 該函數最多接收兩個參數:
#   第一個參數: 提供選項名,該選項名要求以'-'開頭,才是合法選項.
#   第二個參數: 提供選項的參數. 部分選項后面需要跟著一個參數.
# 當傳入的選項被handle_config_option()函數處理時,該函數返回處理后的狀態碼.
# 例如,處理成功返回0,失敗返回非0. 當傳入的選項不被該函數處理時,它返回127.
handle_config_option()
{
    if [ -z "${PARSECFG_filepath}" ]; then
        # 如果配置文件變量值為空,說明還沒有解析配置文件,不能往下處理.
        echo "出錯: 請先調用 open_config_file filename 來解析配置文件."
        return 1
    fi
    local option="$1"
    local argument="$2"

    case "${option}" in
        -l) show_config_file ;;
        -v) show_key_values ;;
        -i) search_value_from_file "${argument}" ;;
        -e) edit_config_file ;;
        -a) append_key_value "${argument}" ;;
        -d) delete_key_value "${argument}" ;;
         *) return 127 ;;
    esac

    # 當return語句不加上具體狀態碼時,它會返回上一條執行命令的狀態碼.
    return
}

使用 parsecfg.sh 腳本的例子

假設有一個 testparsecfg.sh 腳本,具體的代碼內容如下:

#!/bin/bash

CFG_FILE="cfgfile.txt"

# 通過 source 命令加載 parsecfg.sh 的腳本代碼
source parsecfg.sh

# 調用 open_config_file 函數解析配置文件
open_config_file "$CFG_FILE"

# 調用 handle_config_option 函數處理 -v 選項.
# 該選項以鍵值對的形式列出所有配置項.
handle_config_option -v

# 獲取 am 這個鍵名對應的鍵值
value=$(get_value_by_key "am")
echo "The value of 'am' key is: $value"

# 使用 get_key_of_entry 函數從鍵值對中獲取鍵名.該函數
# 針對鍵值對自身進行處理,所給的鍵值對可以不在配置文件中.
key=$(get_key_of_entry "a|adb logcat -b all")
echo "The key of 'a|adb logcat -b' is: $key"

這個腳本所調用的函數都來自于 parsecfg.sh 腳本。

這個 testparsecfg.sh 腳本指定解析一個 cfgfile.txt 配置文件。

該配置文件的內容如下:

am|frameworks/base/services/core/java/com/android/server/am/
w|frameworks/base/wifi/java/android/net/wifi/

把 parsecfg.sh 腳本、testparsecfg.sh 腳本、和 cfgfile.txt 配置文件都放到同一個目錄下。

然后給這兩個腳本文件都添加可執行權限。

執行 testparsecfg.sh 腳本,具體結果如下:

$ ./testparsecfg.sh
key='am'        value='frameworks/base/services/core/java/com/android/server/am/'
key='w'         value='frameworks/base/wifi/java/android/net/wifi/'
The value of 'am' key is: frameworks/base/services/core/java/com/android/server/am/
The key of 'a|adb logcat -b' is: a

可以看到,在 testparsecfg.sh 腳本中通過 source 命令引入 parsecfg.sh 腳本.

之后可以調用 parsecfg.sh 腳本里面的代碼來解析配置文件,非常方便。

如果多個腳本需要解析多個不同的配置文件,可以在各自腳本中引入 parsecfg.sh 腳本,然后提供不同的配置文件名即可。

 

分享到:
標簽:Bash
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定