I'm writing a POSIX shell function based on bash declare -p
behaviour, the difference being that it targets POSIX shells.
I've set a few goals:
declare -p
does.My current problem is in #4 but any advice about the other points is welcome ;-)
Here's the code. The first awk
validates the arguments and generates a shell command for updating $@
, which is then processed with eval
. The second awk
will then print the variables declarations with single-quote escaping:
edit: fixed the code based on @thatotherguy suggestion
#!/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: $?"
output:
declare_p: var-x: not a valid identifier # (stderr)
var1=' a b '
var2='a'\''b'
var3='a
b'
var4='
'
return code: 1
Here's a corrected version of the code, based on @thatotherguy advice:
#!/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
}
' -- "$@"
}
The first awk
forces the eval
to end with a false
command in case of an invalid parameter; the return code is passed to the second awk
with -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 >