Dividir comando e argumentos, e colocar em variável em um script bash

2

Eu tenho um script que repete o mesmo (muito longo) grep comando muitas vezes. Eu quero tornar o script mais legível cortando o tamanho da linha, então tentei colocar partes do comando em variáveis.

Esta é a linha (que é repetida várias vezes no script, com diferentes parâmetros):

rsync -av --delete -R --exclude=alternatives /etc/ /backup.raw/ | grep -E '^deleting|[^/]$' >> /var/log/rsync.log

Essa linha funciona bem. O que eu tentei abreviar foi:

_rscmd='rsync -av --delete -R'
_mn=/backup.raw/
_grep='grep -E "^deleting|[^/]$"'
_rslog=/var/log/rsync.log

para que a linha "final" seja:

$_rscmd --exclude=alternatives /etc/ $_mn | $_grep >> $_rslog

Mas essa linha falha na variável $_grep . Como posso escrever certo? Eu tentei usar "…" e '…' , sem sorte. Preste atenção no comando grep original que funciona, por favor.

    
por sergius 03.07.2015 / 16:06

3 respostas

3

A divisão em diferentes partes (comandos e argumentos) não funciona se variáveis são usadas. Use eval para este caso:

eval $_rscmd --exclude=alternatives /etc/ $_mn | eval $_grep >> "$_rslog"

Em geral, é melhor usar funções do shell ou aliases do que usando variáveis:

alias my_grep='grep -E "^deleting|[^/]$"'
...
... | my_grep >> "$_rslog"
    
por 03.07.2015 / 16:13
2

A maneira usual é fazer apenas argumentos do comando como variáveis

_mn=/backup.raw/
_rscmdarg='-av --delete -R'
_greparg='-E "^deleting|[^/]$"'
_rslog=/var/log/rsync.log

como $_rslog é um arquivo e $_mn a dir, não há problema em mantê-los.

e a linha "final" é:

rsync $_rscmdarg --exclude=alternatives /etc/ $_mn | grep  $_greparg >> $_rslog
    
por 03.07.2015 / 16:16
1

Uma maneira de fazer isso inclui (IMHO) as melhores partes das outras duas respostas é escrever uma função de shell que incorpora toda a funcionalidade que você quer ser invariante e leva argumentos especificando as coisas que você quer que sejam diferentes em instâncias diferentes:

my_rsync_grep() {
      rsync -av --delete -R "$1" "$2" "$3" | grep -E '^deleting|[^/]$' >> /var/log/rsync.log
}

e chame com os parâmetros desejados:

my_rsync_grep --exclude=alternatives /etc/ /backup.raw/

Isso age como se você tivesse colocado o comando rsync … | grep … em um script separado. (Exceto que não é um arquivo separado, então você não tem o problema de gerenciamento de configuração de manter o controle de vários arquivos ou o problema logístico de como invocar outro script (ou seja, você usa ./ ou não?), e você não tem nenhuma E / S de disco extra toda vez que invoca a função.

Notas:

  • Por uma questão de legibilidade e estilo de programação, talvez você queira mover o redirecionamento de E / S ( >> /var/log/rsync.log ) fora da função e aplicá-lo a cada invocação:

    my_rsync_grep --exclude=alternatives /etc/ /backup.raw/ >> /var/log/rsync.log
    

    Isso torna óbvio para quem lê ou mantém o script que essa linha está gravando em um arquivo. No entanto, também facilita para inadvertidamente introduzir inconsistências no script. Claro, você pode (e provavelmente deve) colocar o nome do arquivo em uma variável (por exemplo, $_rslog ) para reduzir o risco de ter uma linha que grava em /var/log/rsycn.log .

  • Se você acha que o código de função é prejudicado por legibilidade porque é uma linha muito longa, você pode dividi-la em várias linhas. Anexe um \ a cada linha, mas o último:

          rsync -av --delete -R "$1" "$2" "$3" | grep -E \
                  '^deleting|[^/]$' >> /var/log/rsync.log
    

    Como um caso especial, se você interromper imediatamente após um | , && ou || , o \ é opcional:

          rsync -av --delete -R "$1" "$2" "$3" |
                grep -E '^deleting|[^/]$' >> /var/log/rsync.log
    
  • A função, como escrita acima, assume que ela sempre será invocada com exatamente três parâmetros. Esta suposição parece irrealista. Para executar o comando rsync com qualquer parâmetro / all passado para a função, use "$@" :

          rsync -av --delete -R "$@" | grep …
    
  • Se, por exemplo, você quisesse alterar a função para usar o argumento grep como argumento, por exemplo,

    my_rsync_grep --exclude=alternatives /etc/ /backup.raw/ (other arg(s)) '^deleting|[^/]$'

    você pode fazer assim:

    my_rsync_grep() {
          nargs=$(($#-1))
          rsync -av --delete -R "${@:1:nargs}" | grep -E "${@:$#}" >> /var/log/rsync.log
    }
    

    Veja a seção Expansão de Parâmetros de bash (1) para mais informações sobre $@ e construções como ${parameter:offset:length} .

    Nota: Esta sintaxe pode não funcionar em shells diferentes de bash .

por 04.07.2015 / 06:31