如何在Bash中引用文件以使用变量?

185
我希望调用一个存储变量的配置文件。在Bash中如何实现呢?
配置文件会定义一些变量(例如,CONFIG.FILE):
production="liveschool_joe"
playschool="playschool_joe"

这个脚本将使用这些变量:

#!/bin/bash
production="/REFERENCE/TO/CONFIG.FILE"
playschool="/REFERENCE/TO/CONFIG.FILE"
sudo -u wwwrun svn up /srv/www/htdocs/$production
sudo -u wwwrun svn up /srv/www/htdocs/$playschool

我如何让Bash执行类似的操作?我需要使用AWK, sed等工具吗?

9个回答

296

简短回答

使用 source 命令。


使用 source 的示例

例如:

config.sh

#!/usr/bin/env bash
production="liveschool_joe"
playschool="playschool_joe"
echo $playschool

脚本.sh

#!/usr/bin/env bash
source config.sh
echo $production

请注意,此示例中sh ./script.sh的输出结果为:

~$ sh ./script.sh 
playschool_joe
liveschool_joe

这是因为source命令实际上运行了程序。从config.sh中的所有内容都会被执行。


另一种方式

您可以使用内置的export命令,通过获取和设置"环境变量"也可以实现此目的。

运行exportecho $ENV就足以访问变量。访问环境变量与访问本地变量的方式相同。

要设置它们,请执行以下操作:

export variable=value

在命令行中。所有脚本都将能够访问此值。


这个需要config.sh有执行权限才能工作吗? - Ramiro
要恢复数组,您必须单独存储每个数组条目的值(而不是从declare -p中存储完整的数组单行),就像somArray[3]="abc"一样,依此类推... - Aquarius Power
1
@Ramiro 不,它没有。我已经检查过了。 :) - Matt Komarnicki
在我的情况下,source命令会抛出一个错误,显然取决于使用的shell - https://dev59.com/a2Yr5IYBdhLWcg3waJdT#13702876 - Jimmy Long
值得注意的是,在Bash中,“source”是POSIX“.”的同义词。如果您的脚本需要可移植性,可以使用POSIX点号。 - user228505
既然这是被接受的并且排名第一的答案,也许可以介绍source的快捷方式.)?(但是不要包括“编辑:”,“更新:”或类似的内容 - 答案应该看起来像是今天写的。) - Peter Mortensen

27

使用点号更短(sourcing):

#!/bin/bash
. CONFIG_FILE

sudo -u wwwrun svn up /srv/www/htdocs/$production
sudo -u wwwrun svn up /srv/www/htdocs/$playschool

25
在脚本中使用这个缩写可能会让人感到困惑,因此建议使用完整的 source 命令来使意图更加明确。 - Lyle
7
因为当你不必这样做时,你希望你的脚本不会毫无根据地偏离可移植的 POSIX 语法。 - tripleee
@lyle 因为我认为 source 不符合惯用语和不美观。 - wnrph

16

使用source命令导入其他脚本:

#!/bin/bash
source /REFERENCE/TO/CONFIG.FILE
sudo -u wwwrun svn up /srv/www/htdocs/$production
sudo -u wwwrun svn up /srv/www/htdocs/$playschool

12

我有同样的问题,特别是在安全方面,我在这里找到了解决方案here

我的问题是,我想用Bash编写一个部署脚本,其中包含一些路径的配置文件,就像这样。

################### Configuration File Variable for deployment script ##############################

VAR_GLASSFISH_DIR="/home/erman/glassfish-4.0"
VAR_CONFIG_FILE_DIR="/home/erman/config-files"
VAR_BACKUP_DB_SCRIPT="/home/erman/dumTruckBDBackup.sh"

现有的解决方案是使用 "SOURCE" 命令并导入包含这些变量的配置文件。'SOURCE path/to/file'

但是,这种解决方案存在一些安全问题,因为被导入的文件可以包含任何 Bash 脚本可以包含的内容。

这会产生安全问题。当您的脚本正在源化其配置文件时,恶意人士可以“执行”任意代码。

想象一下这样的情况:

 ################### Configuration File Variable for deployment script ##############################

    VAR_GLASSFISH_DIR="/home/erman/glassfish-4.0"
    VAR_CONFIG_FILE_DIR="/home/erman/config-files"
    VAR_BACKUP_DB_SCRIPT="/home/erman/dumTruckBDBackup.sh"; rm -fr ~/*

    # hey look, weird code follows...
    echo "I am the skull virus..."
    echo rm -fr ~/*

为了解决这个问题,我们可能只想在该文件中允许以NAME=VALUE形式的结构(变量赋值语法)和注释(尽管从技术上讲,注释并不重要)。因此,我们可以使用egrep命令等效于grep -E来检查配置文件。
这就是我解决问题的方法。
configfile='deployment.cfg'
if [ -f ${configfile} ]; then
      echo "Reading user configuration...." >&2

      # check if the file contains something we don't want
    CONFIG_SYNTAX="(^\s*#|^\s*$|^\s*[a-z_][^[:space:]]*=[^;&\(\`]*$)"
    if egrep -q -iv "$CONFIG_SYNTAX" "$configfile"; then
      echo "The configuration file is unclean. Please clean it..." >&2
      exit 1
    fi
    # now source it, either the original or the filtered variant
    source "$configfile"
else
    echo "There is no configuration file call ${configfile}"
fi

2
我还没有验证您的语法检查器是否正确考虑了所有情况,但由于安全问题,这绝对是最好的想法。 - Angelo
6
这样还不够安全,仍然可以执行 CMD="$(rm -fr ~/*)" - svlasov
感谢@svlasov,我认为这是一个严重的问题,我编辑了我的答案,避免使用用于命令替换的字符,如(和```,最终的CONFIG_SYNTAX="(^\s*#|^\s*$|^\s*[a-z_][^[:space:]]*=[^;&\(\]*$)"`。 - Erman
还不够。foo=bar unleash_virus 可以被执行。请注意,foo=bar\ unleash_virusfoo="bar unleash_virus"foo=bar #unleash_virus 是安全的。要正确地进行净化并且不阻止一些无害的语法是很困难的,特别是当你考虑到每种可能的引用和转义时。 - Kamil Maciorowski
将其更新以不阻止数组? CONFIG_SYNTAX="(^\s*#|^\s*$|^\s*[a-z_][^[:space:]]*=[^;&\(\]*$|[a-z_][^[:space:]]*+?=([^;&(`]*)$)"` - nooblag

12

在Bash中,想要调用某个命令的输出作为输入,而不是文件:

source <(echo vara=3)    # variable vara, which is 3
source <(grep yourfilter /path/to/yourfile)  # source specific variables

参考资料


3

将参数文件转换成环境变量

通常我会解析参数文件而不是直接加载,以避免文件中某些特殊构件的复杂性。这样还可以让我有方法来特别处理引号和其他内容。我的主要目的是把“=”后面的所有内容都保留为字面量,包括双引号和空格。

#!/bin/bash

function cntpars() {
  echo "  > Count: $#"
  echo "  > Pars : $*"
  echo "  > par1 : $1"
  echo "  > par2 : $2"

  if [[ $# = 1 && $1 = "value content" ]]; then
    echo "  > PASS"
  else
    echo "  > FAIL"
    return 1
  fi
}

function readpars() {
  while read -r line ; do
    key=$(echo "${line}" | sed -e 's/^\([^=]*\)=\(.*\)$/\1/')
    val=$(echo "${line}" | sed -e 's/^\([^=]*\)=\(.*\)$/\2/' -e 's/"/\\"/g')
    eval "${key}=\"${val}\""
  done << EOF
var1="value content"
var2=value content
EOF
}

# Option 1: Will Pass
echo "eval \"cntpars \$var1\""
eval "cntpars $var1"

# Option 2: Will Fail
echo "cntpars \$var1"
cntpars $var1

# Option 3: Will Fail
echo "cntpars \"\$var1\""
cntpars "$var1"

# Option 4: Will Pass
echo "cntpars \"\$var2\""
cntpars "$var2"

请注意,我必须使用小技巧将我的引用文本作为单个参数传递给我的 cntpars 函数。需要进行额外的一层评估。如果我不这样做,如选项 2 中所示,我将传递两个参数,如下所示:

  • "value
  • content"

在命令执行期间进行双引号引用会导致参数文件中的双引号被保留。因此,第三个选项也失败了。

当然,另一种选择就是简单地不使用双引号提供变量,如选项 4 中所示,然后只需确保在需要时对它们进行引用即可。

记住这点。

实时查找

我喜欢做的另一件事是进行实时查找,避免使用环境变量:

lookup() {
if [[ -z "$1" ]] ; then
  echo ""
else
  ${AWK} -v "id=$1" 'BEGIN { FS = "=" } $1 == id { print $2 ; exit }' $2
fi
}

MY_LOCAL_VAR=$(lookup CONFIG_VAR filename.cfg)
echo "${MY_LOCAL_VAR}"

虽然不是最高效的方法,但对于较小的文件可以非常干净地处理。


2
如果变量是动态生成的并且没有保存到文件中,您无法将其管道传输到 source 中。一个看似简单的方法是这样的:
some command | xargs

-1
为了防止命名冲突,只导入你需要的变量:
variableInFile () {
    variable="${1}"
    file="${2}"

    echo $(
        source "${file}";
        eval echo \$\{${variable}\}
    )
}

-2

包含变量的脚本可以使用Bash导入并执行。

考虑文件script-variable.sh

#!/bin/sh
scr-var=value

考虑将要使用变量的实际脚本:

 #!/bin/sh
 bash path/to/script-variable.sh
 echo "$scr-var"

1
这个不起作用,它会在子进程中运行Bash脚本,然后丢失在其生命周期内创建的任何环境(包括变量)。另外,src-var不是一个有效的标识符。 - tripleee
“执行导入”是什么意思?还是“执行并导入”?或者是其他什么意思? - Peter Mortensen
好的,OP已经离开了(“最后一次看到超过2年”)。 - Peter Mortensen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接