1: 2# RCSid: 3# $Id: safe_eval.sh,v 1.28 2026/04/22 16:36:32 sjg Exp $ 4# 5# @(#) Copyright (c) 2023-2026 Simon J. Gerraty 6# 7# SPDX-License-Identifier: BSD-2-Clause 8# 9# Please send copies of changes and bug-fixes to: 10# sjg@crufty.net 11 12_SAFE_EVAL_SH=: 13 14# does local *actually* work? 15local_works() { 16 local _fu 17} 18 19if local_works > /dev/null 2>&1; then 20 _local=local 21else 22 _local=: 23fi 24 25## 26# safe_set [xtras] 27# 28# return a safe variable setting 29# any non-alphanumeric chars other than those in "xtras" 30# will be replaced with '_' 31# Lines containing `` or $() are too likely to result in syntax errors 32# so just delete them. 33# 34# "xtras" should be used with caution and cannot include ';' 35# 36safe_set() { 37 ${SED:-sed} -e 's/^[ ]*//;s/[ ]*#.*//;s/^:.*//' \ 38 -e '/`/d' -e '/\$(/d' \ 39 -e '/^[A-Za-z_][A-Za-z0-9_]*=/!d;s;[^A-Za-z0-9_. "'"$1"'$,/=:+-];_;g;' \ 40 -e '/=.*_.*[ ]/s,=\(.*\),="\1",;s,"",",g' 41} 42 43## 44# safe_eval [file] 45# 46# eval variable assignments only from file 47# taking care to eliminate any shell meta chars 48# 49safe_eval() { 50 eval `cat "$@" | safe_set` 51} 52 53## 54# safe_eval_export [file] 55# 56# eval variable assignments only from file 57# taking care to eliminate any shell meta chars 58# export any variables thus set 59# 60safe_eval_export() { 61 eval `cat "$@" | safe_set | ${SED:-sed} 's/^\([^=]*\)=.*/&; export \1/'` 62} 63 64## 65# safe_dot file [...] 66# 67# feed all "file" that exist to safe_eval 68# 69safe_dot() { 70 eval $_local ef ex f rc 71 ef= 72 ex= 73 rc=1 74 while : 75 do 76 case "$1" in 77 --export) ex=_export; shift;; 78 *) break;; 79 esac 80 done 81 for f in "$@" 82 do 83 test -s "$f" -a -f "$f" || continue 84 : check for space or tab in "$f" 85 case "$f" in 86 *[[:space:]]*|*" "*|*" "*) # we cannot do this efficiently 87 dotted="$dotted $f" 88 safe_eval$ex "$f" 89 rc=$? 90 continue 91 ;; 92 esac 93 ef="${ef:+$ef }$f" 94 dotted="$dotted $f" 95 done 96 test -z "$ef" && return $rc 97 safe_eval$ex $ef 98 return 0 99} 100 101case /$0 in 102*/safe_eval*) 103 case "$1" in 104 dot|eval|set) op=safe_$1; shift; $op "$@";; 105 *) safe_dot "$@";; 106 esac 107 ;; 108esac 109