中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么使用命令執行Shell腳本

發布時間:2020-12-28 14:51:45 來源:億速云 閱讀:183 作者:Leah 欄目:開發技術

怎么使用命令執行Shell腳本?很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。

假設這個 shell 腳本的名稱為 tinyshell.sh。

在 Linux 下進行項目開發,經常會用到一些調試開發命令。

這些命令可能比較長,需要輸入多個字符。

例如,Android 系統抓取全部 log 并包含 log 時間的命令是 adb logcat -b all -v threadtime。

抓取 log 是調試開發非常常見的操作,這個命令又很長,輸入起來不方便。

為了簡化輸入,可以配置一些命令簡寫來對應比較長命令。

例如,配置 ala 對應 adb logcat -b all -v threadtime。

把 als 作為參數傳遞給當前的 tinyshell.sh 腳本,會執行該命令簡寫對應的命令。

這樣只需要輸入比較少的字符,就能執行比較長的命令。

實際上,這個功能類似于 bash 的 alias 別名,只是將這些別名統一放到該腳本來處理。

可以把 tinyshell.sh 腳本作為學習 shell 腳本的參考例子,獨立維護更新,根據需要擴充更多的功能。

配置命令簡寫

如之前說明,可以用 ala 表示 adb logcat -b all -v threadtime 這個命令。

這個 ala 稱之為 “命令簡寫”。

命令簡寫使用一些簡單的字符來表示特定的命令。

可以在命令簡寫后面動態提供命令的參數。

為了方便動態添加、刪除、查詢命令簡寫,可以把這些命令簡寫保存在一個配置文件里面。

在執行 tinyshell.sh 腳本時,會讀取配置文件內容,獲取到各個配置項的值。

配置項的基本格式是:命令簡寫|命令內容

每個配置項占據一行。每一行默認以第一個豎線 ‘|' 隔開命令簡寫和命令內容。

一個參考的配置文件內容如下所示:

ll|ls --color=auto -l
ala|adb logcat -b all -v threadtime
gl|git log
gp|git pull --stat --no-tags $(git remote) $(git rev-parse --abbrev-ref HEAD)

這里配置的命令內容可以是系統支持的任意命令。

解析配置文件時,需要用到之前文章介紹的 parsecfg.sh 腳本。

要獲取 parsecfg.sh 腳本的代碼,可以查看之前的文章。

后面會提供具體測試的例子,可供參考。

腳本代碼

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

在這個代碼中,對大部分關鍵代碼都提供了詳細的注釋,方便閱讀。

這篇文章的后面也會對一些關鍵點進行說明,有助理解。

#!/bin/bash -i
# 使用 bash 的 -i 選項,讓該腳本在交互模式下運行.
# 實現一個小型的 shell. 支持內置命令、命令簡寫. 如果提供這兩種命令之外
# 的其他命令,會嘗試在 bash 中直接執行所給命令,可以執行系統支持的命令.
# 命令簡寫指的是一些簡單的字符,會對應一串實際要執行的命令.只要輸入命令
# 簡寫就可以執行對應的命令,減少需要輸入的字符.命令簡寫在配置文件中配置.

# 下面變量指定默認解析的配置文件名.該文件配置了命令簡寫、以及對應的命令.
# 這個 tinyshellcmds.txt 文件需要預先配置好,放到指定路徑的目錄底下.
# 直接修改這個配置文件,就可以動態添加或刪除命令簡寫.不需要修改腳本代碼.
SHORT_COMMANDS="${HOME}/.liconfig/tinyshellcmds.txt"

# PARSECFG_filepath 是 parsecfg.sh 腳本里面的變量. 如果這個變量為空,
# 說明還沒有打開過配置文件,進入下面的分支打開默認的配置文件.
if [ -z "$PARSECFG_filepath" ]; then
  # 導入解析配置文件的腳本,以便調用該腳本的函數來解析配置文件.
  source parsecfg.sh
  # 調用 parsecfg.sh 里面的 open_config_file() 函數解析配置文件.
  # 如果配置文件不存在,會返回 1,經過'!'操作符取反為 0,會退出執行.
  if ! open_config_file "$SHORT_COMMANDS"; then
    exit 2
  fi
fi

# 下面變量指定 tiny shell 的提示字符串.
PROMPT="TinySh>>> "

# 下面使用 basename 命令來提取出腳本的文件名,去掉目錄路徑部分.
show_help()
{
printf "USAGE
  $(basename $0) [option] [shortcmd [argument1 ... [argumentn]]]
OPTIONS
  option: 可選的選項參數. 支持的選項參數描述如下:
    -h: 打印這個幫助信息.
    -l: 打印配置文件本身的內容,會列出配置的命令簡寫和對應的命令.
    -v: 以鍵值對的方式列出命令簡寫和對應的命令.
    -i: 在配置文件中查找指定內容.后面跟著一個參數,指定要查找的內容.
    -e: 使用 vim 打開腳本的配置文件,以供編輯.
    -a: 新增或修改一個命令簡寫和對應的命令.后面跟著一個參數,用
      單引號括起來,以指定命令簡寫和命令. 格式為: 命令簡寫|命令.
      例如 -a 'p|git pull',如果p簡寫不存在則新增它,否則修改它.
    -d: 從腳本配置文件中刪除一個命令簡寫和對應的命令.后面跟著一個
      參數,指定要刪除的命令簡寫.例如 -d s,會刪除命令簡寫為 s 的行.
  shortcmd: 可選選項.
    指定要直接執行的命令簡寫. 提供命令簡寫參數,不會進入 tiny shell.
  argument1 ... argumentn: 可選選項.
    指定該命令簡寫的參數. 命令簡寫對應一個命令,支持動態提供參數.
NOTE
  如果沒有提供任何參數,默認會進入 tiny shell 解釋器. 在 tiny shell 中
  接收用戶輸入并執行對應的命令.直到讀取到EOF、或者執行quit命令才會退出.
"
}

# tiny shell 的內置命令數組. 這是一個關聯數組. 數組元素的
# 鍵名是內置命令名. 數組元素的鍵值是響應內置命令的函數名.
declare -A BUILTIN_COMMAND=( \
  [help]="builtin_command_help" \
  [quit]="builtin_command_quit" \
  [debug]="builtin_command_debug" \
)

# bash 的 help 命令默認會打印內置命令列表. 這里仿照這個行為,
# 讓 help 內置命令打印內置命令列表、以及配置文件包含的命令簡寫.
builtin_command_help()
{
printf "下面列出 Tiny Shell 支持的內置命令列表和配置的命令簡寫列表.
輸入內置命令名或命令簡寫,會執行對應的命令.
也可以輸入系統自身支持的命令,會在 bash 中執行所給命令.

內置命令列表:
  debug: 所給第一個參數指定打開、或關閉調試功能. 其參數說明如下:
    on: 打開調試功能,會執行 bash 的 set -x 命令
    off: 關閉調試功能,會執行 bash 的 set +x 命令
  help: 打印當前幫助信息.
  quit: 退出當前 Tiny Shell.

命令簡寫列表:
"
  # 調用 parsecfg.sh 的 handle_config_option -v 打印命令簡寫列表
  handle_config_option -v
}

# quit 內置命令. 執行該命令會退出整個腳本,從而退出當前 tiny shell.
builtin_command_quit()
{
  exit
}

# debug 內置命令. 所給第一個參數指定打開、或關閉調試功能.
# debug on: 打開調試功能,會執行 bash 的 set -x 命令
# debug off: 關閉調試功能,會執行 bash 的 set +x 命令
builtin_command_debug()
{
  if [ $# -ne 1 ]; then
    echo "Usage: debug on/off"
    return 1
  fi

  if [ "$1" == "on" ]; then
    set -x
  elif [ "$1" == "off" ]; then
    set +x
  else
    echo -e "Unknown argument: $1\nUsage: debug on/off"
  fi
  return
}

# 處理 tiny shell 內置命令.對于內置命令,會調用對應函數進行處理.
# 該函數的返回值表示所給命令名是否內置命令.
# 返回 0, 表示是內置命令. 返回 1, 表示不是內置命令.
execute_builtin_command()
{
  # 在傳遞過來的參數中,第一個參數是命令名,剩余的參數是該命令的參數.
  local cmdname="$1"
  # 從 BUILTIN_COMMAND 數組中獲取所給命令對應的處理函數.
  # 如果所給命令不是內置命令,會獲取為空.
  local cmdfunc="${BUILTIN_COMMAND["${cmdname}"]}"

  if [ -n "${cmdfunc}" ]; then
    # 將位置參數左移一位,移除命令名,剩下的就是該命令的參數.
    shift 1
    ${cmdfunc} "$@"
    # 無論執行內置命令是否報錯,都會返回 0,表示該命令是內置命令.
    return 0
  else
    return 1
  fi
}

# 處理 tiny shell 的命令簡寫.在所解析的配置文件中包含了支持的命令簡寫.
# 該函數的返回值表示所給命令名是否命令簡寫.
# 返回 0, 表示是命令簡寫. 返回 1, 表示不是命令簡寫.
execute_short_command()
{
  # 判斷所給的參數是否對應配置文件中的某個鍵名.如果是,將取出鍵值.
  local key="$1"
  # 從配置文件中獲取所給命令簡寫對應要執行的命令
  local cmd_value=$(get_value_by_key "${key}")
  if test -n "${cmd_value}"; then
    # 將位置參數左移一位,移除命令簡寫,剩下的就是命令的參數.
    shift 1
    # 下面要用 "$*" 來把所有參數組合成一個參數,再跟命令內容一起傳入
    # bach -c,確保 bash -c 把命令內容和所有參數都當成要執行的命令
    bash -c "$cmd_value $*"
    # 打印命令簡寫,以及該簡寫對應的命令,以便查看具體執行了什么命令.
    # 先執行命令,再打印命令內容. 由于有些命令的輸出很多,先打印命令
    # 內容的話,需要拉動終端滾動條,才能找到打印的命令內容,不便于查看.
    echo -e "\e[33m命令簡寫: ${key}. 命令: ${cmd_value} $*\e[0m"
    return 0
  else
    # 如果獲取到的鍵值為空,表示所給鍵名不是有效的命令簡寫,返回 1
    return 1
  fi
}

# 處理所給的內容.這個內容可能是內置命令,命令簡寫,或者命令本身.
handle_input_command()
{
  # 所給參數是要執行的命令名、以及命令參數. 如果命令名是配置的
  # 命令簡寫,會把該命令簡寫替換成對應的命令,再進行對應的命令.
  local inputcmd="$@"

  # if 語句可以直接判斷命令返回值是否為 0,并不是只能搭配 [ 命令使用.
  # 注意: 由于有的 tiny shell 內置命令接收參數,下面的 ${cmd_line}
  # 不能用雙引號括起來,否則多個參數會被當成一個參數.
  if execute_builtin_command ${inputcmd}; then
    # 先調用 execute_builtin_command 函數處理內置命令.如果所給
    # 命令是內置命令,則調用對應的函數進行處理,且不再往下執行.
    return 0
  elif execute_short_command ${inputcmd}; then
    # 調用 execute_short_command 函數處理命令簡寫.
    return 0
  else
    # 對于 tiny shell 不能執行的命令,嘗試用 bash -c 在 bash 中執行.
    bash -c "${inputcmd}"
    # 當 return 命令不加具體狀態碼時,它會返回上一條執行命令的狀態碼.
    return
  fi
}

# SIGINT 信號的處理函數.目前不做特殊處理,只是想在輸入CTRL-C后,不會終止
# 當前 tiny shell. 輸入 CTRL-C 還是可以終止 tiny shell 啟動的子 shell.
sigint_handler()
{
  # 當輸入 CTRL-C 后,終端只顯示"^C",但是不會自動換行,需要輸入回車才會
  # 換行,并重新輸出提示字符串. 而在交互式Bash中,輸入"^C"后,就會自動回
  # 車,并輸出提示字符串.這里模仿這個行為,先輸出一個回車,再輸出提示符.
  printf "\n${PROMPT}"
}

# 啟動 tiny shell 解釋器. 從標準輸入不停讀取、并執行所給命令.直到
# 使用 CTRL-D 輸入 EOF 為止, 或者輸入 quit 命令退出當前解釋器.
start_tinyshell()
{
  # 執行 python 命令,默認會打印 python 版本號和一句幫助提示.
  # 這里仿照這個行為,打印 tiny shel 版本號和一句幫助提示.
  echo -e "Tiny shell 1.0.0\nType 'help' for more information."

  # 捕獲SIGINT信號,以便輸入 CTRL-C 后,不會退出當前的 tiny shell.
  # 注意: 由于子shell會繼承父shell所忽略的信號,所以不能將 SIGINT 信號
  # 設成忽略,而是要指定一個處理函數. 當前 shell 所捕獲的信號不會被
  # 子 shell 繼承. 所以子 shell 還是可以被 CTRL-C 終止. 即,指定信號處理
  # 函數后,當前 tiny shell 不會被CTRL-C終止.但是當前 tiny shell 執行的
  # 命令會運行在子 shell 下,可以用 CTRL-C 終止運行在子 shell 下的命令.
  # 查看 man bash 對子 shell 的信號繼承關系說明如下:
  # traps caught by the shell are reset to the values inherited from
  # the shell's parent, and traps ignored by the shell are ignored
  trap "sigint_handler" SIGINT

  # 如果不使用 -e 選項,輸入上光標鍵, read 會讀取到 "^[[A";輸入下光標鍵,
  # read 會讀取到 "^[[B".而使用 -e 選項后,輸入上下光標鍵,不會讀取到亂碼,
  # 但是在子shell中,也不會返回歷史命令.因為shell腳本是在非交互模式下執行.
  # 可以使用 bash 的 -i 選項讓腳本在交互模式下運行,例如: "#/bin/bash -i"
  while read -ep "${PROMPT}" input; do
    # 傳遞參數給函數時,參數要用雙引號括起來,避免參數帶有空格時,會拆分
    # 成多個參數. 當輸入CTRL-C時, tiny shell 捕獲了這個信號,不會退出
    # 當前的 tiny shell.但是read命令會被中斷,此時讀取到的 input 為空.
    # 不需要對空行做處理,所以下面先判斷 input 變量值是否為空.
    if [ -n "${input}" ]; then
      handle_input_command "${input}"
      # 執行 history -s 命令把所給的參數添加到當前歷史記錄中.后續
      # 通過上下光標鍵獲取歷史命令,就可以獲取到新添加的命令.這個只
      # 影響當前 tiny shell 的歷史記錄,不會寫入外部shell的歷史記錄.
      history -s "${input}"
    fi
  done

  # 輸出一個換行.當用戶輸入CTRL-D結束執行后,需要換行顯示原先的終端提示符.
  echo
}

# 循環調用 getopts 命令處理選項參數.
while getopts "hlvi:ea:d:" opt; do
  # 調用parsecfg.sh腳本處理選項的函數來處理 "lvi:ea:d:" 這幾個選項.
  # 如果處理成功,就直接繼續讀取下一個選項,不再往下處理.
  # handle_config_option()函數要求傳入的選項以'-'開頭,而getopts命令
  # 返回的選項不帶有'-',所以下面在 ${opt} 前面加上一個 '-'.
  handle_config_option "-${opt}" "${OPTARG}"
  if [ $? -ne 127 ]; then
    continue
  fi

  case "$opt" in
    h) show_help ;;
    ?) echo "出錯: 異常選項,請使用 -h 選項查看腳本的幫助說明." ;;
  esac
done

# $# 大于0,說明提供了命令參數. $# 等于OPTIND減去1,說明傳入的參數都
# 是以 '-' 開頭的選項參數. 此時,直接結束執行,不需要再往下處理.
# 下面的 -a 表示兩個表達式都為真時才為真.表達式之間不要加小括號.
# Shell里面的小括號有特殊含義,跟C語言的小括號有些區別,加上會有問題.
if [ $# -gt 0 -a $# -eq $((OPTIND-1)) ]; then
  exit 0
fi

if [ $# -eq 0 ]; then
  # 當不帶任何參數時,默認啟用 tiny shell.
  start_tinyshell
else
  # 左移所給的命令參數,去掉已處理過的選項參數,只剩下非選項參數.
  shift $((OPTIND-1))
  # 執行腳本時,如果提供了非選項參數,那么第一個參數認為是命令簡寫,
  # 需要執行該命令簡寫對應的命令. 第一個參數之后的所有參數認為是
  # 命令的參數. 即,可以在命令簡寫之后提供參數來動態指定一些操作.
  execute_short_command "$@"
fi

exit

代碼關鍵點說明

使用 trap 命令捕獲信號

在 bash 中,可以使用 trap 命令捕獲信號,并指定信號處理函數。

捕獲信號后,可以避免收到某個信號終止腳本執行。

當前 tinyshell.sh 腳本使用 trap 命令捕獲 SIGINT 信號。

也就是 CTRL-C 鍵所發送的信號,避免按 CTRL-C 鍵會退出當前 tiny shell。

要注意的是,不能設置成忽略 SIGINT 信號。

在 bash 中,父 shell 所忽略的信號,也會被子 shell 所忽略。

除了內置命令之外,當前 tiny shell 所執行的命令運行在子 shell 下。

如果設置成忽略 SIGINT 信號,那么子 shell 也會忽略這個信號。

那么就不能用 CTRL-C 來終止子 shell 命令的執行。

例如,Android 系統的 adb logcat 命令會不停打印 log,需要按 CTRL-C 來終止。

此時,在 tiny shell 里面按 CTRL-C 就不能終止 adb logcat 的執行。

父 shell 所捕獲的信號,子 shell 不會繼承父 shell 所捕獲的信號。

子 shell 會繼承父 shell 的父進程的信號狀態。

父 shell 的父進程一般是外部 bash shell 進程。

而 bash shell 進程默認捕獲SIGINT并終止前臺進程。

即,雖然當前 tiny shell 捕獲了 SIGINT 信號,但是子 shell 并沒有捕獲該信號。

可以在 tiny shell 使用 CTRL-C 來終止子 shell 命令的執行。

使用 history -s 命令添加歷史記錄

在 tiny shell 執行命令后,默認不能用上下光標鍵查找到 tiny shell 自身執行的歷史命令。

為了可以查找到 tiny shell 自身執行的歷史命令,使用 history -s 命令添加命令到當前 shell 的歷史記錄。

這個命令只會影響當前 shell 的歷史記錄。

退出當前 shell 后,在外部 shell 還是看不到 tiny shell 所執行的命令。

由于這個 tiny shell 主要是為了執行命令簡寫。

這些命令簡寫只有 tiny shell 自身支持,不需要添加到 bash shell 的歷史記錄。

如果想要命令歷史信息添加到外部 shell 的歷史記錄,可以在退出 tinyshell.sh 腳本之前,執行 history -w ~/.bash_history 命令把歷史記錄寫入到 bash 自身的歷史記錄文件。

測試例子

把 tinyshell.sh 腳本放到 PATH 變量指定的可尋址目錄下。

查看 tinyshell.sh 腳本代碼,可知要解析的配置文件名是 tinyshellcmds.txt。

把前面貼出的命令簡寫配置信息寫入 tinyshellcmds.txt 文件。

把這個文件放到 HOME 目錄的 .liconfig 目錄下。

之后,就可以開始執行 tinyshell.sh 腳本。

當前的 tinyshell.sh 腳本可以執行內置命令、命令簡寫對應的命令、系統自身支持的命令。

當不提供任何命令參數時,會進入 tiny shell。

在 tiny shell 中,會不停接收用戶輸入并執行對應命令。

直到讀取到 EOF 、或者執行 quit 命令才會退出 tiny shell。

處理選項參數和直接處理命令簡寫的例子

下面是不進入 tiny shell,只處理選項參數和命令簡寫的例子:

$ tinyshell.sh -v
key='gl'    value='git log'
key='gp'    value='git pull --stat --no-tags $(git remote) $(git rev-parse --abbrev-ref HEAD)'
key='ll'    value='ls --color=auto -l'
key='ala'    value='adb logcat -b all -v threadtime'
$ tinyshell.sh ll
-rwxrwxr-x 1 xxx xxx 964 11月 14 17:37 tinyshell.sh
命令簡寫: ll. 命令: ls --color=auto -l

這里先執行 tinyshell.sh -v 命令,用鍵值對的形式列出支持的命令簡寫。

此時,只處理所給的選項參數,不會進入 tiny shell 里面。

tinyshell.sh ll 命令,提供了一個 ll 參數(兩個小寫字母 l)。

這個參數會被當成命令簡寫,然后執行該命令簡寫對應的命令。

執行結束后,不會進入 tiny shell 里面。

基于剛才列出的命令簡寫,可知 ll 對應 ls --color=auto -l 命令。

實際執行的也是這個命令。

進入 tiny shell 循環處理命令的例子

當不提供任何命令參數時,會進入 tiny shell 里面,循環處理命令。

具體例子如下所示:

$ tinyshell.sh
Tiny shell 1.0.0
Type 'help' for more information.
TinySh>>> help
下面列出 Tiny Shell 支持的內置命令列表和配置的命令簡寫列表.
輸入內置命令名或命令簡寫,會執行對應的命令.
也可以輸入系統自身支持的命令,會在 bash 中執行所給命令.

內置命令列表:
  debug: 所給第一個參數指定打開、或關閉調試功能. 其參數說明如下:
    on: 打開調試功能,會執行 bash 的 set -x 命令
    off: 關閉調試功能,會執行 bash 的 set +x 命令
  help: 打印當前幫助信息.
  quit: 退出當前 Tiny Shell.

命令簡寫列表:
key='gl'    value='git log'
key='gp'    value='git pull --stat --no-tags $(git remote) $(git rev-parse --abbrev-ref HEAD)'
key='ll'    value='ls --color=auto -l'
key='ala'    value='adb logcat -b all -v threadtime'
TinySh>>> date
2019年 12月 31日 星期二 17:46:41 CST
TinySh>>> ll -C
tinyshell.sh
命令簡寫: ll. 命令: ls --color=auto -l -C

當執行 tinyshell.sh 命令會進入 tiny shell 時,會打印一個 “TinySh>>>” 提示符。

在 tiny shell 中執行 help 命令可以查看支持的內置命令和命令簡寫。

在 tiny shell 中執行 date 命令打印當前的日期和時間。

當前的 tiny shell 自身不支持 date 命令。

這里執行了系統自身的 date 命令。

最后執行 ll -C 命令。

這里的 ll 是命令簡寫。后面的 -C 是對應命令的參數。

具體執行的命令是 ls --color=auto -l -C。

ls 命令的 -C 選項會多列顯示文件名,覆蓋了 -l 選項的效果。

由于 -l 選項的效果被覆蓋,輸出結果沒有打印文件的詳細信息,只列出文件名。

可以看到,在命令簡寫之后,可以再提供其他的命令參數。

即,可以只配置比較長的命令前綴部分,一些簡單的參數可以動態提供。

不需要在配置文件中添加很多內容相似、只有細微差異的配置項。

看完上述內容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注億速云行業資訊頻道,感謝您對億速云的支持。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

柘荣县| 彰化市| 新巴尔虎左旗| 西平县| 洛宁县| 满洲里市| 鹤壁市| 桐庐县| 乌鲁木齐县| 游戏| 平南县| 鄱阳县| 达日县| 卢龙县| 鹤庆县| 视频| 昌乐县| 伊金霍洛旗| 邯郸县| 丰县| 驻马店市| 巫山县| 石嘴山市| 得荣县| 铜梁县| 营山县| 大埔县| 嘉祥县| 秦皇岛市| 平塘县| 巴塘县| 滁州市| 吴旗县| 开封县| 祥云县| 海安县| 东方市| 赤峰市| 偃师市| 乡城县| 邮箱|