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

4

Eu gostaria de encurtar um dos meus scripts e não tenho idéia, mas não sei como fazer isso. Tem um pedaço 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 ser capaz de encurtá-la, e não gravar esse $ COMMAND em todos os lugares, então eu gostaria de ter algo assim:

$COMMAND <the case statement return value>

mas eu 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 como apontado por Serg. Agora, SE eu quisesse fazer isso sem a função, eu tenho certeza que há um jeito: D Não, como o Serg não resolveu, eu só estou curioso: D

    
por IcyIcyIce 12.11.2015 / 03:44

5 respostas

7

Existem várias maneiras possíveis de simplificar o código. Abaixo estão as soluções ordenadas pela quantidade de código:

  1. printf e substituição de parâmetros (sem verificação de erros)
  2. xargs e substituição de parâmetros (sem verificação de erros)
  3. find e apenas sub-parâmetro (sem verificação de erros)
  4. estrutura de caso de fall-through (resolve o problema de verificação de erros)
  5. Lógica de teste, xargs e substituição de parâmetros
  6. Função de bash
  7. Array, for-loop, test e & &
  1. printf e solução de substituição de parâmetros

    Uma qualidade pouco conhecida da função printf é que, se você chamar printf "%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 e printf 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.

  2. xargs e substituição de parâmetros

    Usando 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 de VAR ); Neste caso, queremos o primeiro caractere para um tipo directory ou file . Então, podemos usar xargs para passar isso como parâmetro para find

    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úscula D , 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 .

  3. find com a 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, um one-liner com o comando find e uma subseqüência da variável $1 . Além disso, nenhuma verificação de erros .

  4. 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 comando break . Em bash isso pode ser feito também com ;& terminator. Citação de man 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.
  1. Solução de função

    O comando

    find 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 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. 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  
    
por Sergiy Kolodyazhnyy 12.11.2015 / 04:54
4

Coloque em uma função:

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

Agora você pode sempre usá-lo como MyFind $TYPE

Em relação ao seu primeiro comentário

Você também pode colocar apenas a declaração de caso em uma função

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

COMMAND="find /etc -type "
$COMMAND $(FType $TYPE) 
    
por Germar 12.11.2015 / 03:52
2
[[ "$1" == "directory" ]] && find /etc -type d
    
por Cyrus 12.11.2015 / 07:40
2

Outra abordagem:

declare -A foo    # associative array

foo["directory"]="d"
foo["file"]="f"
foo["link"]="l"

find /etc -type ${foo["$1"]}
    
por Cyrus 12.11.2015 / 08:11
1

Apenas mais uma ideia para adicionar ao já amplo conjunto de opções da excelente resposta do Serg.

Você pode usar um alias para isso - possivelmente com algumas pequenas mudanças em como você faz as coisas no momento.

Um 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 comumente usado 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 precise ser nomeado após um comando existente - ou até menor que seu padrão de destino - para que você possa, por exemplo, alias tgzcreator='tar -czvf'

alias es compartilham a vida útil com o shell no qual eles estão definidos. 'Persistente' alias es para você pode ser configurada em ~/.bash_aliases , que deve ser originada automaticamente pela maioria dos scripts .bashrc padrão e assim por diante.

Observe algumas dicas:

  • Em vez de se preocupar em um determinado script se um alias previamente definido interferiria em seu código, você pode colocar um prefixo com uma barra invertida para garantir que alias ing seja ignorado. por exemplo. Eu normalmente alias cp to cp -i para evitar sobrescrever coisas acidentalmente, mas em alguns scripts, eu claramente quero que as coisas sejam sobrescritas. (Eu não vou usar uma solução horrível como configurar um usuário conhecido de unalias ed!) Então, nesse script, eu usarei \cp src dst
  • alias es pode não ser originado por padrão dentro de 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_aliases em seu script. Eu tenho isso de:

Então, baseando-se no contexto limitado disponível em sua postagem, talvez você queira fazer algo assim:

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 seu tipo de inode parametrizado para um sufixo por alias. É apenas outra opção para como os usuários podem reduzir os bits de código em geral. De qualquer forma, eu tentei explicar de forma abrangente com base no que sei e espero que seja útil em algum lugar.

(Além disso, sugiro mover isso para o Unix / Linux SE, supondo que seja o ideal para coisas que não são específicas do Ubuntu?)

    
por underscore_d 12.11.2015 / 09:31