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:
printf
y sustitución de parámetros (sin verificación de errores)xargs
y sustitución de parámetros (sin verificación de errores)find
y sustitución de parámetros únicamente (sin verificación de errores)- estructura de casos fallidos (resuelve el problema de verificación de errores)
- Lógica de prueba, xargs y sustitución de parámetros
- función de golpe
- Matriz, bucle for, prueba y &&
printf
y solución de sustitución de parámetrosUna cualidad poco conocida de la
printf
función es que si llamaprintf "%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 yprintf
así: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.xargs
y sustitución de parámetrosUsando 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 deVAR
); en este caso queremos el primer carácter para un tipodirectory
ofile
. Luego podemos usarxargs
para pasar eso como parámetro afind
echo ${1:0:1} | xargs -I {} find /etc -type {}
La
find
página de manual menciona que para-type
la bandera en Solaris hay algo conocido como archivo de puerta, que está representado por la letra mayúsculaD
, 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
flower
como$1
parámetro, esto seguirá buscando-type f
, porque tomamos el primer carácter de cualquier cadena... En otras palabras,no hay verificación de errores.find
con expansión de parámetrosLlevando 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
find
comando y una subcadena de la$1
variable.Además, no hay verificación de errores..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
switch
declaración que ejecute el mismo comando en múltiples casos si simplemente omite elbreak
comando. Esobash
tambié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.
Solución de función
find
El 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
sudo
porque muchos archivos en/etc
usuarios 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
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
test
comando) 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 alias
para esto, posiblemente con algunos pequeños cambios en la forma en que haces las cosas actualmente.
An alias
es 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 alias
nombre 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'
alias
es comparten la vida útil con el shell en el que están definidos. Los mensajes 'persistentes' alias
para usted se pueden configurar en ~/.bash_aliases
, lo quedeberíaser obtenido automáticamente por la mayoría de los scripts predeterminados bien escritos .bashrc
y similares.
Tenga en cuenta un par de sugerencias:
- En lugar de preocuparse en un script en particular si algo previamente definido
alias
interferirá con su código, puede anteponerle una barra invertida para asegurarse dealias
que se omita ing. Por ejemplo, normalmente lo hagoalias
cp
paracp -i
evitar sobrescribir cosas accidentalmente, pero en algunos scripts, claramentedesearpara sobrescribir cosas. (¡No voy a usar ninguna solución horrible como configurar unalias
usuario no ed conocido!) Entonces, en ese script, usaré\cp src dst
alias
Es 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ónexpand_aliases
en 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).