在bash中使用getops解析命令行参数

时间:2025-04-22 07:42:22

在linux环境中,我们通常会使用‘tar -xvf ’这样的指令,其中“-xvf ”就是命令行参数。

在bash脚本中,如果希望我们可以使用类似于‘bash_name.sh -x ’的指令去指定如何运行脚本,则我们可以使用getops.

getops会使用类似于以下格式去解析我们的命令行参数:

# 1. -s -r can be used directly
# 2. d:/f: can be used -d/-f parameters
# The arguments would be parsed as $OPTARG
# 3. $OPTIND is another parameter getops provided as default.
# It will helps us to get the current index of the command argument  
while getopts 'srd:f:' c
do
  echo "Processing $c : OPTIND is $OPTIND"
  case $c in
    s) ACTION=SAVE ;;
    r) ACTION=RESTORE ;;
    d) DB_DUMP=$OPTARG ;;
    f) TARBALL=$OPTARG ;;
  esac
done

以上脚本并不能运行, 目前只是定义了一些变量,并没有实际的操作,则我们可以补充一些函数,来看一下其是如何工作的。

#!/usr/bin/env bash
set -eo pipefail
save_database()
{
	echo "I am saving database $1"
}

restore_database()
{
	echo "I am restoring database $1"
}

save_files()
{
	echo "I am saving files $1"
}

restor_files()
{
	echo "I am restoring files $1"
}

# 1. -s -r can be used directly
# 2. d:/f: can be used -d/-f parameters
# The arguments would be parsed as $OPTARG
# 3. $OPTIND is another parameter getops provided as default.
# It will helps us to get the current index of the command argument  
while getopts 'srd:f:' c
do
  echo "Processing $c : OPTIND is $OPTIND"
  case $c in
    s) ACTION=SAVE ;;
    r) ACTION=RESTORE ;;
    d) DB_DUMP=$OPTARG ;;
    f) TARBALL=$OPTARG ;;
  esac
done

if [ -n "${DB_DUMP}" ]; then
	case $ACTION in
		SAVE) save_database ${DB_DUMP}  ;;
        RESTORE) restore_database ${DB_DUMP} ;;
    esac
fi

if [ -n "${TARBALL}" ]; then
	case $ACTION in
		SAVE) save_files ${TARBALL} ;;
		RESTORE) restore_database ${TARBALL} ;;
	esac
fi

我们运行该脚本,则其输出结果为:

$ ./import_db.sh -s -d database_name -f tar_name 
Processing s : OPTIND is 2
Processing d : OPTIND is 4
Processing f : OPTIND is 6
I am saving database database_name
I am saving files tar_name

如果我们不指定-d或者-f的参数则会报错,如下:

$ ./import_db.sh -s -d
Processing s : OPTIND is 2
./import_db.sh: option requires an argument -- d
Processing ? : OPTIND is 3

如上脚本,我们看到,我们遇到错误,并没有给出很友好的信息。

则如果想让我们的脚本更规范,我们可以做以下改进,加入校验机制。

例如改变我们定义变量的方法,如下:

#!/usr/bin/env bash
set -eo pipefail
set_variable()
{
	# Declare one local variable
	local varname=$1
	# Move on
	shift
	# If the varname has not been set, set the $varname=$1
	# The exclamation mark before varname tells the shell to replace 
	# that with the value of $varname
	# ACTION=SAVE
	if [ -z "${!varname}" ]; then
		echo eval "$varname=\"$@\""
		eval "$varname=\"$@\""
	else
		echo "Error: $varname already set"
	fi
}

save_database()
{
	echo "I am saving database $1"
}

restore_database()
{
	echo "I am restoring database $1"
}

save_files()
{
	echo "I am saving files $1"
}

restor_files()
{
	echo "I am restoring files $1"
}

# 1. -s -r can be used directly
# 2. d:/f: can be used -d/-f parameters
# The arguments would be parsed as $OPTARG
# 3. $OPTIND is another parameter getops provided as default.
# It will helps us to get the current index of the command argument  
while getopts 'srd:f:' c
do
  echo "Processing $c : OPTIND is $OPTIND"
  case $c in
    s) set_variable ACTION SAVE ;;
    r) set_variable ACTION RESTORE ;;
    d) set_variable DB_DUMP $OPTARG ;;
    f) set_variable TARBALL $OPTARG ;;
  esac
done

if [ -n "${DB_DUMP}" ]; then
	case $ACTION in
		SAVE) save_database ${DB_DUMP}  ;;
        RESTORE) restore_database ${DB_DUMP} ;;
    esac
fi

if [ -n "${TARBALL}" ]; then
	case $ACTION in
		SAVE) save_files ${TARBALL} ;;
		RESTORE) restore_database ${TARBALL} ;;
	esac
fi

引入定义变量的方法,如果没有定义则初始化变量,否则不定义变量。

让脚本支持help,显示使用方法:

#!/usr/bin/env bash
set -eo pipefail

usage()
{
  echo "Usage: $0 [-h|-s|-r] [ -d DB_DUMP ] [ -f TARBALL ]"
  exit 2
}

set_variable()
{
	# Declare one local variable
	local varname=$1
	# Move on
	shift
	# If the varname has not been set, set the $varname=$1
	# The exclamation mark before varname tells the shell to replace 
	# that with the value of $varname
	# ACTION=SAVE
	if [ -z "${!varname}" ]; then
		echo eval "$varname=\"$@\""
		eval "$varname=\"$@\""
	else
		echo "Error: $varname already set"
		usage
	fi
}

save_database()
{
	echo "I am saving database $1"
}

restore_database()
{
	echo "I am restoring database $1"
}

save_files()
{
	echo "I am saving files $1"
}

restor_files()
{
	echo "I am restoring files $1"
}

# 1. -s -r can be used directly
# 2. d:/f: can be used -d/-f parameters
# The arguments would be parsed as $OPTARG
# 3. $OPTIND is another parameter getops provided as default.
# It will helps us to get the current index of the command argument  
while getopts 'hsrd:f:' c
do
  case $c in
  	h) usage ;;
    s) set_variable ACTION SAVE ;;
    r) set_variable ACTION RESTORE ;;
    d) set_variable DB_DUMP $OPTARG ;;
    f) set_variable TARBALL $OPTARG ;;
  esac
done

if [ -n "${DB_DUMP}" ]; then
	case $ACTION in
		SAVE) save_database ${DB_DUMP}  ;;
        RESTORE) restore_database ${DB_DUMP} ;;
    esac
fi

if [ -n "${TARBALL}" ]; then
	case $ACTION in
		SAVE) save_files ${TARBALL} ;;
		RESTORE) restore_database ${TARBALL} ;;
	esac
fi

则可运行和执行脚本如下:

$ ./import_db.sh -h
Usage: ./import_db.sh [-h|-s|-r] [ -d DB_DUMP ] [ -f TARBALL ]

脚本可在我的github项目中直接获取。