Desfazer ação do comando copy (cp)

14

Copiei algum arquivo da pasta de origem para a pasta de destino usando a linha de comando abaixo no terminal.

sudo cp From_SOURCE/* To_DESTINATION/

Agora eu quero desfazer este comando.

αғsнιη
fonte
Não sei se entendi. Basta remover a cópia? (sudo arquivo rm)
Jacob Vlijm
@JacobVlijm não é ação de desfazer, é um comando separado. Se eu não copiasse, poderia usar seu comando sugerido anteriormente. E eu tenho que usar cada vez mais este comando para excluir mais de 1000 arquivos copiados?
αғsнιη
@KasiyA - uma opção seria encontrar todos os nomes de arquivos selecionados From_SOURCE/*e removê-los To_DESTINATION/; isso pode ser feito com o bash - os problemas aqui podem incluir o comando cp, substituindo qualquer coisa, links simbólicos foram copiados, arquivos com espaços em nomes etc. - para que ele possa ter problemas e ficar um pouco maluco. Uma opção perfeita de "desfazer" poderia ser responsável por muitas coisas.
Wilf

Respostas:

13

Se bem entendi, é o seguinte:

  • Você copiou um número (presumivelmente grande) de arquivos para um diretório existente e precisa / deseja reverter a ação.
  • O diretório de destino contém vários outros arquivos que você precisa manter lá, o que torna impossível simplesmente remover todos os arquivos do diretório

O script abaixo procura no diretório original (fonte) e lista esses arquivos. Em seguida, ele analisa o diretório para o qual você copiou os arquivos e remove apenas os arquivos listados, pois existem no diretório de origem.

O tryelemento é adicionado para evitar erros, por exemplo, caso você já tenha removido alguns arquivos manualmente ou se nem todos os arquivos do diretório de origem foram copiados para o destino. Se você precisar de privilégios de sudo, basta executar o script com "sudo" (veja abaixo).

O script

#!/usr/bin/env python

import os

source_dir = "/path/to/source" # the folder you copied the files from
target_folder = "/path/to/destination" # the folder you copied the files to

for root, dirs, files in os.walk(source_dir):
    for name in files:
        try:
            os.remove(target_folder+"/"+name)
        except FileNotFoundError:
            pass

Como usar

  • Cole o script em um arquivo vazio, salve-o como reverse.py,
  • Insira os caminhos corretos para a pasta de origem e destino,
  • Torne-o executável por razões de conveniência,
  • Execute-o pelo comando:

    [sudo] /path/to/reverse.py
    

Atenção

Primeiro tente um diretório de teste se eu entendi bem o que você precisa alcançar!


Se o diretório de origem for "plano"

Caso o diretório de origem não possua subdiretórios, o script pode ser ainda mais simples:

#!/usr/bin/env python

import os

source_dir = "/path/to/source" # the folder you copied the files from
target_folder = "/path/to/destination" # the folder you copied the files to

for file in os.listdir(source_dir):
    try:
        os.remove(target_folder+"/"+file)
    except FileNotFoundError:
        pass

Nota

Se a ação de copiar overwrote (substituído) um arquivo de nome semelhante no destino, o arquivo será removido, mas o arquivo original (é claro) não ser trazido de volta pelo script. A suposição é que não há conflitos de nome.

Jacob Vlijm
fonte
Funciona, obrigado. Eu corro sudo python3 reverse.py. Eu aceito sua resposta.
αғsнιη
Perfeito! Obrigado por uma questão legal, que descreve uma situação Provavelmente todos nós já esteve em :)
Jacob Vlijm
Nota: Se algum arquivo na origem sobrescreveu um arquivo no destino por ter o mesmo nome, você não pode simplesmente desfazê-lo, e esse script apenas o exclui. A suposição aqui é que não houve conflitos de nome quando você fez a inicial cp.
precisa saber é o seguinte
@neon_overload Correto, editei na minha resposta.
Jacob Vlijm
Isso não funciona cp -r. Eu mudei os.removecom print, e ele está produzindo um monte de arquivos que não existem. Suponho que esteja faltando os subdiretórios e listando apenas os arquivos no final.
Typewar
7

TL; DR:

Todos os arquivos que estão presentes nos dois srce destpodem ser removidos destassim:

find . -maxdepth 1 -type f -exec cmp -s '{}' "$destdir/{}" \; -exec mv -n "$destdir/{}" "$toDelete"/ \;

Para uma explicação passo a passo, veja abaixo.

Simplificando o problema:

Para entender o que o comando que queremos desfazer realmente fez, começamos por simplificá-lo:

O comando que queremos desfazer é

sudo cp From_SOURCE/* To_DESTINATION/

Para entender como desfazer, sudonão é relevante.

Vou usar os nomes de diretório srcpara From_SOURCEe destpara To_DESTINATION.

Agora, nosso comando é:

cp src/* dest/

Se srccontém os arquivos f1, f2e f3, e os diretórios d1e d2, o shell expande esse comando para:

cp src/f1 src/f2 src/f3 src/d1 src/d2 dest/

Sem opções como -r, -Rou -a, o comando cpnão copia diretórios.
Isso significa que o comando os deixará de fora, mostrando um erro para cada um deles:

$ cp src/f1 src/f2 src/f3 src/d1 src/d2 dest/
cp: omitting directory 'src/d1'
cp: omitting directory 'src/d2'

Isso significa que apenas copiamos arquivos simples e nenhum diretório para dest.

Decidindo quais arquivos remover:

Possivelmente havia arquivos com desto mesmo nome que arquivos src. Nesse caso, os arquivos foram substituídos (1). É tarde demais para eles, desculpe. Recupere-os do backup mais recente.

Em relação aos arquivos existentes, queremos remover apenas os arquivos que foram copiados. Esses arquivos existem nos dois diretórios, com o mesmo nome e o mesmo conteúdo.

Então, procuramos esses arquivos:

Primeiro, cdem src, porque faz as seguintes findcomandos muito mais simples, e definir uma variável para o caminho absoluto do dest:

$ cd src
$ destdir="$(readlink -f dest)"

Em seguida, listamos todos os arquivos no src:

$ find . -maxdepth 1 -type f

e, para cada arquivo encontrado, use cmppara verificar se há um arquivo com o mesmo conteúdo em dest:

$ find . -maxdepth 1 -type f -exec cmp -s '{}' "$destdir/{}" \; -print

Removendo os arquivos, com cuidado:

Esses arquivos são os que queremos remover. Mas para ter certeza, nós os movemos para um diretório diferente primeiro - e examinamos os comandos antes de executá-los:

$ toDelete=/tmp/toDelete ; mkdir -p "$toDelete"
$ find . -maxdepth 1 -type f -exec cmp -s '{}' "$destdir/{}" \; -exec echo mv -n "$destdir/{}" "$toDelete"/ \;
mv -n /path/to/dest/./f1 /tmp/toDelete/`
mv -n /path/to/dest/./f2 /tmp/toDelete/`
mv -n /path/to/dest/./f3 /tmp/toDelete/`

Parece bom! Agora podemos deixar echode executar os mvcomandos reais :

find . -maxdepth 1 -type f -exec cmp -s '{}' "$destdir/{}" \; -exec mv -n "$destdir/{}" "$toDelete"/ \;

Todos os arquivos destcopiados srce ainda os mesmos em srce destagora estão em /tmp/toDelete/e podem ser removidos depois de uma última olhada.


Notas:
(1) Verifique se cphá um alias mais cp -iou menos, que teria evitado sobrescrever arquivos perguntando primeiro.

Volker Siegel
fonte
5

Isso é antigo, mas eu só queria postar uma resposta pura do bash:

Primeiro mude para o diretório em que você copiou os arquivos.

cd dest

Em seguida, lso diretório de origem e canalize a saída pararm

ls source | xargs rm

Michael Goldstein
fonte
1
analisar lsnormalmente é uma má ideia. mywiki.wooledge.org/ParsingLs Não vou votar contra sua postagem, mas ela deve ser observada.
Sergiy Kolodyazhnyy 17/07/2016