Criando inclusões e exclusões rsync muito longas com Python

1

Estou tentando executar um backup de todas as minhas pastas .x em Home para salvar configurações.

Com o programa que estou escrevendo, você pode selecionar qualquer pasta que queira incluir ou excluir do backup. Essa lista é dinâmica.

Estou tentando criar algo assim:

rsync -aP --exclude $Home/.cache/google-chrome/Default/Cache/* --exclude $Home/.cache/google-chrome/Default/Media\ Cache/* --exclude $Home/.wine $HOME/.* /mnt/ext/

Assim, no exemplo acima, tudo é copiado para uma unidade externa, exceto para os caches do Google Wine e do Chrome. Estou ciente de que posso criar um arquivo de texto e dizer rsync --exclude-from 'textfile' , mas prefiro não ler e reescrever um arquivo de texto.

Minha preocupação é que, à medida que isso se expande, a lista de exclusões pode ficar bastante longa, já que meu projeto eventualmente começará a incluir pastas fora do $ HOME e em compartilhamentos NFS de outros sistemas. Eu posso iniciar o comando e usar a lista de exclusões em uma variável:

exlist = "--exclude $Home/.cache/google-chrome/Default/Cache/* --exclude $Home/.cache/google-chrome/Default/Media\ Cache/* --exclude $Home/.wine" etc etc
subprocess.Popen("rsync -options ",exlist," /source /dest")

Mas isso causará problemas com comandos de shell extremamente longos em que terei que dividi-los em partes menores? Prefiro ficar à frente de problemas futuros em potencial e começar a escrever manipuladores para realizar isso agora, em vez de tentar corrigi-lo e corrigí-lo mais tarde.

    
por Dorian 23.06.2016 / 17:10

2 respostas

1

Descobri que meus limites de tamanho não estão necessariamente no comprimento de um comando, mas sim no comprimento do argumento. Os comandos do shell podem ter entre 100.000 e 200.000 caracteres de comprimento. No entanto, os argumentos estão limitados a algumas centenas de caracteres, dependendo do sistema, conforme descoberto usando getconf ARG_MAX .

Isso me leva ao problema de falta de espaço quando adiciono muitas exclusões de pastas aos meus argumentos rsync .

Para contornar esse problema, eu preciso dividir programaticamente o processo rsync de uma execução longa para várias mais pequenas.

Isso requer "construir como eu vou" até atingir um limite. O programa tentará criar uma cadeia de todas as exclusões individuais, cada uma dentro de uma lista (matriz). Se o limite de tamanho de args for excedido, ele irá reparar a lista atual para fazer exclusões mais amplas, reduzindo o total e armazenando as exclusões excluídas.

Veja um exemplo da lista de exclusões, conforme está sendo criada:

Esta lista é chamada excludes

(long list items...)  
--exclude $Home/.wine  
--exclude $Home/.cache/somefolders/*   
--exclude $Home/.cache/somefolders/*  
--exclude $Home/.cache/somefolders/*  
--exclude $Home/.cache/somefolders/*   
--exclude $Home/.cache/google-chrome/Default/Cache/*  
--exclude $Home/.cache/google-chrome/Default/Media\ Cache/* 
**LIMIT EXCEEDED**

Agora, nosso limite de ARGS foi excedido. O programa reparará essa lista procurando pastas comuns e criará uma segunda lista a ser salva para a próxima execução de rsync .

Após a análise, excludes é recriado em uma lista como esta:

(long list items...)  
--exclude $Home/.wine  
--exclude $Home/.cache/*  

Como foi analisado, as linhas que foram removidas foram colocadas em outra lista, vamos chamá-lo de excludesnext e é assim:

--exclude $Home/.cache/somefolders/*   
--exclude $Home/.cache/somefolders/*  
--exclude $Home/.cache/somefolders/*  
--exclude $Home/.cache/somefolders/*   
--exclude $Home/.cache/google-chrome/Default/Cache/*  
--exclude $Home/.cache/google-chrome/Default/Media\ Cache/*

Assim, todas as pastas repetitivas são removidas de excludes e colocadas em excludesnext . À medida que os itens são movidos e as pastas amplas abreviadas são substituídas em excludes , uma nova lista chamada cutlist é criada com essas novas pastas mais curtas.

Portanto, a lista cutlist inclui:

--exclude $Home/.cache/* 

rsync é executado usando a lista excludes como argumentos.

Depois que rsync for concluído, a lista excludes será esvaziada e a lista excludesnext será copiada para a lista excludes .

Em seguida, o rsync é executado em um loop usando a nova lista abreviada cutlist como a origem, e todas as subpastas correspondentes de excludes são usadas como exclusões.

É claro que pode haver muitos itens na lista cutlist , e o programa passará por cada um deles. Esta segunda execução de rsync é executada em um loop. Mas neste loop, quando os comprimentos máximos são atingidos, a excludes lista é analisada da mesma forma que antes, exceto que as novas pastas encurtadas são adicionadas a cutlist em vez de irem para excludes . Dessa forma, enquanto o loop passa pela lista de corte, ela pode se expandir à medida que passa pela lista. Como os itens da lista excludesnext são usados, eles são removidos.

Eventualmente, isso faz com que todas as pastas e subpastas sejam copiadas com todas as exclusões incluídas, independentemente de quantas delas existirem. Completar o último item em cutlist significa que tudo foi executado.

Esse método pode fazer com que rsync seja executado várias ou até várias vezes, dependendo de quantas exclusões você tem, mas nunca repete as pastas.

Desculpas por não postar código real, mas é um trabalho confuso em andamento e não me sinto à vontade para postar meu código antes de poder liberar um serviço de trabalho.

    
por Dorian 28.06.2016 / 16:57
0
  

Estou ciente de que posso criar um arquivo de texto e dizer ao rsync --exclude-from 'textfile', mas prefiro não ter que ler e reescrever um arquivo de texto.

Basta fazer:

with contextlib.closing(tempfile.NamedTemporaryFile()) as exclude_from:
    print(*your_exclude_list, sep="\n", flush=True, file=exclude_from)  # etc
    subprocess.check_call(['rsync', '--exclude-from', exclude_from.name, ...])

... e não se preocupe com o arquivo temporário. Eu aprecio que os arquivos temporários pareçam confusos, mas com a biblioteca e os gerenciadores de contexto do Python tudo isso pode ser muito bem embrulhado em um pacote com um arco para que você não tenha que se preocupar com isso.

    
por Robie Basak 28.06.2016 / 17:10