我正在尝试创建一个使用 getopts 的 shell 脚本文件。该程序的目的是删除项目中的文件并将它们放在已删除的目录(又称回收站)中。我已经能够成功完成此操作。
我还能够使用 getopt 命令 -i (交互式)传递到 case 语句,然后向用户触发一个问题,询问他们是否确定要删除文件。
这是通过创建变量“ision”并在激活 case 语句时将其设置为 true 来完成的。我已在下面插入我的代码。我已经尝试了几次,一切似乎都很好,但我想添加详细的活动/模式。有谁可以帮助我吗?
#!/bin/bash
while getopts ":i:v" option ;
do
case "$option" in
i) echo "interactive mode set"
ision=true;
break;;
v) echo "verbose mode"
vison=true;
break;;
esac
done
echo "this is the proof we need"
echo $@
shift $(($OPTIND-2))
echo $@
echo "this is working too"
if [ ! -e ~/deleted ]
then
mkdir ~/deleted
fi
echo "the echo file was made or just created"
if [ $# -eq 0 ]
then
echo "safe_rm missing operand"
exit
fi
echo "all workking on the western front"
for i in $@
do
if [ "$ision" == "true" ]
then
echo "do you want to remobve the file"
echo "variable test $i"
echo "yes or no"
read -p "Enter " answer
if [ "$answer" == "no" ]
then
continue;
fi
fi
if [ ! -f $i ]
then
echo "no such file or directory"
exit
fi
if [ $i == safe_rm ]
then
echo "cant remove safe_rm"
continue
fi
if [ $i == safe_rm_restore ]
then
continue
echo "cant remove safe_rm_restore"
fi
inode=$(ls -i $i | cut -c -6 )
echo "the inode is $inode "
pathname=$(dirname $i)
if [ $pathname == "." ]
then
pathname=$(pwd)
echo $pathname
fi
basename=$(basename $i)
path=$basename"_"$inode":"$pathname"/"$basename
echo $path
if [ ! -f .restoreinfo ]
then
touch .restoreinfo
fi
echo $path >> .restoreinfo
mv $i ~/deleted/
done
答案1
我将在这里重点关注命令行解析:
# defaults:
ision=0
vison=0
while getopts "iv" option; do
case "$option" in
i)
echo "interactive mode set"
ision=1 ;;
v)
echo "verbose mode"
vison=1 ;;
*)
exit 1 ;;
esac
done
shift $(( OPTIND - 1 ))
(( vison )) && echo 'This is a verbose message'
if (( ision )); then
# interactive code
fi
如果它们是整数,ision
则操作起来会容易得多。vison
然后你可以像(( vison ))
我上面所示的那样测试它们的值。
您还得到了错误的命令选项字符串getopts
。据我所知,这两个选项都没有参数,这意味着字符串中不应该有任何冒号。如果选项带有参数,则冒号应位于其后面,如 中所示v:
。
break
当您解析命令行时,不要这样做,这将停止对其的解析。相反,只需设置标志并执行您需要执行的其他操作,然后让循环继续。我假设您可能对 C 有一定的了解,其中之一做需要break
来自一个case
声明(在 a 中switch
)。这里不一样。
解析命令行时不要进行输出,除非作为调试辅助。许多实用程序具有相互抵消的标志,例如,-v
对于详细模式,然后是-q
“安静”模式,并且让代码输出“进入详细模式”,然后“进入安静模式”只是噪音。
顺便说一句,要做类似的事情(处理互斥选项),你可以
case "$option" in
q)
quiet=1
verbose=0 ;;
v)
verbose=1
quiet=0 ;;
# etc.
用户使用时-qv
,quiet
会先设置为1,再verbose
设置为0,然后将值反转。在这种情况下应该不会报告错误,因为您不知道命令行是手动组合还是通过调用脚本组合在一起。
我插入了一条exit 1
语句,如果给出未知的命令行选项,该语句将被执行。这可能是件好事,因为这意味着用户犯了一个错误,并且从那时起盲目相信命令行上的内容可能是危险的。
shift
(移走已解析的选项)应按上述方式完成。转移$OPTIND - 2
会从参数列表中移走太多的东西。
我还改进了缩进,使其更易于阅读,从而更易于调试和维护。
至于脚本的其余部分,我没有仔细查看,但我注意到很多未加引号的变量扩展。不要这样做,因为如果给定的文件名中包含空格(或其他空白字符),它会弄乱事情。
特别是$@
在eg循环中使用时需要加引号。这可以防止命令行参数被空格分割(更准确地说是 的内容$IFS
),并且如果名称包含通配模式字符,则可以避免意外的文件名通配。
也有相当多的continue
说法。恕我直言,这些应该被消除,因为它们使遵循代码流程变得困难(特别是当代码没有正确缩进时)。它们本身并没有错,但如果代码适当地处理各种条件,则可以将它们删除。至少在一种情况下,您在语句echo
之后直接有一个continue
,这意味着echo
永远不会触发:
continue
echo "cant remove safe_rm_restore"
答案2
您可以使用一些速记比较。这是一个 if/then/else 语句,其列出方式类似于 C 和其他几种语言中的 ?: 表示法。您可以运行比较并使用 && 运行块(如果为 true),并使用 || 运行块。如果为 false 则运行一个块。
$ vison=true
$ [[ "$vison" == "true" ]] && vison=false || vison=true
$ echo $vison
false
$ [[ "$vison" == "true" ]] && vison=false || vison=true
$ echo $vison
true
也可以执行相同的操作来检查详细日志记录。
[[ "$vison" == "true" ]] && echo "this is shown only in verbose mode"
答案3
有false
和true
命令可以方便地在 shell 中表达布尔值。对于详细程度,常见的方法是使用日志级别,其中每次出现都会增加或减少-v
日志级别。-q
interactive=false
log_level=1
log() {
local level="$1"
if [ "$level" -ge "$log_level" ]; then
shift
local IFS=" "
printf '%s\n' "$*"
fi
}
while getopts iqv option; do
case $option in
i)
log 2 "interactive mode set"
interactive=true;;
v)
log 2 "increasing log level"
log_level=$((log_level + 1));;
q)
log 2 "decreasing log level"
log_level=$((log_level - 1));;
*)
exit 1 ;;
esac
done
shift "$((OPTIND - 1))"
log 2 'This is a verbose message'
log 3 'This is a debug message'
if "$interactive"; then
# interactive code
fi
# or:
ask() { # args: var default question
if "$interactive"; then
printf %s "$3"
IFS= read -r "$1"
else
eval "$1=\$2"
fi
}
yesno() { # args: default question
local answer="$1"
ask answer "$1" "$2"
case $answer in
([yY][eE][sS] | y | Y) return 0;;
([nN][oO] | n | N) return 1;;
(*) case $1 in
([yY][eE][sS] | y | Y) return 0;;
([nN][oO] | n | N) return 1;;
(*) log >&2 -1 "Wrong default value $default"; exit 1;;
esac;;
esac
}
if yesno no "Are you OK with that (yes/[no])? "; then
log 1 OK do it
...
fi
上面的代码(未经测试)符合 Debian 策略,因此sh
无论配置为dash
、lksh
或bash
(如果不是 ,也可以是 POSIX local
),都可以与 Debian 一起使用。