从配置文件解释bash脚本中的变量而不采购?

时间:2015-12-09 22:09:08

标签: bash shell parsing configuration-files

我一直面临着制造无数简单" shell脚本通用于某些不那么通用的进程。我被迫使用配置文件,或者更确切地说基于(在某些情况下)已经实现的内容:在运行时包含脚本中的配置文件或使用source命令。

我尽力偏离基于众多disagreements的人,但我可能别无选择。我需要能够允许诸如外卡,自定义数组和将当前变量放在下一个变量中。我的问题主要是答案最简单。

1。我可以让我当前的函数用于解释配置文件中的$ vars吗?

2。这有关系吗?是否比仅使用源更安全?

我的配置文件看起来像一个INI文件,虽然我不是那样打算因为我不在乎。

[production]
file_date="%Y%m%d" #WAAAA That's not how we format our dates :(
#file_date="gotcha rm testfile"
ssh_user=myuser
server=myserver.net
source_directory=/home/myuser/inb
destination_directory="ONE"
destination_directory="TWO"
destination_directory="THREE"
file_name="SOME_UNIQUE_CONSTANT_*_$file_date" #Or no date
zip_file="SLIGHTLY_DIFFERENT_$file_name.zip" #Or no zip
data_file="A_LITTLE_DIFFERENT_$file_name.dat" #Or no extension
#What to use (sftp|sftpg3|...)
#Everything in directory yes/no
#Everything named *_THIS_*
#Rename *_THIS_* to *_THAT_*
#Who the hell knows...

[testing]
file_date="%Y%m%d" #WAAAA That's not how we format our dates :(
ssh_user=jsharpe
server=myserver.net
file_name="TEST_FILE_$file_date"
zip_file="$file_name.zip"
data_file="$file_name.dat"
source_directory="/home/$ssh_user/$server/$file_name"
destination_directory="test1"
destination_directory="test2"
destination_directory="test3"

[log]
level=verbose
report_on_warning=false
report_on_error=true
report_email_list="myemail@myserver.net"
report_email_list="youremail@myserver.net"

这是我阅读变量的主要功能,它可以很好地防止哑输入。我尝试先使用declare然后使用相同结果的case。一切都是字面上的解释,这实际上是我想要的,试图保护我的脚本免受它读取的配置文件,但它是苦乐参半。还要感谢antonio他的帖子让我在右边?方向。

  

更新2:修改了使用函数的情况并声明而不是eval并在配置行中搜索任何以前包含的变量。   使搜索函数递归为n个变量(如图所示)   用于测试的源目录)。声明有一个数组选项   我的配置只重复相同的变量。现在我只需要   从配置文件中测试通配符。请尝试这个和或让我知道   如果你能用我的方法想出任何重大问题或缺陷。我知道大多数情况都没有函数调用,如果有一个尚未定义的变量的引用,我需要出现一些明显的错误。

function var_in_var() {
    var_in_var_match = "$1"
    if [[ "$1" =~ "$" ]] ; then
        for var in "${prev_case[@]}" ; do :
            if [[ "$1" =~ "$var" ]] ; then
                local var_val="${!var}"
                var_in_var_match="${1//\$$var/$var_val}"
                var_in_var "$var_in_var_match" || return
                return 0
            fi
        done
    fi
}

function read_config_file() {
    #Problematic if user is not knowlegeable on how to correctly make / modify config files.
    echo "Reading config file $config_file"
    shopt -s extglob # Turn on extended globbing.
    #temp_file=$"$file_basename_$RANDOM.tmp"
    tr -d '\r' '\t' < "$config_file" > "$temp_file" #Remove DOS EOL characters and tabs
    local line_num=1 #Counter in case we need to error out config line number
    local section=""
    while IFS='= ' read lhs rhs ; do :
        if [[ "$lhs" =~ "["*"]" ]] ; then
            lhs="${lhs%\]}"  #Remove closing brackets
            lhs="${lhs#\[}"  #Remove opening brackets
            section="$lhs"_
        elif [[ ! $lhs =~ ^\ *# && -n $lhs ]] ; then
            rhs="${rhs%%\#*}"    # Del in line right comments (like this one)
            rhs="${rhs%%*( )}"   # Del trailing spaces
            rhs="${rhs%\"*}"     # Del closing string quotes
            rhs="${rhs#*\"}"     # Del opening string quotes
            #declare -x "$section"_"$lhs"="$rhs" #Does not store $vars from config file
            x="$section$lhs"
            rhs="${rhs//$/\$$section}"
            case $lhs in
                "ssh_user" ) declare "$x=$rhs" ; prev_case+=("$x");;
                "server" ) declare "$x=$rhs" ; prev_case+=("$x");;
                "source_directory" ) var_in_var $rhs || return ; declare "$x"="${var_in_var_match:-$rhs}" ; prev_case+=("$x");;
                "file_date" ) declare "$x=$(date +"$rhs")" ; prev_case+=("$x");;
                "file_name" ) var_in_var $rhs || return ; declare "$x"="${var_in_var_match:-$rhs}" ; prev_case+=("$x");;
                "zip_file" ) var_in_var $rhs || return ; declare "$x"="${var_in_var_match:-$rhs}" ; prev_case+=("$x");;
                "data_file" ) var_in_var $rhs || return ; declare "$x"="${var_in_var_match:-$rhs}" ; prev_case+=("$x");;
                "destination_directory" ) declare -a "$x+=($rhs)" ; prev_case+=("$x");;
                "level" ) declare "$x"="$rhs" ; prev_case+=("$x");;
                "report_on_warning" ) declare "$x"="$rhs" ; prev_case+=("$x");;
                "report_on_error" ) declare "$x"="$rhs" ; prev_case+=("$x");;
                "report_email_list" ) declare "$x"="$rhs" ; prev_case+=("$x");;
                * ) echo "Unknown variable $lhs skipping..." ; exit 1 ;;
            esac
        elif [[ $lhs == "" ]] ; then
            if ! [[ $rhs == "" ]] ; then
                echo "Reading line $line_num from file failed. Variable name is null. Exiting read..." ; has_warnings=true ;
                return 1
            fi
        elif [[ $rhs == "" ]] ; then
            echo "Reading line $line_num from file failed. Variable value is null. Exiting read..." ; has_warnings=true ;
            return 1
        else

            if [[ $lhs =~ ^\ *# ]] ; then
                echo "Line $line_num is a comment, skipping..."
            else
                echo "Reading line $line_num from file failed. Exiting read..." ; has_warnings=true ;
                return 1
            fi
        fi
        line_num=$(($line_num + 1))
    done < "$temp_file"
    #source "somefile.config" <-- Bad idea
    shopt -u extglob     # Turn off extended globbing.
    echo_out_production_variables
    echo_out_testing_variables
    return 0
}

function echo_out_testing_variables() {
    echo "Testing SSH_User: $testing_ssh_user"
    echo "Testing Server: $testing_server"
    echo "Testing Source Directory: $testing_source_directory"
    echo "Testing File Date: $testing_file_date"
    echo "Testing File Name: $testing_file_name"
    echo "Testing Zip File: $testing_zip_file"
    echo "Testing Data File: $testing_data_file"
    for destination in "${testing_destination_directory[@]}" ; do : ; echo "Testing Destination(s): $destination" ; done
}

function echo_out_production_variables() {
    echo "Production SSH_User: $production_ssh_user"
    echo "Production Server: $production_server"
    echo "Production Source Directory: $production_source_directory"
    echo "Production File Date: $production_file_date"
    echo "Production File Name: $production_file_name"
    echo "Production Zip File: $production_zip_file"
    echo "Production Data File: $production_data_file"
    for destination in "${production_destination_directory[@]}" ; do : ; echo "Production Destination(s): $destination" ; done
}

我的脚本输出。

-bash-3.2$ ./get_file.bash -f test.conf 
Reading config file test.conf
Line 3 is a comment, skipping...
Line 13 is a comment, skipping...
Line 14 is a comment, skipping...
Line 15 is a comment, skipping...
Line 16 is a comment, skipping...
Line 17 is a comment, skipping...
Production SSH_User: myuser
Production Server: myserver.net
Production Source Directory: /home/myuser/inb
Production File Date: 20151211
Production File Name: SOME_UNIQUE_CONSTANT_*_20151211
Production Zip File: SLIGHTLY_DIFFERENT_SOME_UNIQUE_CONSTANT_*_20151211.zip
Production Data File: A_LITTLE_DIFFERENT_SOME_UNIQUE_CONSTANT_*_20151211.dat
Production Destination(s): ONE
Production Destination(s): TWO
Production Destination(s): THREE
Testing SSH_User: jsharpe
Testing Server: myserver.net
Testing Source Directory: /home/jsharpe/myserver.net/TEST_FILE_20151211
Testing File Date: 20151211
Testing File Name: TEST_FILE_20151211
Testing Zip File: TEST_FILE_20151211.zip
Testing Data File: TEST_FILE_20151211.dat
Testing Destination(s): test1
Testing Destination(s): test2
Testing Destination(s): test3
  

更新1:我查看了eval并不比source更好但是它让我保持当前的功能设置。我的案例陈述   看起来像

"file_date" ) eval "$x"=$(date +"${rhs}") ;;  "ssh_user" ) eval "$x"="${rhs}" ;;
"data_file" ) eval "$x"="${rhs}" ;;
     

但是我通过将此行添加到我的配置文件中来打破它:

data_file="gotcha rm testfile"
     

我认为这更接近我想要的,因为我有适度的控制权   这一点超过了字符串,可能之前可以消毒它   试图评估它。这是一种可能性。

[SPOILER]:我的简单474行脚本的作用......

  

(sftp | scp | connect direct | sftpg3 | ...)n个文件从一个位置到n个位置。当没有人能够就他们的文件命名达成一致意见时,乐趣就开始了,如果需要在发送之前重命名,如果所有文件或仅发送一些文件,如果需要添加日期,如果它是压缩的,并且需要在目的地解压缩。

0 个答案:

没有答案