如何最小化运行此自定义函数所需的代码?

Icy*_*Ice 4 command-line bash

我想缩短我的一个脚本,但我不知道该怎么做。有一段这样的代码:

COMMAND="find /etc -type "
case $1:
 "directory") $COMMAND d
 ;;
esac
Run Code Online (Sandbox Code Playgroud)

当然这是简短的版本 :) 现在我希望能够缩短它,而不是到处都写 $COMMAND,所以我想要这样的东西:

$COMMAND <the case statement return value>
Run Code Online (Sandbox Code Playgroud)

但我不想使用变量来存储大小写的结果。

谢谢 :) 希望你明白我想要什么 :D

编辑 1:可以创建一个函数并将 find 参数作为 Serg 所指的 $1 传递。现在,如果我只想在没有该功能的情况下做到这一点,我肯定有办法:D 不像 Serg 没有解决它,我只是好奇:D

Ser*_*nyy 7

有几种可能的方法来简化代码。以下是按代码量排序的解决方案:

  1. printf 和参数替换(无错误检查)
  2. xargs 和参数替换(无错误检查)
  3. find 仅参数替换(无错误检查)
  4. fall-through case 结构(解决错误检查问题)
  5. 测试逻辑、xargs 和参数替换
  6. 重击功能
  7. 数组、for 循环、测试和 &&

  1. printf 和参数替换解决方案

    printf函数的一个鲜为人知的特性是,如果您调用printf "%c" someString,它将仅打印该字符串的第一个字符。因此,我们可以避免使用带有参数扩展的 case 语句,printf如下所示:

    xieerqi:$ cat someScript.sh
    #!/bin/bash
    find /etc -type $(printf "%c" $1 )
    
    Run Code Online (Sandbox Code Playgroud)

    现在执行:

    xieerqi:$ sudo ./someScript.sh  directory | head
    [sudo] password for xieerqi: 
    /etc
    /etc/logrotate.d
    /etc/apm
    /etc/apm/event.d
    /etc/apm/scripts.d
    /etc/apm/resume.d
    /etc/apm/suspend.d
    /etc/speech-dispatcher
    /etc/speech-dispatcher/modules
    /etc/speech-dispatcher/clients
    
    Run Code Online (Sandbox Code Playgroud)

    这里的一个限制是我们正在分叉一个进程来调用printf,函数解决方案避免了这种情况——函数和案例结构都是本机 bash 工具。

  2. xargs 和参数替换

    使用 bash 的参数替换,我们可以从变量中截取一个子字符串(例如${VAR:0:3}给出 的前 3 个字符VAR);在这种情况下,我们需要类型directoryor的第一个字符file。然后我们可以使用xargs它作为参数传递给find

    echo ${1:0:1} | xargs -I {} find /etc -type  {} 
    
    Run Code Online (Sandbox Code Playgroud)

    find手册页提到了-type在Solaris上标志有一些被称为门的文件,这是由大写字母来表示D,但由于我们是在Linux上,我们可以说这是一个限制是合理的,不容忽视。

    然而,这段代码还有另一个危险——如果用户flower作为$1参数输入,它仍然会搜索-type f,因为我们取任何字符串的第一个字符......换句话说,没有错误检查

  3. find 带参数扩展

    进一步进行参数扩展,我们可以这样做:

     find /etc -type ${1:0:1}
    
    Run Code Online (Sandbox Code Playgroud)

    基本上,一个带有find命令和$1变量子字符串的单行。此外,没有错误检查

  4. 跌落式机箱结构

    后三种方法的大问题是错误检查。当您相信用户不是傻瓜,或者只是为自己编写代码时,它们是很好的。现在,在 Java 中,switch如果您只是省略该break命令,则可以编写一个语句,该语句将在多个情况下运行相同的命令。在bash这是可以做到的,以及;&终止。报价自man bash

    使用;&代替;;导致执行继续与下一组模式关联的列表。

    我们所要做的就是测试类型,如“目录”、“文件”、“块”等,然后使用参数替换来切掉第一个字符。像这样

    #!/bin/bash
    case "$1" in
      "directory") ;&
      "file");&
      "block") find /etc -type  ${1:0:1} ;;
      *) exit 1 ;;
    esac
    
    Run Code Online (Sandbox Code Playgroud)

5.测试逻辑、xargs 和参数替换

 Basic idea here is that we're sending $1 variable through pipe to `xargs`, which in turn substitutes it into test (again square brackets). `xargs` in turn builds the actual command that runs by replacing `{}` with whatever was passed to `xargs`.  As for test command, it's simple or statement,  `EXPRESSION -o EXPRESSION ` , where we test if string $1 is equal to either "file" or "directory string"

    echo "$1" | xargs -I {}  [ "{}" = "file" -o "{}" = "directory"  ] \
    && find /etc -type ${1:0:1}

 `xargs` is really useful when you need to process multiple argumens with the same command. Considering that in this case we only have one argument that needs to be processed with the same command , this can be simplified to

    [ "$1" = "file" -o "$1" = "directory"  ] && find /etc -type ${1:0:1} 

Of course the big limitation is that if you test for more than one type, you need longer `[ EXPR1 -o EXPR2 ]` structure with multiple `-o` parts.
Run Code Online (Sandbox Code Playgroud)
  1. 功能解决方案

    find 命令可以放入一个函数中,然后可以使用位置参数调用该函数。

    例如:

    function findStuff
    {
     find /etc -type "$1" 
    }
    
    Run Code Online (Sandbox Code Playgroud)

    这是一个小演示。请注意,我使用的sudo是因为/etc普通用户中的许多文件没有读取权限

    xieerqi:$ sudo ./someScript.sh directory | head                                            
    [sudo] password for xieerqi: 
    /etc
    /etc/logrotate.d
    /etc/apm
    /etc/apm/event.d
    /etc/apm/scripts.d
    /etc/apm/resume.d
    /etc/apm/suspend.d
    /etc/speech-dispatcher
    /etc/speech-dispatcher/modules
    /etc/speech-dispatcher/clients
    
    xieerqi:$ sudo ./someScript.sh file | head                                                 
    [sudo] password for xieerqi: 
    /etc/hosts.deny
    /etc/logrotate.d/speech-dispatcher
    /etc/logrotate.d/pm-utils
    /etc/logrotate.d/rsyslog
    /etc/logrotate.d/yate
    /etc/logrotate.d/apport
    /etc/logrotate.d/apt
    /etc/logrotate.d/consolekit
    /etc/logrotate.d/fail2ban
    /etc/logrotate.d/cups-daemon
    
    xieerqi:$ cat someScript.sh                                                                
    #!/bin/bash
    function findStuff
    {
     find /etc -type "$1" 
    }
    
    case "$1" in 
      "directory")findStuff d ;;
      "file") findStuff f;;
    esac
    
    Run Code Online (Sandbox Code Playgroud)
  2. 数组、for 循环、测试和 &&

    这里的基本思想 - 将用户的输入与列表匹配,如果匹配,则执行某些操作。我们创建了一个要检查的项目数组,有一个测试条件(顺便说一下,方括号是test命令的别名),然后运行一个循环来测试 $1 变量。&&当且仅当左侧的内容&&成功时,运算符才允许执行命令。所以如果我们在数组中找到一个字符串,我们就执行 find 命令。${1:0:1} 在前面的例子中讨论过——参数扩展会切断我们匹配类型的第一个字符。因此,此解决方案具有错误检查功能,并且将大量代码打包到 3 行(如果包含#!行则为 4行)。

    #!/bin/bash   
    array=("file" "directory" "block");
    for TYPE in "${array[@]}"; do 
       [ "$1" = "$TYPE"  ] && find /etc/ -type ${1:0:1}; 
    done  
    
    Run Code Online (Sandbox Code Playgroud)