为我的 shell 脚本使用配置文件

Pol*_*len 40 shell bash shell-script

我需要为我自己的脚本创建一个配置文件:
这是一个例子:

脚本:

#!/bin/bash
source /home/myuser/test/config
echo "Name=$nam" >&2
echo "Surname=$sur" >&2
Run Code Online (Sandbox Code Playgroud)

内容/home/myuser/test/config

nam="Mark"
sur="Brown"
Run Code Online (Sandbox Code Playgroud)

那个有效!

我的问题:这是正确的方法还是有其他方法?

Mik*_*kel 28

source不安全,因为它会执行任意代码。这对您来说可能不是问题,但如果文件权限不正确,具有文件系统访问权限的攻击者可能会通过将代码注入到由其他安全脚本加载的配置文件中来作为特权用户执行代码,例如初始化脚本。

到目前为止,我能够确定的最佳解决方案是笨拙的重新发明轮子解决方案:

我的脚本文件

password=bar
echo rm -rf /
PROMPT_COMMAND='echo "Sending your last command $(history 1) to my email"'
hostname=localhost; echo rm -rf /
Run Code Online (Sandbox Code Playgroud)

使用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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

如果您在我的代码中发现安全漏洞,请回复。


gw0*_*gw0 18

这是一个干净且便携的版本,它与 Mac 和 Linux 上的 Bash 3 及更高版本兼容。

它在一个单独的文件中指定所有默认值,以避免在所有 shell 脚本中需要一个巨大的、混乱的、重复的“默认值”配置函数。它可以让你在有或没有默认后备的阅读之间进行选择:

配置文件

myvar=Hello World
Run Code Online (Sandbox Code Playgroud)

config.cfg.defaults

myvar=Default Value
othervar=Another Variable
Run Code Online (Sandbox Code Playgroud)

config.shlib(这是一个库,所以没有shebang-line):

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}";
}
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

测试脚本说明:

  • 请注意,test.sh 中 config_get 的所有用法都用双引号括起来。通过将每个 config_get 用双引号括起来,我们确保变量值中的文本永远不会被误解为标志。它确保我们正确保留空格,例如配置值中的一行中的多个空格。
  • 那条printf线是什么?嗯,这是您应该注意的事情:echo是用于打印您无法控制的文本的错误命令。即使您使用双引号,它也会解释标志。尝试将myvar(in config.cfg)设置为-e,您将看到一个空行,因为echo会认为它是一个标志。但是printf没有这个问题。在printf --写着“打印本,并没有解释任何东西作为标志”,并"%s\n"说“格式化输出与尾随换行符的字符串,最后是最后一个参数是对的printf格式的值。
  • 如果您不打算将值回显到屏幕上,那么您只需正常分配它们,例如myvar="$(config_get myvar)";. 如果您要将它们打印到屏幕上,我建议使用 printf 对可能在用户配置中的任何回显不兼容的字符串是完全安全的。但是,如果用户提供的变量不是您要回显的字符串的第一个字符,则echo 很好,因为这是可以解释“标志”的唯一情况,因此类似的东西echo "foo: $(config_get myvar)";是安全的,因为“foo”没有以破折号开头,因此告诉 echo 字符串的其余部分也不是它的标志。:-)

  • 我真的很喜欢这个版本。我在调用 `$(config_get var_name "default_value")` 时删除了 `config.cfg.defaults` 来代替定义它们。https://tritarget.org/static/Bash%2520script%2520configuration%2520file%2520format.html (3认同)

Kus*_*nda 18

解析配置文件,不要执行。

我目前正在编写一个使用极其简单的 XML 配置的应用程序:

<config>
    <username>username-or-email</username>
    <password>the-password</password>
</config>
Run Code Online (Sandbox Code Playgroud)

在 shell 脚本(“应用程序”)中,这就是我为获取用户名所做的事情(或多或少,我已将其放入 shell 函数中):

username=$( xmlstarlet sel -t -v '/config/username' "$config_file" )
Run Code Online (Sandbox Code Playgroud)

xmlstarlet命令是XMLStarlet,这是适用于大多数Unix系统。在某些系统上,它安装为xml.

我正在使用 XML,因为应用程序的其他部分也处理以 XML 文件编码的数据,所以它是最简单的。

如果您更喜欢 JSON,这里有jq一个易于使用的 shell JSON 解析器。

我的配置文件在 JSON 中看起来像这样:

{                                 
  "username": "username-or-email",
  "password": "the-password"      
}         
Run Code Online (Sandbox Code Playgroud)

然后我会在脚本中获取用户名:

username=$( jq -r .username "$config_file" )
Run Code Online (Sandbox Code Playgroud)

还有 TOML(“Tom's Obvious Minimal Language”),具有多种语言的解析器。我个人最喜欢的解析器tomlqhttps://kislyuk.github.io/yq/yq分发版的一部分(在幕后使用)。jq

配置文件的 TOML 版本看起来像

username = "username-or-email"
password = "the-password"
Run Code Online (Sandbox Code Playgroud)

(字符串将是 JSON 编码的)......并且从中获取数据将与 JSON 情况一样简单(因为tomlq它构建在 之上jq):

username = "username-or-email"
password = "the-password"
Run Code Online (Sandbox Code Playgroud)

  • +1 表示“解析配置文件,不要执行它” (2认同)

Asc*_*ius 7

大多数用户(尽管容器不多)已经拥有该git二进制文件。因此,为什么不使用git config专用的非冲突配置文件来进行应用程序配置管理,如下例所示?

# Set
$ git config -f ~/.myapp core.mykey myval

# Get
$ git config -f ~/.myapp core.mykey
myval

# Get invalid
$ git config -f ~/.myapp core.mykey
$ echo $?
1

# List
git config -f ~/.myapp -l
core.mykey=myval

# View
$ cat ~/.myapp 
[core]
    mykey = myval
Run Code Online (Sandbox Code Playgroud)

有关其他命令,请参阅其手册页。首先确保配置文件存在是明智的:

touch -a ~/.myapp
Run Code Online (Sandbox Code Playgroud)


小智 6

我在我的脚本中使用它:

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
}
Run Code Online (Sandbox Code Playgroud)

应该支持每个字符组合,除了键不能包含=在其中,因为这是分隔符。其他任何事情都有效。

% 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
Run Code Online (Sandbox Code Playgroud)

此外,这是完全安全的,因为它不使用sourceor eval


小智 5

最常见、最有效、最正确的方法是使用source, 或.作为速记形式。例如:

source /home/myuser/test/config
Run Code Online (Sandbox Code Playgroud)

或者

. /home/myuser/test/config
Run Code Online (Sandbox Code Playgroud)

然而,需要考虑的是,考虑到可以插入额外的代码,使用额外的外部来源配置文件可能会引发安全问题。有关更多信息,包括如何检测和解决此问题,我建议您查看http://wiki.bash-hackers.org/howto/conffile#secure_it的“保护它”部分

  • 我对那篇文章抱有很高的期望(我的搜索结果中也出现了),但作者提出的尝试使用正则表达式来过滤恶意代码的建议是徒劳的。 (10认同)