我对 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