Como posso minimizar o código necessário para executar esta função personalizada?

Como posso minimizar o código necessário para executar esta função personalizada?

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:

  1. printfe substituição de parâmetros (sem verificação de erros)
  2. xargse substituição de parâmetros (sem verificação de erros)
  3. finde apenas substituição de parâmetros (sem verificação de erros)
  4. estrutura de caso de falha (resolve problema de verificação de erros)
  5. Lógica de teste, xargs e substituição de parâmetros
  6. Função Bash
  7. Matriz, loop for, teste e &&

  1. printfe solução de substituição de parâmetros

    Uma qualidade pouco conhecida da printffunção é que se você chamar printf "%c" someString, ela imprimirá apenas oprimeirocaractere dessa string. Assim, podemos evitar o uso da instrução case com expansão de parâmetros e printfassim:

    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.

  2. xargse substituição de parâmetros

    Usando 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 de VAR); neste caso, queremos o primeiro caractere de um tipo directoryou file. Então podemos usar xargspara passar isso como parâmetro parafind

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

    A findpágina de manual menciona que para -typeflag no Solaris existe algo conhecido como arquivo de porta, que é representado pela letra maiúscula D, 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 flowercomo $1parâ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.

  3. findcom expansão de parâmetros

    Levando a expansão dos parâmetros ainda mais longe, podemos fazer isso:

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

    Basicamente, uma linha com findcomando e uma substring da $1variável.Além disso, sem verificação de erros.

  4. 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 switchinstrução que executará o mesmo comando para vários casos se você simplesmente omitir o breakcomando. Isso bashtambé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.
  1. Solução de função

    findO 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 sudoporque muitos arquivos em /etcusuários regulares não têm permissões de leitura

    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, 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 testcomando) 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 aliaspara 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 aliasnome 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'

aliases compartilham o tempo de vida com o shell no qual estão definidos. 'Persistentes' aliaspara você podem ser configurados em ~/.bash_aliases, quedeveser originado automaticamente pela maioria dos scripts padrão bem escritos .bashrce similares.

Observe algumas dicas:

  • Em vez de se preocupar em um script específico se algo definido anteriormente aliasirá interferir no seu código, você pode prefixá-lo com uma barra invertida para garantir que aliasing seja ignorado. por exemplo, eu normalmente alias cpevito cp -isobrescrever coisas acidentalmente, mas em alguns scripts, eu claramentequererpara sobrescrever coisas. (Não vou usar nenhuma solução alternativa horrível, como configurar um aliasusuário conhecido e desconhecido!) Então, nesse script, usarei\cp src dst
  • aliases 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ção expand_aliasesem 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?)

informação relacionada