【问题标题】:Issue controlling script flow问题控制脚本流
【发布时间】:2012-12-03 11:58:42
【问题描述】:

我是 shell 脚本的新手,我的脚本似乎没问题,但我在控制流程时遇到了问题。请有人指出我犯了什么愚蠢的错误。

#! /bin/sh

echo "Are you sure youx want to delete $1? Answer y or n"
read ans
echo $ans
if $ans = "y"|"Y"
then
    mv $1 /home/parallels/dustbin
    echo "File $1 has been deleted"
else echo "File $1 has not been deleted"
fi

【问题讨论】:

    标签: linux shell scripting


    【解决方案1】:

    让你的 if 条件像这样:

    if [ "$ans" = "y" -o "$ans" = "Y" ]
    

    【讨论】:

    • 感谢您的快速而有帮助的回复!
    • 不客气。如果解决方案对您有效,请接受答案。如果有多个答案适合您,请接受最适合您问题的答案...
    【解决方案2】:

    您的脚本有一些问题。有些很严重,有些不太严重。

    首先,严重的问题。

    正如大师建议的那样,您需要使用方括号来包围您的 if 条件。这是因为if 只测试条件的输出,它不执行实际的字符串比较。传统上,一个名为/bin/test(也称为/bin/[)的程序负责处理这些问题。如今,该功能已内置到 shell 中,但 /bin/sh 仍然表现得好像它是一个单独的程序。

    事实上,当您使用方括号表示您的条件时,您可以使用if 做一些有趣的事情。例如,if grep -q 'RE' /path/to/file; then 很常见。 grep -q 命令不发出任何输出,而只是返回由if 检测到的“成功”或“失败”。

    第二个严重的问题是您正在呼应的状态可能是真实的,也可能不是真实的。我称这是一个严重的问题,因为......好吧,日志消息根本不应该做出虚假声明。如果$1 中的文件的权限错误,或者文件名包含空格,那么您的mv 命令将失败,但消息会声称它没有。稍后会详细介绍。

    接下来是不太严重的问题。

    这些主要是样式和优化的东西。

    首先,大多数平台上的read 包含一个-p 选项,可让您指定提示。使用它,您不需要包含echo 命令。

    其次,您的缩进使您很难看到 if 构造正在包装什么。在这么小的程序中,这不是一个大问题,但随着您的成长,您真的希望遵循一致的标准。

    第三,如果您使用case 语句而不是if,您可能会在此类多项选择题中获得更大的灵活性。

    毕竟,这是我编写此脚本的方式:

    #!/bin/sh
    
    if [ "$1" = "-y" ]; then
      ans=y
      shift
    elif [ -t 0 ]; then
      read -p "Are you sure you want to delete '$1' (y/N) ? " ans
    fi
    
    case "$ans" in
      Y*|y*)
        retval=0
        if [ -z "$1" ]; then
          retval=64
          echo "ERROR: you didn't specify a filename." >&2
        if [ ! -f "$1" ]; then
          retval=66
          echo "ERROR: file '$1' not found!" >&2
        elif mv "$1" /home/parallels/dustbin/; then
          echo "File '$1' has been deleted" >&2
        else
          retval=$?
          echo "ERROR: file '$1' could not be deleted!" >&2
        fi
        ;;
      *)
        echo "ABORT: file '$1' has not been deleted" >&2
        retval=4
        ;;
    esac
    
    exit $retval
    

    除了上面提到的之外,下面是这段代码sn-p中的一些内容:

    • [ "$1" = "-y" ] - 如果用户指定了 -y 选项,那么我们的行为就好像问题的答案是“是”。
    • [ -t 0 ] - 这测试我们是否在交互式终端上。如果是,那么通过read 提问是有意义的。
    • Y*|y*) - 在 case 语句中,这匹配任何以大写或小写“y”开头的字符串。因此,有效的肯定回答应该是“Y”、“yes”、“yellow”等。
    • [ ! -f "$1" ] - 测试文件是否存在。您可以man testman sh 查看shell 中可用的各种测试。 (-f 可能不是最适合你的。)
    • >&2 - 在一行的末尾,将其输出发送到“标准错误”而不是“标准输出”。这会改变管道、cron 等处理输出的方式。错误和日志数据通常会发送到 stderr,因此 stdout 可以专用于程序的实际输出。
    • mv "$1" ... - 文件名用引号引起来。这可以保护您,以防文件名中包含空格等特殊字符。
    • $retval - 此值来自对 man sysexits 中最接近的项目的最佳猜测。
    • retval=$? - 这是最近执行的命令的退出状态。在这种情况下,这意味着我们将mv 的退出状态分配给变量$retval,这样如果mv 失败,整个脚本都会报告失败的原因,就mv 而言.

    【讨论】:

    • 我得到的唯一问题是,当 mv 失败时是否还需要从脚本中返回错误代码(这目前被 echo ERROR.. 隐藏,返回正常)
    • @GermanGarcia - 是的,这可能是个好主意,特别是如果脚本经过精心设计,可以被其他脚本包装。我已经更新了答案以包含更多“学习”。 :-)
    • 很好的教学答案。干杯。
    【解决方案3】:

    您还可以将用户响应转换为任何一种情况,然后检查相应的情况,例如

    read ans
    ans=${ans,,}  # make 'ans' lowercase, or use ${ans^^} for making it uppercase
    if [ "$ans" = "y" ]
    then
       ....
    fi
    

    【讨论】:

    • 这种转换为小写的方法只适用于 bash。 OP 的脚本运行 /bin/sh - 我们不知道 bash 是否也可用,但即使是,您的答案也应该将 bash 指定为要求,因为 bash 在以 sh 调用时使用 POSIX 行为。
    【解决方案4】:

    以下是包含错误处理的完美代码

    #!/bin/sh
    
    echo "Are you sure you want to delete $1? Answer y or n"
    read ans
    echo $ans
    
    if [ $ans == "y" ] || [ $ans == "Y" ]
    then
        if [ -f $1 ]
        then
               mv $1 /home/parallels/dustbin
               echo "File $1 has been deleted"         
        else
               echo " File $1 is not found"
        fi
    else 
        echo "File $1 has not been deleted"
    fi
    

    【讨论】:

      猜你喜欢
      • 2011-03-04
      • 2016-03-29
      • 1970-01-01
      • 2015-11-22
      • 1970-01-01
      • 2016-11-22
      • 2011-12-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多