如何在 UNIX 中使用 case 语句打开和关闭详细模式

如何在 UNIX 中使用 case 语句打开和关闭详细模式

我正在尝试创建一个使用 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.

用户使用时-qvquiet会先设置为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

falsetrue命令可以方便地在 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无论配置为dashlkshbash(如果不是 ,也可以是 POSIX local),都可以与 Debian 一起使用。

相关内容