Bashの関数内でスクリプトパラメータを取得して設定する

Bashの関数内でスクリプトパラメータを取得して設定する

コンテキスト:

引数を解析する大きなセクションを持つ古いbashスクリプトがあります。この部分を2回呼び出す必要があるので、コードの重複を避けるために関数に移動します。

質問:

このセクションでは、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

婦人声明:

上記の議論に基づいてソリューションを実装しました。 ${args_array[1]}が$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[:/][^}]}などを使用し、関数、awk、Perlなどを使用しないでください。
  2. 一部の人にとっては、変数名はbashで大文字と小文字を区別し、ほとんど使用されていないと思うので、args_arrayの代わりにAまたは_Aを使用できますが、私の好みに応じて$ {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

関連情報