¿Cómo puedo minimizar el código necesario para ejecutar esta función personalizada?

¿Cómo puedo minimizar el código necesario para ejecutar esta función personalizada?

Me gustaría acortar uno de mis guiones y tengo una idea, pero no sé cómo hacerlo. Tenga un fragmento de código como este:

COMMAND="find /etc -type "
case $1:
 "directory") $COMMAND d
 ;;
esac

Por supuesto, esta es la versión corta :) Y ahora quiero poder acortarla, no escribir ese $COMMAND en todas partes, así que me gustaría tener algo como esto:

$COMMAND <the case statement return value>

pero no quiero usar una variable para almacenar el resultado del caso.

Gracias :) Espero que hayas entendido lo que quiero :D

EDITAR 1: Es posible crear una función y pasar el parámetro de búsqueda como $1 como lo indica Serg. Ahora, SI quisiera hacerlo sin la función, estoy seguro de que hay una manera :D No es que Serg no lo haya resuelto, solo tengo curiosidad :D

Respuesta1

Hay varias formas posibles de simplificar el código. A continuación se muestran las soluciones ordenadas por cantidad de código:

  1. printfy sustitución de parámetros (sin verificación de errores)
  2. xargsy sustitución de parámetros (sin verificación de errores)
  3. findy sustitución de parámetros únicamente (sin verificación de errores)
  4. estructura de casos fallidos (resuelve el problema de verificación de errores)
  5. Lógica de prueba, xargs y sustitución de parámetros
  6. función de golpe
  7. Matriz, bucle for, prueba y &&

  1. printfy solución de sustitución de parámetros

    Una cualidad poco conocida de la printffunción es que si llama printf "%c" someString, imprimirá solo elprimerocarácter de esa cadena. Por lo tanto, podemos evitar el uso de declaraciones de caso con expansión de parámetros y printfasí:

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

    ahora ejecuta:

    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
    

    Una limitación aquí es que estamos bifurcando un proceso para llamar printf, lo que la solución de función evita: la función y la estructura de casos son todas herramientas nativas de bash.

  2. xargsy sustitución de parámetros

    Usando la sustitución de parámetros de bash podemos cortar una subcadena de una variable (por ejemplo, ${VAR:0:3}proporciona los primeros 3 caracteres de VAR); en este caso queremos el primer carácter para un tipo directoryo file. Luego podemos usar xargspara pasar eso como parámetro afind

    echo ${1:0:1} | xargs -I {} find /etc -type  {} 
    

    La findpágina de manual menciona que para -typela bandera en Solaris hay algo conocido como archivo de puerta, que está representado por la letra mayúscula D, pero como estamos en Linux, podemos decir que es una limitación que es razonable ignorar.

    Sin embargo, hay otro peligro en este código: si un usuario ingresa flowercomo $1parámetro, esto seguirá buscando -type f, porque tomamos el primer carácter de cualquier cadena... En otras palabras,no hay verificación de errores.

  3. findcon expansión de parámetros

    Llevando la expansión de parámetros aún más lejos, podemos hacer esto:

     find /etc -type ${1:0:1}
    

    Básicamente, una línea con findcomando y una subcadena de la $1variable.Además, no hay verificación de errores..

  4. Estructura de casos fallidos

    El gran problema con los últimos tres métodos es la comprobación de errores. Son buenos cuando confías en que el usuario no será un tonto o simplemente escribirá código por ti mismo. Ahora, en Java es posible escribir una switchdeclaración que ejecute el mismo comando en múltiples casos si simplemente omite el breakcomando. Eso bashtambién se puede hacer con ;&terminador. Cita deman bash

    El uso ;&en lugar de ;;hace que la ejecución continúe con la lista asociada con el siguiente conjunto de patrones.

    Todo lo que tenemos que hacer es probar los tipos, como "directorio", "archivo", "bloque", etc., y luego usar la sustitución de parámetros para cortar el primer carácter. Al igual que

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

5.Lógica de prueba, xargs y sustitución de parámetros

 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.
  1. Solución de función

    findEl comando se puede colocar en una función, que luego se puede llamar con parámetros posicionales.

    Por ejemplo:

    function findStuff
    {
     find /etc -type "$1" 
    }
    

    Aquí hay una pequeña demostración. Tenga en cuenta que lo estoy usando sudoporque muchos archivos en /etcusuarios normales no tienen permisos de lectura.

    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
    
  2. Matriz, bucle for, prueba y &&

    La idea básica aquí es hacer coincidir la entrada del usuario con una lista y, si coincide, hacer algo. Creamos una serie de elementos para verificar, tenemos una condición de prueba (por cierto, los corchetes son alias del testcomando) y simplemente ejecutamos un bucle para probar la variable $1. &&El operador permite ejecutar el comando si y solo si lo que está a la izquierda &&fue exitoso. Entonces, si encontramos una cadena que está en la matriz, ejecutamos el comando buscar. El ${1:0:1} se analizó en ejemplos anteriores: expansión de parámetros que elimina el primer carácter de nuestro tipo coincidente. Entonces, esta solución tiene verificación de errores y una gran cantidad de código empaquetado en solo 3 líneas (4 si incluye #!la línea).

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

Respuesta2

Ponlo en una función:

MyFind () {
  COMMAND="find /etc -type "
  case $1:
   "directory") $COMMAND d
   ;;
  esac
}

Ahora siempre podrás usarlo comoMyFind $TYPE


Respecto a tu primer comentario

También puedes poner solo la declaración de caso en una función.

FType () {
  case $1 in
    "directory") echo d;;
    "file") echo f;;
  esac
}

COMMAND="find /etc -type "
$COMMAND $(FType $TYPE) 

Respuesta3

[[ "$1" == "directory" ]] && find /etc -type d

Respuesta4

Solo otra idea para agregar al ya amplio conjunto de opciones de la excelente respuesta de Serg.

Es posible que puedas usar un aliaspara esto, posiblemente con algunos pequeños cambios en la forma en que haces las cosas actualmente.

An aliases simplemente una palabra que usted elige asignar a una cadena más grande, que el shell expandirá cada vez que la encuentre. Quizás el caso de uso más común sea aplicar "valores predeterminados" a un comando existente, como este:

alias ls='ls -a --color=auto'

Sin embargo, no es necesario que el aliasnombre de su comando sea el de un comando existente, o incluso más corto que su patrón de destino, por lo que podría, por ejemplo,alias tgzcreator='tar -czvf'

aliases comparten la vida útil con el shell en el que están definidos. Los mensajes 'persistentes' aliaspara usted se pueden configurar en ~/.bash_aliases, lo quedeberíaser obtenido automáticamente por la mayoría de los scripts predeterminados bien escritos .bashrcy similares.

Tenga en cuenta un par de sugerencias:

  • En lugar de preocuparse en un script en particular si algo previamente definido aliasinterferirá con su código, puede anteponerle una barra invertida para asegurarse de aliasque se omita ing. Por ejemplo, normalmente lo hago alias cppara cp -ievitar sobrescribir cosas accidentalmente, pero en algunos scripts, claramentedesearpara sobrescribir cosas. (¡No voy a usar ninguna solución horrible como configurar un aliasusuario no ed conocido!) Entonces, en ese script, usaré\cp src dst
  • aliasEs posible que es no se obtenga de forma predeterminada dentro de los scripts de shell, que inician su propia copia no interactiva del shell. Puede asegurarse de que estén expandidos configurando la opción expand_aliasesen su secuencia de comandos. Obtuve esto de:https://stackoverflow.com/questions/2197461/how-to-set-an-alias-inside-a-bash-shell-script-so-that-is-it-visible-from-the-ou

Entonces, basándose en el contexto limitado disponible en tu publicación, es posible que desees hacer algoun pococomo esto:

shopt -s expand_aliases
alias get_etc_dir='find /etc -type d'
alias get_etc_fil='find /etc -type f'

Para usted, es posible que esto no funcione sin realizar ajustes, por ejemplo, cambiando el tipo de inodo parametrizado a un sufijo por alias. Es sólo otra opción sobre cómo los usuarios pueden acortar fragmentos de código en general. De cualquier manera, he tratado de explicarlo exhaustivamente basándome en lo que sé y espero que sea útil en alguna parte.

(Además, sugeriría mover esto a Unix/Linux SE, suponiendo que sea ideal para cosas que no son específicas de Ubuntu).

información relacionada