带有命令行选项的 Bash 脚本卡住并且没有设置变量的默认值

带有命令行选项的 Bash 脚本卡住并且没有设置变量的默认值

我对 bash 脚本非常陌生,并且对 bash 中的命令行功能完全陌生。我尝试了一个脚本,如果用户喜欢直接编辑代码,该脚本应该可以与命令行参数以及手动设置变量值一起使用。

总体思路是这样的:

  • 使用 while/case/getopt 结构为不同函数定义命令行参数。
  • 设置相应案例内每个选项的变量值。
  • 稍后,检查是否确实使用 if 情况提供了命令行参数。
  • 如果没有,则将该值设置为默认参数。

这样,我们既可以使用myscript.sh -i somestring,也可以在 if 语句中手动设置与 -i 关联的变量。这样,只需执行 ,即可运行脚本./myscript.sh

我发现我的代码中的 if 情况实际上没有做任何事情,或者至少没有做我想要的事情。当我在没有命令行参数的情况下运行脚本,然后回显应由 if 情况设置的默认值时,它们是空的。

这意味着脚本中稍后的 for 循环无法工作,因为这些变量尚未设置为任何内容。脚本卡住的行是 slurm_file_string=$(cat $slurm_template_file | sed "s/INPUT_FILE/$gs_file/")

所以我不知道如何实现我想要实现的目标以及如何解决这个“卡住问题”。我需要以某种方式更改 if 情况,以便 if 情况内的默认值实际上执行某些操作,但不知道如何执行。

这是我的代码:

#!/bin/bash

# TODO: 
    # Need to figure out if I can launch all slurm jobs with "&" and let slurm handle the rest.
    # Add scratch removal logic to slurm file template. Makes more sense to do it per run in the file that actually runs gaussian.
    # Add commandline options for:
        # input folder (-i) 
        # verbose mode (-v)
        # extension name (-x)
        # slurm template name (-t)
    # Define function be_verbose() which wraps the logic for printing additional info if -v is set to true.

# INFO: Script for running a batch of gaussian jobs using the SLURM scheduler.
    # General program flow: 
        # The script finds all gaussian input files in inps_folder (needs to be manually specified)
            # The input files are identified by the file extension in "extension"
        # The script iterates over all gaussian input files.
            # For each file a slurm file is created based on the template: submit_gaussian_template.slurm
            # Inside the template the string "INPUT_FILE" is replaced by the current input file.
            # The new slurm file is copied to the input files folder
            # The script changes directories to the common pwd of the slurm file and gaussian input file
            # The slurm file is executed and then the next iteration starts.
                # The cleanup is handeled by the shell code inside the slurm file (deleting temp files from /scratch)
            
# IMPORTANT:
    # This script is designed for a certain folder structure.
    # It is required that all your gaussian input files are located in one directory.
    # That folder must be in the same dir as this script.
    # There must be a template slurm file in the directory of this script
    # There must be a string called INPUT_FILE inside the template slurm file 
################################################################################################################################################



# this implements command line functionality. If you do not wish to use them, look for MANUAL_VARS_SPEC below to specify your variables that way.
while getopts "i:vx:t:" opt; do
    case $opt in
        i)
            inps_folder="$OPTARG"
            ;;
            v)
            verbose=true
        ;;
        x)
        extension="$OPTARG"
        echo "$extension"
        ;;
        t)
        slurm_template_file="$OPTARG"
        ;;
        \?)
        echo "Usage: $0 [-i input_folder] [-s value_s] [-k value_k]"
        exit 1
        ;;
    esac
done


# MANUAL_VARS_SPEC: Change the varibles to the appropriate values if you do not wish to use the comman line options.
# These are essentially the default settings of the script.

# folder for input files
if [ -n "$inps_folder" ]; then # "if the commandline option is an empty string (not set), set the variable to this value."
    inps_folder="testinps"
fi

# verbose mode
if [ -n "$verbose" ]; then
    verbose=0
fi

# file extension of your gaussian input files
if [ -n "$extension" ]; then
    echo "AFASGSG"
    extension="gin"
fi

# slurm template file name
if [ -n "$slurm_template_file" ]; then
    slurm_template_file="submit_gaussian_template.slurm"
fi




# HELPER FUNCTIONS
function be_verbose(){
    local print_string="$1" # set the first argument provided to the funtion to the local var "print_string".
    if [ $verbose = true ]; then
        echo "$print_string"
    fi
}

echo "$inps_folder"
echo "$verbose"
echo "$extension"
echo "$slurm_template_file"


#### START OF MAIN LOOP.
files="${inps_folder}/*.${extension}" # iteratable for all gaussian input files.

for file in $files; do
    gs_file=$(basename $file) # get the file without the preceeding path
    gs_file_name="${gs_file%.*}" # get the file name without the extension
    
    # Make a new slurm file for the current job based on the template slurm file in the pwd.
    slurm_file_string=$(cat $slurm_template_file | sed "s/INPUT_FILE/$gs_file/") # get template and replace INPUT_FILE with gaussian input file. FAIL!!!!
    slurm_file="${gs_file_name}.slurm"
    echo "$slurm_file_string" > "$slurm_file" # write the string of the new slurm file to a new file
    mv "$slurm_file" "${inps_folder}/${slurm_file}" # move the new slurm file to the inps_folder

    cd "$inps_folder" # change directories so that slurm files can be executed
    echo "Is running ${gs_file}" #PUT HERE WHATEVER THE COMMAND FOR RUNNIGN A SLURM FILE IS &
    cd ..
done

预先感谢您的任何意见。

答案1

一般来说,如果有一个“最小可验证示例”的代码,就更容易提供帮助——足以证明问题。然而,在这里我想我可以看到问题所在。

解析选项时,您有这样的代码:

while getopts 't:' opt
do
    case "$opt" in
        (t) slurm_template_file="$OPTARG" ;;
    esac
done

但稍后你会看到这个,上面写着“如果变量有值,则将其设置为固定值”:

if [ -n "$slurm_template_file" ]
then
    slurm_template_file='submit_gaussian_template.slurm'
fi

我本来希望使用此代码,-z而不是-n这样,如果变量未设置或为空,则分配一个默认值。

我的方法是将变量设置为默认值,然后使用命令行开关覆盖它们。如果以后的决策有必要,我还会设置一个标志来指示用户明确设置该值,但根据我的经验,这通常不需要。

slurm_template_file='submit_gaussian_template.slurm'
slurm_template_file_isset=false

while getopts 't:' opt
do
    case "$opt" in
        (t)
            slurm_template_file="$OPTARG"
            slurm_template_file_isset=true
            ;;
    esac
done

if "$slurm_template_file_isset"
then
    echo "User overrode the default" >&2
fi
echo "The working value is: $slurm_template_file" >&2

或者,您可以在代码顶部设置默认值,并在未设置工作值时使用它们。对于长变量名,代码有点笨拙,但小心的话可以更清晰:

template_default='submit_gaussian_template.slurm'
template=

while getopts 't:' opt
do
    case "$opt" in
        (t)
            template="$OPTARG"
            ;;
    esac
done

if [ -n "$template" ]
then
    echo "User overrode the default: $template" >&2
fi
echo "The working value is: ${template:-$template_default}" >&2

相关内容