Defina menos de duas listas terminadas por novas linhas / listas negras genéricas usando itens domésticos comuns

4

Recentemente, eu estava fazendo o backup de uma árvore de diretórios usando cp -r , quando fiquei sem espaço na unidade de recebimento. Eu tive que continuar com o backup, mas para um destino diferente. Normalmente, para retomar o comando cp , você deve pedir ao cp para copiar o arquivo apenas se ele não estiver no destino. Você pode ver o problema aqui.

Aqui estava a minha solução:

  1. estragar.

    # cp -rv sourcedir destdir
    error: no space left on device.
    
  2. Faça uma lista de todos os arquivos que foram copiados com sucesso.

    # cd destdir
    # find . >/tmp/alreadycopied
    
  3. Escreva um script que possa pegar qualquer lista A e uma lista negra B e retornar uma nova lista C = B \ A, contendo todos os elementos em A que não estejam em B. Eu os chamo setminus. ***generate list A*** | setminus listB retorna C para stdout.

  4. Use find e setminus para copiar todos os arquivos restantes para o novo destino.

    # cd sourcedir
    # find . -type f | setminus /tmp/alreadycopied | xargs -d '\n' -I file cp -v --parents file overflowdestdir
    

Funcionou, mas acho que esse conjunto de listas de menos é um problema bastante comum que as ferramentas padrão do UNIX devem de alguma forma cobrir esse caso de uso, tornando meu script desnecessário. Algum de vocês se deparou com esse problema e, em caso afirmativo, como você o resolveu?

O script setminus:

#!/usr/bin/env python3

# ***generate list*** | setminus blacklist
# Performs a set minus on its inputs and returns the result. Specifically,
# filters a newline-separated list in stdin using blacklist---also a
# newline-separated list. If an element of stdin is found in blacklist, it is
# excluded from the output. Otherwise, the element is returned to stdout. Very
# useful in conjunction with find commands.

from sys import *

try:
    blacklistfile = argv[1]
except IndexError:
    stderr.write('usage: ***generate list*** | setminus blacklist.\n')
    exit(1)

# A dict is used instead of a list to speed up searching. This blacklist could potentially be very large!
blacklist = {}
for line in open(blacklistfile):
    blacklist[line] = True

for line in stdin:
    inblacklist = False
    try:
        inblacklist = blacklist[line]
    except KeyError:
        pass

    if not inblacklist:
        stdout.write(line)
    
por enigmaticPhysicist 06.10.2016 / 13:39

1 resposta

4

Se as suas listas estiverem classificadas, você poderá usar comm -23 para obter os elementos exclusivos da primeira lista. Se não estiverem, você pode usar grep like

find -type f | grep -vFxf /tmp/alreadyCopied
  • -v encontrará todas as linhas sem correspondência
  • -F diz para usar as strings como strings fixas, não como padrões
  • -x corresponde à linha inteira em vez da string em qualquer lugar da linha
  • -f /tmp/alreadyCopied leu as linhas para correspondência do arquivo fornecido

Você precisará garantir que os caminhos correspondam, por isso, se find estiver produzindo ./dir1/file1 , que precisa ser a mesma sequência em /tmp/alreadyCopied

Observe, porém, que essa abordagem geral terá problemas se, digamos, você tiver um nome de arquivo com \n . Você provavelmente poderia refazer tudo em find com algo como

find . -type f -exec test ! -f destdir/{} \; -exec cp -v --parents {} overflowdestdir \;
    
por 06.10.2016 / 13:56