Executar no bash, string como o comando não funciona (Duplicity)

0

Estou usando o Duplicity para fazer backup do meu servidor em outro.

Como existem vários servidores, quero criar um script bash que seja executado com a separação cron fazendo por pasta (e servidor). Para isso fiz a montagem do comando em string:

printf -v DulicityCMD 'duplicity --log-file %s --progress --no-encryption --include="/etc" --include="/var/log" --include="/var/db" --include="/var/www" --include="/home" --exclude="**" / scp://[email protected]:22//home/duplicity/backups/%s' $LOG_FILE $VPSNAME

echo $DulicityCMD

# THIS
$DulicityCMD

# OR THIS
sudo $DulicityCMD

Se você exibir o conteúdo de $DulicityCMD com echo , será algo parecido com isto:

duplicity --log-file /var/log/duplicity/backup.log --progress --no-encryption --include="/etc" --include="/var/log" --include="/var/db" --include="/var/www" --include="/home" --exclude="**" / scp://[email protected]:22//home/duplicity/backups/myhost

Ao digitar este comando, exatamente como o que é exibido com echo , tudo funciona perfeitamente. No entanto, ao executar da variável, dentro de bash , isso não funciona.

Mesmo se eu executar isso com sudo ou como usuário root, isso não funcionará. A saída do erro é esta:

Traceback (most recent call last):
  File "/bin/duplicity", line 1546, in <module>
    with_tempdir(main)
  File "/bin/duplicity", line 1540, in with_tempdir
    fn()
  File "/bin/duplicity", line 1375, in main
    action = commandline.ProcessCommandLine(sys.argv[1:])
  File "/usr/lib64/python2.7/site-packages/duplicity/commandline.py", line 1131, in ProcessCommandLine
    set_selection()
  File "/usr/lib64/python2.7/site-packages/duplicity/commandline.py", line 969, in set_selection
    sel.ParseArgs(select_opts, select_files)
  File "/usr/lib64/python2.7/site-packages/duplicity/selection.py", line 268, in ParseArgs
    self.add_selection_func(self.glob_get_sf(arg, 1))
  File "/usr/lib64/python2.7/site-packages/duplicity/selection.py", line 434, in glob_get_sf
    sel_func = self.glob_get_filename_sf(glob_str, include)
  File "/usr/lib64/python2.7/site-packages/duplicity/selection.py", line 485, in glob_get_filename_sf
    raise FilePrefixError(filename)
FilePrefixError: "/etc"

Quando uso eval , não recebo esse erro, mas ele perde os direitos de root e não é executado corretamente. Porque eu preciso usar a saída deste em conjunto com outro comando (sendmail) usando | .

Qual é o problema e como posso corrigi-lo?

    
por guinalz 22.01.2017 / 23:08

2 respostas

0

A resposta de Stephen Rauch descreve o problema. Eu tenho uma solução diferente.

Mas primeiro: por que você está colocando um comando tão longo em uma variável? Só assim você pode imprimi-lo? Por que não apenas usar a opção detalhada (show commands) do shell?

set -v
(your_command)
set +v

A opção x (rastreamento de execução) é semelhante; tente o acima com set -x e set +x para ver a diferença. Se você gosta dos dois, pode combiná-los com set -vx e set +vx .

É uma péssima ideia criar uma string a partir de uma sequência de palavras, separados por espaços, e tente separá-lo para recuperar as palavras componentes originais. Você encontrou uma das armadilhas. Outra é a possibilidade teórica que um nome de arquivo pode conter espaço (s); seria impossível dizer na string de comando cat foo bar baz que foo bar é um nome de arquivo e baz é outro (e você viu isso criando a string como cat "foo bar" baz não funciona bem também). Estas questões e outras são discutidas aqui:

Além disso, você não precisa de printf apenas para concatenar strings.

Se você realmente quer construir sua linha de comando no armazenamento, e você está usando o bash, você pode usar uma variável array, assim:

DulicityCMD=(duplicity --log-file "$LOG_FILE" --progress --no-encryption --include="/etc" --include="/var/log" --include="/var/db" --include="/var/www" --include="/home" --exclude="**" / scp://[email protected]:22//home/duplicity/backups/"$VPSNAME")

Observe que eu coloquei $LOG_FILE e $VPSNAME entre aspas. Você deve sempre citar todas as referências às variáveis do shell a menos que você tenha uma boa razão para não e você tem certeza de que sabe o que está fazendo. Mas Stephen está certo: você não precisa citar as strings constantes como "/etc" , "/var/log" , "/var/db" e assim por diante. (Mas, claro, você faz precisa citar "**" , porque * é um caractere especial.)

Por questões de legibilidade, você pode dividir o acima em várias linhas. Observe que a primeira linha diz = e as linhas subsequentes dizem += , porque eles estão adicionando ao que veio antes:

DulicityCMD=(duplicity --log-file "$LOG_FILE" --progress --no-encryption)
DulicityCMD+=(--include="/etc" --include="/var/log" --include="/var/db")
DulicityCMD+=(--include="/var/www" --include="/home" --exclude="**")
DulicityCMD+=(/ scp://[email protected]:22//home/duplicity/backups/"$VPSNAME")

Então você pode fazer

printf "%s\n" "${DulicityCMD[*]}"           (to print the command)
"${DulicityCMD[@]}"                         (to execute the command)

ou use sudo "${DulicityCMD[@]}" , se precisar. Veja bash (1) para mais informações sobre a sintaxe do bash, e minha resposta aqui para ver mais sobre essa técnica de matriz.

    
por 23.01.2017 / 04:37
0

Problema:

Este é um problema de citação de string apropriada. Quando você executa a partir da linha de comando com algo como:

duplicity --include="/etc"

o "/etc" será passado para o programa subjacente como /etc porque os " serão removidos pelo shell. Mas quando você faz algo como:

DuplicityCMD='duplicity --include="/etc"'
$DuplicityCMD

O "/etc" é passado exatamente assim. Isso se deve ao fato de que, quando você definiu sua variável DuplicityCMD , solicitou explicitamente que o " fosse incluído, colocando-o dentro de ' . Eu sugeriria remover o " , pois eles parecem ser desnecessariamente desnecessários.

( Mais informações: Como evitar citações no shell? )

    
por 23.01.2017 / 00:24