POSIX shell 相当于 bash“declare -p”

问题描述 投票:0回答:2

我正在编写一个基于 bash

declare -p
行为的 POSIX shell 函数,不同之处在于它针对的是 POSIX shell。

我设定了一些目标:

  1. 创建一个强大的函数
    => 对于当前代码来说还可以(我认为)。
  2. 不要定义任何变量(以避免冲突)
    => 好的,当前的实现没有定义任何变量
  3. 尽可能少的叉子
    => 目前有 2 个叉子。一个就很好,但我不确定是否可以在不定义任何变量的情况下实现。
  4. 当参数指定无效变量名时,返回状态 1
    => 目前情况并非如此 当前的行为是处理好的参数并跳过无效的参数(打印错误消息并将返回状态设置为 1),就像
    declare -p
    所做的那样。

我当前的问题在#4,但欢迎任何有关其他点的建议;-)

这是代码。第一个

awk
验证参数并生成用于更新
$@
的 shell 命令,然后使用
eval
进行处理。然后,第二个
awk
将打印带有单引号转义的变量声明:

编辑:根据@thatotherguy建议修复了代码

#!/bin/sh

declare_p() {
    eval "$(
        awk -v funcname="declare_p" '
            BEGIN {
                output = "set --"
                for (i = 2; i < ARGC; i++) {
                    if (ARGV[i] ~ /^[[:alpha:]_][[:alnum:]_]*$/) {
                        output = output" "ARGV[i]" \042\044"ARGV[i]"\042"
                    } else {
                        print funcname": "ARGV[i]": not a valid identifier" > "/dev/stderr"
                        error = 1
                        # exit 1
                    }
                }
                print output
                exit
            }
            END { if (error) print "false" }
        ' -- "$@"
    )" # || return 1
    awk -v rc="$?" '
        BEGIN {
            for (i = 2; i < ARGC; i += 2) {
                gsub(/\047/,"\047\134\047\047",ARGV[i+1])
                print ARGV[i] "=" "\047" ARGV[i+1] "\047"
            }
            exit rc
        }
    ' -- "$@"
}
示例
#!/bin/sh

var1=" a  b "
var2="a'b"
var3="a
b"
var4="
"

declare_p var1 var2 var3 var4 var-x
echo "return code: $?"

输出:

declare_p: var-x: not a valid identifier # (stderr)
var1=' a  b '
var2='a'\''b'
var3='a
b'
var4='
'
return code: 1
bash sh
2个回答
1
投票

只是:

declare_p() {
    while [ "$#" -ne 0 ]; do
       if ! printf %s "$1" | grep -qx '[a-zA-Z_][a-zA-Z_0-9]*'; then
             echo "declare_p: Invalid variable name: $1" >&2
             return 1
       fi
       printf "%s='%s'\n" "$1" "$(
            eval "printf %s \"\$$1\"" |
            sed "s/'/'\\\\''/"
       )"
       shift
    done
}

# unit test
var=$(seq 255 | xargs printf %02x\\n | xxd -r -p)
eval "$(var2="$var" ; declare_p var2)"
if [ "$var" = "$var2" ]; then echo FINE; fi

当然,您可以将

sed
grep
优化为一些神奇的 awk - 如果您想要性能使用不同的编程语言,我认为不值得花时间。


0
投票

这是根据@thatotherguy 建议修正后的代码版本:

#!/bin/sh

declare_p() {
    eval "$(
        awk -v funcname="declare_p" '
            function perror(str) { print str | "cat 1>&2" }
            BEGIN {
                output = "set --"
                for (i = 2; i < ARGC; i++) {
                    if (ARGV[i] ~ /^[[:alpha:]_][[:alnum:]_]*$/) {
                        output = output" "ARGV[i]" \042\044"ARGV[i]"\042"
                    } else {
                        perror(funcname ": " ARGV[i] ": not a valid identifier")
                        error = 1
                        # exit 1
                    }
                }
                print output
                exit
            }
            END { if (error) print "false" }
        ' -- "$@"
    )" # || return 1
    awk -v rc="$?" '
        BEGIN {
            for (i = 2; i < ARGC; i += 2) {
                gsub(/\047/,"\047\134\047\047",ARGV[i+1])
                print ARGV[i] "=" "\047" ARGV[i+1] "\047"
            }
            exit rc
        }
    ' -- "$@"
}

第一个

awk
在参数无效的情况下强制
eval
false
命令结束;返回代码通过
awk
 传递给第二个 
-v rc="$?"


用例示例:
var1="123 456 789"
var2=" abc "

{
declare_p var1 var2
cat <<'EOF' # quoted heredocs don't expand anything so you can write your raw commands here
printf '<%s>\n' "$var1" "$var2"
EOF
} | ssh user@host sh
<123 456 789>
< abc >
© www.soinside.com 2019 - 2024. All rights reserved.