如何在shell脚本中动态解释变量?

时间:2017-11-01 19:03:39

标签: json bash shell unix jq

我正在使用JQ在shell脚本中读取JSON。在这里,我无法动态解释shell脚本中的变量$ HOME,$ HOST,$ PEMFILE。

JSON文件:

{
    "script": {
    "install": "${HOME}/lib/install.sh $HOST $PEMFILE",
    "Setup": "${HOME}/lib/setup.sh $HOST $PEMFILE $VAR1 $VAR2"
    }

}

Shell脚本:

#!/bin/bash
examplefile="../lib/example.json"
HOST=ec2-..-...-...-...us-west-2.compute.amazonaws.com
PEMFILE=${HOME}/test.pem

installScript=($(jq '.script.install' $examplefile))
bash "$installScript"

有没有办法可以在不修改JSON的情况下动态解释这些变量?

P.S我不想使用eval。

5 个答案:

答案 0 :(得分:4)

使用gnu实用程序envsubst

很容易
installScript=$(jq -r '.script.install' "$examplefile" | envsubst)

答案 1 :(得分:3)

以下是使用uname(2)env执行替换的解决方案。

请注意#!/bin/bash examplefile="../lib/example.json" HOST=ec2-..-...-...-...us-west-2.compute.amazonaws.com PEMFILE=${HOME}/test.pem export HOST export PEMFILE installScript=$(jq -Mr ' .script.install | gsub("(?<x>[$][{]?\\w+[}]?)"; env[.x|gsub("[${}]+";"")] ) ' $examplefile) echo $installScript 要求变量作为环境变量传递,而不是shell变量。

/home/runner/lib/install.sh ec2-..-...-...-...us-west-2.compute.amazonaws.com /home/runner/test.pem

示例输出

import java.awt.*;
import javax.swing.*;

public class Menu_Frame extends JFrame{

    Menu_Panel menu_panel = new Menu_Panel();

    public Menu_Frame(){
        Toolkit kit = Toolkit.getDefaultToolkit();      
        Dimension screenSize = kit.getScreenSize();
        int screenHeight = screenSize.height;
        int screenWidth = screenSize.width;

        setExtendedState(JFrame.MAXIMIZED_BOTH);
        setPreferredSize(new Dimension(screenWidth, screenHeight));
        setLocationByPlatform(true);
        getContentPane().add(menu_panel);   
    }
}

gsub

答案 2 :(得分:3)

具体解决方案

这是解决所述问题的jq解决方案,但它只适用于&#34; global&#34;环境变量。

def substitute:
  gsub("\\${HOME}"; env.HOME)
  | gsub("\\$HOST"; env.HOST)
  | gsub("\\$PEMFILE"; env.PEMFILE)
  | gsub("\\$VAR1"; env.VAR1)
  | gsub("\\$VAR2"; env.VAR2)
  ;

walk( if type=="string" then substitute else . end )

如果你的jq还没有walk/1,请升级你的jq或snarf https://github.com/stedolan/jq/blob/master/src/builtin.jq

上面的解决方案有点脆弱,但很容易被证明或推广,如下一节所示。

一般解决方案

walk(if type == "string"
     then gsub("\\$(?<x>[A-Za-z_][A-Za-z0-9_]+)"; "\(env[.x])") 
          | gsub("\\${(?<x>[A-Za-z_][A-Za-z0-9_]+)}"; "\(env[.x])") 
     else . end)

答案 3 :(得分:0)

#!/bin/sh

TMP=$(mktemp /tmp/$$.XXX)

cat<<E_O_F > $TMP
cat <<EOF
$(cat so-dollar-variables.json)
EOF
E_O_F

. $TMP

/bin/rm "$TMP"

答案 4 :(得分:0)

我多年来一直在打这个。我想我终于得到了一个不错的纯bash解决方案:使用正则表达式匹配和间接参数替换

# read the file
json=$(< file.json)
echo step 0
echo "$json"

# set the relevant vars, just plain shell variables
HOST=_host_
PEMFILE=_pemfile_
VAR1=_var1_
VAR2=_var2_

# replace '$var' forms
while [[ $json =~ ("$"([[:alnum:]_]+)) ]]; do 
    json=${json//${BASH_REMATCH[1]}/${!BASH_REMATCH[2]}}
done; 
echo
echo step 1
echo "$json"

# replace '${var}' forms
while [[ $json =~ ("$""{"([[:alnum:]_]+)"}") ]]; do 
    json=${json//${BASH_REMATCH[1]}/${!BASH_REMATCH[2]}}
done
echo
echo step 2
echo "$json"

输出

step 0
{
    "script": {
    "install": "${HOME}/lib/install.sh $HOST $PEMFILE",
    "Setup": "${HOME}/lib/setup.sh $HOST $PEMFILE $VAR1 $VAR2"
    }

}

step 1
{
    "script": {
    "install": "${HOME}/lib/install.sh _host_ _pemfile_",
    "Setup": "${HOME}/lib/setup.sh _host_ _pemfile_ _var1_ _var2_"
    }

}

step 2
{
    "script": {
    "install": "/home/jackman/lib/install.sh _host_ _pemfile_",
    "Setup": "/home/jackman/lib/setup.sh _host_ _pemfile_ _var1_ _var2_"
    }

}

神奇的是:

  1. 正则表达式,我捕获$VARVAR,以及

    [[ $json =~ ("$"([[:alnum:]_]+)) ]]
    # ..........1   2             21
    
  2. 参数替换,我在其中搜索字符串"$VAR"并将其替换为间接变量扩展${!VAR}

    ${json//${BASH_REMATCH[1]}/${!BASH_REMATCH[2]}}