如何在POSIX Shell脚本中用函数封装含shift的重复case逻辑?
POSIX Shell中封装位置参数处理函数的安全性问题
问题背景
POSIX兼容Shell脚本仅支持位置参数数组,不支持其他数组。主脚本中多个case分支需执行包含位置参数shift操作的重复代码(实际代码超10行),为减少代码重复,需将该逻辑封装为函数。尝试了一种通过函数处理参数后用eval重置位置参数的方案,已修复换行截断问题,但担忧仅转义单引号的方式是否足够安全,参考getopt的实现发现,除tcsh外它也仅转义单引号。
原脚本示例
#!/bin/sh # /tmp/test.sh case "$1" in ( *one* ) echo 1 ; shift ; echo 'more' ;; ( *two* ) echo 2 ; shift ; echo 'more' ;; ( *three* ) echo 3 ; shift ; echo 'more' ;; ( other ) echo 'other' ;; ( * ) : esac printf '[%s]\n' "$@"
原脚本测试输出
$ /tmp/test.sh 'arg one' '3 spaces' "with 'quotes'" 1 more [3 spaces] [with 'quotes']
尝试的解决方案脚本
#!/bin/sh # /tmp/test.sh newline=$(printf '\n ') newline=${newline% } func() { # Perform something based on arg $1. # But do not echo here. echo here will mess up with the return value. # Actual code do not echo here. # echo "$1" shift shift # Escape dangerous characters and preserve spaces modified_args="" for arg in "$@"; do trailing_newlines=${arg##*[!${newline}]} modified_args="${modified_args} '$(printf "%s" "$arg" | sed "s/'/'\\\\''/g")${trailing_newlines}'" done printf '%s' "$modified_args" } case "$1" in ( *one* ) args=$(func 1 "$@") ; eval set -- "$args" ;; ( *two* ) args=$(func 2 "$@") ; eval set -- "$args" ;; ( *three* ) args=$(func 3 "$@") ; eval set -- "$args" ;; ( other ) echo 'other' ;; ( * ) : esac printf '[%s]\n' "$@"
解决方案测试输出
$ newline=$(printf '\n ') $ newline=${newline% } $ /tmp/test.sh 'arg one' '3 spaces' '' "with 'quotes'" 'double "quote"' "$newline line two $newline line three $newline$newline" [3 spaces] [] [with 'quotes'] [double "quote"] [ line two line three ]
安全性疑问
经@KamilCuk提示修改后已修复换行截断问题,但仍担忧仅转义单引号的方式是否足够安全。参考getopt的实现发现,除tcsh外它也仅转义单引号。
内容的提问来源于stack exchange,提问作者midnite
相关产品推荐
相关产品推荐

