Linux

從 bash 中的函式中獲取和設置腳本參數

  • December 20, 2019

語境:

我有一個舊的 bash 腳本,其中有一大段解析它的參數。現在我需要呼叫這個部分兩次,所以我打算把它移到一個函式中以避免程式碼重複。

問題:

在該部分中set --shift$@被使用,這意味著它們將不再適用於腳本,而是適用於函式,這是錯誤的。

問題:

從函式內部,有沒有辦法獲取和設置腳本參數?

方案:

像這樣的東西:

#!/bin/bash
# > 5000 lines

process_arg()
{
  # about 650 lines

  # set --
  # $@ $* $1 ...
  # shift <n>
}

while (( $# > 0 )); do
  case $1 in
     <cond>)
        <some code here>
        process_arg
        <some more code here>

     <other conditions and code here>

     *)
        <some different code here>
        process_arg
        <some different more code here>
  esac
  shift 1
done

免責聲明:

從上面的討論中,我實現了一個解決方案。到目前為止,這不是我夢寐以求的,因為冗長 $ {args_array[1]}, compared to $ 1. 降低原始碼的可讀性。因此,仍然歡迎改進或更好的解決方案。

來源:

經測試,是這樣的:

#!/bin/bash 

#########################    
# DEBUG
#########################    

# set -x
PS4='${xchars:-+} ${BASH_SOURCE}:${LINENO} (${FUNCNAME[@]}) + ' # full stack

#########################    
# INITIAL ARGS FOR TEST
#########################    
set -- a b c d e f g h

#########################    
# UTILITIES
#########################    

args_array=( "$@" ) # script args here

args_shift() # Usage readability OK, replaces shift <n>
{
  typeset n=${1:-1}

              echo "args_shift $1 in ${FUNCNAME[1]} -- ${args_array[@]}"

  args_array=( "${args_array[@]:$n}" ) # ${1:-1} unsupported in this context

              echo "args_shift $1 in ${FUNCNAME[1]} ++ ${args_array[@]}"
}

args_set() # Usage readability OK, replaces set -- <args>
{
              echo "args_set $@ in ${FUNCNAME[1]} -- ${args_array[@]}"

  args_array=( "$@" ) # function args here

              echo "args_set $@ in ${FUNCNAME[1]} ++ ${args_array[@]}"
}

# Usage
# search/replace OK, and good readability afterward
# shift <n> ---> args_shift <n>
# set -- <args> ---> args_set <args>

# search/replace OK, but bad readability afterward, and refactoring--
# $@ ---> ${args_array[@]}
# $# ---> ${#args_array[@]}
# $1 ---> ${args_array[0]}   !!! 1 -> 0
# $2 ---> ${args_array[1]}   !!! 2 -> 1
# etc

#########################
# TEST
#########################    

f()
{
  args_shift
}

g()
{
  args_set A B C D
}

# main

echo "main -- ${args_array[@]}"
f
args_shift 2
f
g
args_shift
f
echo "main ++ ${args_array[@]}"

輸出:

main -- a b c d e f g h
args_shift  in f -- a b c d e f g h
args_shift  in f ++ b c d e f g h
args_shift 2 in main -- b c d e f g h
args_shift 2 in main ++ d e f g h
args_shift  in f -- d e f g h
args_shift  in f ++ e f g h
args_set A B C D in g -- e f g h
args_set A B C D in g ++ A B C D
args_shift  in main -- A B C D
args_shift  in main ++ B C D
args_shift  in f -- B C D
args_shift  in f ++ C D
main ++ C D

評論:

  1. 有效但不是最易讀的解決方案,重構也不是那麼輕鬆,因為有幾種使用形式需要考慮: $ 1, more or less $ {1$$ :/ $$$$ ^} $$} 或 ${!1$$ :/ $$$$ ^} $$} 等,同時避免那些在函式,awk,perl 等。
  2. 對於某些人來說,由於變數名在 bash 中區分大小寫,而且我認為或多或少很少使用,因此 on 可以使用 A 或 _A 而不是 args_array 但是,根據我的口味, $ {A[1]} or so is even less readable in a long source than $ {args_array$$ 1 $$}.

我的情況:

至少有 616 次事件需要小心處理(有些在函式、awk 或 perl 腳本等中)

for s in shift 'set --' '$@' '${@' '$*' '${*' '$1' '${1' '$2' '${2' '$3' '${3' '$4' '${4' '$5' '${5'; do
  printf '%-10s: %d\n' "$s " "$(fgrep $s <script>|wc -l)"
done # |awk '{sum+=$NF};END{print sum}'

shift     : 44
set --    : 189
$@        : 39
${@       : 2
$*        : 7
${*       : 0
$1        : 182
${1       : 79
$2        : 48
${2       : 3
$3        : 15
${3       : 0
$4        : 8
${4       : 0
$5        : 0
${5       : 0

引用自:https://unix.stackexchange.com/questions/557699