语境:
我有一个旧的 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[:/][^}]} 等,同时避免使用 function、awk、perl 等。
- 对于某些人来说,由于变量名在 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