从 bash 中的函数内获取和设置脚本参数

从 bash 中的函数内获取和设置脚本参数

语境:

我有一个旧的 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

答案1

免责声明:

根据上面的讨论,我实现了一个解决方案。到目前为止,这还不是我梦想的,因为与 $1 相比,${args_array[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,或多或少 ${1[:/][^}]} 或 ${!1[:/][^}]} 等,同时避免使用 function、awk、perl 等。
  2. 对于某些人来说,由于变量名在 bash 中区分大小写,并且我认为或多或少很少使用,因此可以使用 A 或 _A 而不是 args_array 但根据我的口味, ${A[1]} 左右甚至更不可读源代码比 ${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

相关内容