Existem várias maneiras possíveis de simplificar o código. Abaixo estão as soluções ordenadas pela quantidade de código:
-
printf
e substituição de parâmetros (sem verificação de erros) -
xargs
e substituição de parâmetros (sem verificação de erros) -
find
e apenas sub-parâmetro (sem verificação de erros) - estrutura de caso de fall-through (resolve o problema de verificação de erros)
- Lógica de teste, xargs e substituição de parâmetros
- Função de bash
- Array, for-loop, test e & &
-
printf
e solução de substituição de parâmetrosUma qualidade pouco conhecida da função
printf
é que, se você chamarprintf "%c" someString
, ele imprimirá apenas o caractere primeiro dessa cadeia. Assim, podemos evitar o uso de instrução case com expansão de parâmetro eprintf
da seguinte forma:xieerqi:$ cat someScript.sh #!/bin/bash find /etc -type $(printf "%c" $1 )
agora execute:
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
Uma limitação aqui é que estamos forqueando um processo para chamar
printf
, que a função function evita - function e case structure são todas ferramentas bash nativas. -
xargs
e substituição de parâmetrosUsando a substituição de parâmetro do bash, podemos cortar uma substring de uma variável (por exemplo,
${VAR:0:3}
fornece os primeiros 3 caracteres deVAR
); Neste caso, queremos o primeiro caractere para um tipodirectory
oufile
. Então, podemos usarxargs
para passar isso como parâmetro parafind
echo ${1:0:1} | xargs -I {} find /etc -type {}
A página
find
man menciona que para-type
flag no Solaris há algo conhecido como arquivo de porta, representado pela letra maiúsculaD
, mas como estamos no Linux, podemos dizer isso é uma limitação que é razoável ignorar.No entanto, há outro perigo neste código - se um usuário inserir
flower
como o parâmetro$1
, isso ainda vai procurar por-type f
, porque consideramos primeiro o caractere de qualquer string… Em outras palavras , não há verificação de erros . -
find
com a expansão de parâmetrosLevando a expansão dos parâmetros ainda mais longe, podemos fazer isso:
find /etc -type ${1:0:1}
Basicamente, um one-liner com o comando
find
e uma subseqüência da variável$1
. Além disso, nenhuma verificação de erros . -
Estrutura de caso de fall-through
O grande problema com os três últimos métodos é a verificação de erros. Eles são bons quando você confia no usuário para não ser um manequim, ou apenas escrever código para si mesmo. Agora, em Java, é possível escrever uma instrução
switch
que executará o mesmo comando para vários casos se você simplesmente omitir o comandobreak
. Embash
isso pode ser feito também com;&
terminator. Citação deman bash
Using
;&
in place of;;
causes execution to continue with the list associated with the next set of patterns.Tudo o que temos que fazer é testar os tipos, como "diretório", "arquivo", "bloco" e assim por diante, e então usar a substituição de parâmetros para cortar o primeiro caractere. Assim
#!/bin/bash case "$1" in "directory") ;& "file");& "block") find /etc -type ${1:0:1} ;; *) exit 1 ;; esac
5. Lógica de teste, xargs e substituição 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.
-
Solução de função
O comandofind
pode ser colocado em uma função, que pode ser chamada com parâmetros posicionais.Por exemplo:
function findStuff { find /etc -type "$1" }
Aqui está uma pequena demonstração. Observe que estou usando
sudo
porque, para muitos arquivos em/etc
, os usuários comuns não têm permissões de leituraxieerqi:$ 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
-
Array, for-loop, test e & &
Idéia básica aqui - combine a entrada do usuário em uma lista e, se for compatível, faça alguma coisa. Criamos uma matriz de itens para verificar, temos uma condição de teste (colchetes são alias para o comando
test
) e apenas executamos um loop para testar a variável $ 1. O operador&&
permite executar o comando se e somente se o que está à esquerda de&&
foi bem-sucedido. Então, se encontrarmos uma string que esteja na matriz, executamos o comando find. O $ {1: 0: 1} foi discutido nos exemplos anteriores - expansão de parâmetro que corta o primeiro caractere do nosso tipo correspondente. Portanto, esta solução tem verificação de erros e todo o código compactado em apenas 3 linhas (4 se você incluir#!
line).#!/bin/bash array=("file" "directory" "block"); for TYPE in "${array[@]}"; do [ "$1" = "$TYPE" ] && find /etc/ -type ${1:0:1}; done