我需要为自己的脚本创建一个配置文件:这里是一个示例:
脚本:
#!/bin/bash
source /home/myuser/test/config
echo "Name=$nam" >&2
echo "Surname=$sur" >&2
内容/home/myuser/test/config
:
nam="Mark"
sur="Brown"
可行!
我的问题是:这是正确的方法还是其他方法?
我需要为自己的脚本创建一个配置文件:这里是一个示例:
脚本:
#!/bin/bash
source /home/myuser/test/config
echo "Name=$nam" >&2
echo "Surname=$sur" >&2
内容/home/myuser/test/config
:
nam="Mark"
sur="Brown"
可行!
我的问题是:这是正确的方法还是其他方法?
Answers:
source
是不安全的,因为它将执行任意代码。您可能不必担心,但是如果文件许可权不正确,则具有文件系统访问权限的攻击者有可能通过将代码注入由其他方式保护的脚本(如初始化脚本。
到目前为止,我能够确定的最佳解决方案是笨拙的重塑车轮解决方案:
myscript.conf
password=bar
echo rm -rf /
PROMPT_COMMAND='echo "Sending your last command $(history 1) to my email"'
hostname=localhost; echo rm -rf /
使用source
,它将运行echo rm -rf /
两次,以及更改运行用户的$PROMPT_COMMAND
。相反,请执行以下操作:
myscript.sh(Bash 4)
#!/bin/bash
typeset -A config # init array
config=( # set default values in config array
[username]="root"
[password]=""
[hostname]="localhost"
)
while read line
do
if echo $line | grep -F = &>/dev/null
then
varname=$(echo "$line" | cut -d '=' -f 1)
config[$varname]=$(echo "$line" | cut -d '=' -f 2-)
fi
done < myscript.conf
echo ${config[username]} # should be loaded from defaults
echo ${config[password]} # should be loaded from config file
echo ${config[hostname]} # includes the "injected" code, but it's fine here
echo ${config[PROMPT_COMMAND]} # also respects variables that you may not have
# been looking for, but they're sandboxed inside the $config array
myscript.sh(与Mac / Bash 3兼容)
#!/bin/bash
config() {
val=$(grep -E "^$1=" myscript.conf 2>/dev/null || echo "$1=__DEFAULT__" | head -n 1 | cut -d '=' -f 2-)
if [[ $val == __DEFAULT__ ]]
then
case $1 in
username)
echo -n "root"
;;
password)
echo -n ""
;;
hostname)
echo -n "localhost"
;;
esac
else
echo -n $val
fi
}
echo $(config username) # should be loaded from defaults
echo $(config password) # should be loaded from config file
echo $(config hostname) # includes the "injected" code, but it's fine here
echo $(config PROMPT_COMMAND) # also respects variables that you may not have
# been looking for, but they're sandboxed inside the $config array
如果在我的代码中发现安全漏洞,请回复。
*
正确处理输入,但是Bash中的什么可以很好地处理该字符?
my\\password
这是一个干净便携的版本,在Mac和Linux上均与Bash 3及更高版本兼容。
它在一个单独的文件中指定所有默认值,以避免所有shell脚本中都需要庞大,混乱,重复的“默认值”配置功能。它使您可以选择是否读取默认后备:
config.cfg:
myvar=Hello World
config.cfg.defaults:
myvar=Default Value
othervar=Another Variable
config.shlib(这是一个库,因此没有shebang行):
config_read_file() {
(grep -E "^${2}=" -m 1 "${1}" 2>/dev/null || echo "VAR=__UNDEFINED__") | head -n 1 | cut -d '=' -f 2-;
}
config_get() {
val="$(config_read_file config.cfg "${1}")";
if [ "${val}" = "__UNDEFINED__" ]; then
val="$(config_read_file config.cfg.defaults "${1}")";
fi
printf -- "%s" "${val}";
}
test.sh(或您要读取配置值的任何脚本):
#!/usr/bin/env bash
source config.shlib; # load the config library functions
echo "$(config_get myvar)"; # will be found in user-cfg
printf -- "%s\n" "$(config_get myvar)"; # safer way of echoing!
myvar="$(config_get myvar)"; # how to just read a value without echoing
echo "$(config_get othervar)"; # will fall back to defaults
echo "$(config_get bleh)"; # "__UNDEFINED__" since it isn't set anywhere
测试脚本的说明:
printf
线?嗯,这是您应该意识到的事情:这echo
是打印您无法控制的文本的错误命令。即使您使用双引号,它也会解释标志。尝试将myvar
(in config.cfg
)设置为-e
,您会看到一个空行,因为echo
会认为这是一个标志。但是printf
没有那个问题。在printf --
写着“打印本,并没有解释任何东西作为标志”,并"%s\n"
说“格式化输出与尾随换行符的字符串,最后是最后一个参数是对的printf格式的值。myvar="$(config_get myvar)";
。如果要将它们打印到屏幕上,建议您使用printf来防止用户配置中可能存在任何与回声不兼容的字符串,这是绝对安全的。但是,如果用户提供的变量不是您要回显的字符串的第一个字符,则echo会很好,因为这是唯一可以解释“ flags”的情况,因此echo "foo: $(config_get myvar)";
很安全,因为“ foo”不会以破折号开头,因此告诉echo字符串的其余部分也不是其标志。:-)config.cfg.defaults
在致电时,我放弃了对它们的定义$(config_get var_name "default_value")
。tritarget.org/static/…–
解析配置文件,不要执行它。
我目前正在编写一个使用非常简单的XML配置的应用程序:
<config>
<username>username-or-email</username>
<password>the-password</password>
</config>
在shell脚本(“应用程序”)中,这就是我想要获得用户名的方法(或多或少,我将其放在了shell函数中):
username="$( xml sel -t -v '/config/username' "$config_file" )"
该xml
命令是XMLStarlet,可用于大多数Unices。
我正在使用XML,因为应用程序的其他部分还处理XML文件中编码的数据,因此这是最简单的。
如果您更喜欢JSON,那么有jq
一个易于使用的Shell JSON解析器。
我的配置文件在JSON中看起来像这样:
{
"username": "username-or-email",
"password": "the-password"
}
然后我将在脚本中获取用户名:
username="$( jq -r '.username' "$config_file" )"
eval
用于设置多个值,那么您正在执行配置文件的选定部分:-)。
eval
任何东西。与健壮性,代码量,易用性和可维护性相比,将标准格式与现有解析器一起使用(即使是外部实用程序)对性能的影响可以忽略不计。
最常见,有效和正确的方法是使用source
或.
作为简写形式。例如:
source /home/myuser/test/config
要么
. /home/myuser/test/config
但是,考虑到可以插入其他代码的情况,使用附加的外部配置文件会引发安全问题。有关更多信息,包括有关如何检测和解决此问题的信息,我建议您看一下http://wiki.bash-hackers.org/howto/conffile#secure_it的“保护它”部分。
我在脚本中使用了这个:
sed_escape() {
sed -e 's/[]\/$*.^[]/\\&/g'
}
cfg_write() { # path, key, value
cfg_delete "$1" "$2"
echo "$2=$3" >> "$1"
}
cfg_read() { # path, key -> value
test -f "$1" && grep "^$(echo "$2" | sed_escape)=" "$1" | sed "s/^$(echo "$2" | sed_escape)=//" | tail -1
}
cfg_delete() { # path, key
test -f "$1" && sed -i "/^$(echo $2 | sed_escape).*$/d" "$1"
}
cfg_haskey() { # path, key
test -f "$1" && grep "^$(echo "$2" | sed_escape)=" "$1" > /dev/null
}
应该支持所有字符组合,但键中不能包含键=
,因为那是分隔符。其他任何工作。
% cfg_write test.conf mykey myvalue
% cfg_read test.conf mykey
myvalue
% cfg_delete test.conf mykey
% cfg_haskey test.conf mykey || echo "It's not here anymore"
It's not here anymore
另外,由于它不使用任何source
/eval
就我的情况而言,source
还是.
很好,但是我想支持FOO=bar myscript.sh
优先于已配置变量的本地环境变量(即)。我还希望该配置文件对于以前用来获取配置文件的用户来说是可编辑的,并且要使其舒适,并使其尽可能地小/简单,以免分散我的小脚本的工作重点。
这是我想出的:
CONF=${XDG_CONFIG_HOME:-~/config}/myscript.sh
if [ ! -f $CONF ]; then
cat > $CONF << CONF
VAR1="default value"
CONF
fi
. <(sed 's/^\([^=]\+\) *= *\(.*\)$/\1=${\1:-\2}/' < $CONF)
从本质上讲,它检查变量定义(在空格方面不太灵活)并重写这些行,以便将该值转换为该变量的默认值,并且如XDG_CONFIG_HOME
上面的变量一样,该变量如被发现则保持不变。它将源此配置文件的更改版本,然后继续。
将来的工作可以使sed
脚本更加健壮,过滤掉看起来很奇怪或不是定义的行,等等,不会在行尾注释处中断-但这对我来说已经足够了。