
Gostaria de encurtar um dos meus roteiros e não tenho ideia, mas não sei como fazer isso. Tenha um trecho de código como este:
COMMAND="find /etc -type "
case $1:
"directory") $COMMAND d
;;
esac
Claro que esta é a versão curta :) E agora eu quero poder abreviá-la, não escrever $COMMAND em todos os lugares, então eu gostaria de ter algo assim:
$COMMAND <the case statement return value>
mas não quero usar uma variável para armazenar o resultado do caso.
Obrigado :) Espero que você tenha entendido o que eu quero: D
EDIT 1: É possível criar uma função e passar o parâmetro find como $1 conforme apontado por Serg. Agora, SE eu quisesse fazer isso sem a função, tenho certeza que tem um jeito :D Não é como se o Serg não tivesse resolvido, só estou curioso :D
Responder1
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 substituição de parâmetros (sem verificação de erros)- estrutura de caso de falha (resolve problema de verificação de erros)
- Lógica de teste, xargs e substituição de parâmetros
- Função Bash
- Matriz, loop for, teste e &&
printf
e solução de substituição de parâmetrosUma qualidade pouco conhecida da
printf
função é que se você chamarprintf "%c" someString
, ela imprimirá apenas oprimeirocaractere dessa string. Assim, podemos evitar o uso da instrução case com expansão de parâmetros eprintf
assim: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 bifurcando um processo para chamar
printf
, o que a solução da função evita - a função e a estrutura do caso são todas ferramentas bash nativas.xargs
e substituição de parâmetrosUsando a substituição de parâmetros do bash, podemos cortar uma substring de uma variável (por exemplo,
${VAR:0:3}
fornece os 3 primeiros caracteres deVAR
); neste caso, queremos o primeiro caractere de um tipodirectory
oufile
. Então podemos usarxargs
para passar isso como parâmetro parafind
echo ${1:0:1} | xargs -I {} find /etc -type {}
A
find
página de manual menciona que para-type
flag no Solaris existe algo conhecido como arquivo de porta, que é representado pela letra maiúsculaD
, mas como estamos no Linux, podemos dizer que esta é uma limitação que é razoável ignorar.Porém, há outro perigo neste código - se um usuário inserir
flower
como$1
parâmetro, ele ainda irá procurar por-type f
, porque pegamos o primeiro caractere de qualquer string… Em outras palavras,não há verificação de erros.find
com expansão de parâmetrosLevando a expansão dos parâmetros ainda mais longe, podemos fazer isso:
find /etc -type ${1:0:1}
Basicamente, uma linha com
find
comando e uma substring da$1
variável.Além disso, sem verificação de erros.Estrutura de caso de falha
O grande problema dos três últimos métodos é a verificação de erros. Eles são bons quando você confia que o usuário não será um idiota ou apenas escreverá código para si mesmo. Agora, em Java é possível escrever uma
switch
instrução que executará o mesmo comando para vários casos se você simplesmente omitir obreak
comando. Issobash
também pode ser feito com;&
o terminador. Citação doman bash
Usar
;&
no lugar de;;
faz com que a execução continue com a lista associada ao próximo conjunto de padrões.Tudo o que precisamos 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. Igual a
#!/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
find
O comando pode ser colocado em uma função, que então 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 muitos arquivos em/etc
usuários regulares 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
Matriz, loop for, teste e &&
Idéia básica aqui - compare a entrada do usuário com uma lista e, se corresponder, faça alguma coisa. Criamos um array de itens para verificar, temos uma condição de teste (colchetes, aliás, são alias para
test
comando) 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&&
foi bem-sucedido. Portanto, se encontrarmos uma string que está no array, executamos o comando find. O ${1:0:1} foi discutido em exemplos anteriores - expansão de parâmetro que corta - o primeiro caractere do nosso tipo correspondente. Portanto, esta solução possui verificação de erros e muito código compactado em apenas 3 linhas (4 se você incluir#!
a linha).#!/bin/bash array=("file" "directory" "block"); for TYPE in "${array[@]}"; do [ "$1" = "$TYPE" ] && find /etc/ -type ${1:0:1}; done
Responder2
Coloque-o em uma função:
MyFind () {
COMMAND="find /etc -type "
case $1:
"directory") $COMMAND d
;;
esac
}
Agora você sempre pode usá-lo comoMyFind $TYPE
Em relação ao seu 1º comentário
Você também pode colocar apenas a instrução case em uma função
FType () {
case $1 in
"directory") echo d;;
"file") echo f;;
esac
}
COMMAND="find /etc -type "
$COMMAND $(FType $TYPE)
Responder3
[[ "$1" == "directory" ]] && find /etc -type d
Responder4
Apenas mais uma ideia para adicionar ao já amplo conjunto de opções da excelente resposta de Serg.
Você pode usar um alias
para isso - possivelmente com algumas pequenas alterações na forma como você faz as coisas atualmente.
An alias
é simplesmente uma palavra que você escolhe mapear para uma string maior, que o shell expandirá sempre que a encontrar. Talvez o caso de uso mais comum seja aplicar 'padrões' a um comando existente, como este:
alias ls='ls -a --color=auto'
No entanto, não há exigência de que seu alias
nome seja nomeado após um comando existente - ou até mesmo menor que seu padrão de destino - então você pode, por exemploalias tgzcreator='tar -czvf'
alias
es compartilham o tempo de vida com o shell no qual estão definidos. 'Persistentes' alias
para você podem ser configurados em ~/.bash_aliases
, quedeveser originado automaticamente pela maioria dos scripts padrão bem escritos .bashrc
e similares.
Observe algumas dicas:
- Em vez de se preocupar em um script específico se algo definido anteriormente
alias
irá interferir no seu código, você pode prefixá-lo com uma barra invertida para garantir quealias
ing seja ignorado. por exemplo, eu normalmentealias
cp
evitocp -i
sobrescrever coisas acidentalmente, mas em alguns scripts, eu claramentequererpara sobrescrever coisas. (Não vou usar nenhuma solução alternativa horrível, como configurar umalias
usuário conhecido e desconhecido!) Então, nesse script, usarei\cp src dst
alias
es não podem ser originados por padrão em scripts de shell, que iniciam sua própria cópia não interativa do shell. Você pode garantir que eles sejam expandidos definindo a opçãoexpand_aliases
em seu script. Eu peguei isso de:https://stackoverflow.com/questions/2197461/how-to-set-an-alias-inside-a-bash-shell-script-so-that-is-it-visible-from-the-ou
Então, com base no contexto limitado disponível na sua postagem, você pode querer fazer algotipoassim:
shopt -s expand_aliases
alias get_etc_dir='find /etc -type d'
alias get_etc_fil='find /etc -type f'
Para você, isso pode não funcionar sem ajustes, por exemplo, alterando o tipo de inode parametrizado para um sufixo por alias. É apenas mais uma opção de como os usuários podem encurtar bits de código em geral. De qualquer forma, tentei explicar de forma abrangente com base no que sei e espero que seja útil em algum lugar.
(Além disso, sugiro mudar isso para Unix/Linux SE, assumindo que é o ideal para coisas que não são específicas do Ubuntu?)