Acción de comando Deshacer copia (cp)

14

Copié algún archivo de la carpeta de origen en la carpeta de destino usando la siguiente línea de comando en la terminal.

sudo cp From_SOURCE/* To_DESTINATION/

Ahora quiero deshacer este comando.

αғsнιη
fuente
No estoy seguro de entenderlo. ¿Simplemente eliminar la copia? (archivo sudo rm)
Jacob Vlijm
@JacobVlijm no es una acción de deshacer, es un comando separado. Si no copié, podría usar su comando sugerido anteriormente. ¿Y tengo que usar cada vez más este comando para eliminar más de 1000 archivos copiados?
αғsнιη
@KasiyA: una opción sería encontrar todos los nombres de archivo seleccionados From_SOURCE/*y eliminarlos de To_DESTINATION/; esto podría hacerse con bash: los problemas aquí podrían incluir que el comando cp sobrescribió cualquier cosa, se copiaron enlaces simbólicos, archivos con espacios en los nombres, etc., por lo que podría tener problemas y volverse un poco loco. Una opción 'deshacer' perfecta podría tener que dar cuenta de muchas cosas.
Wilf

Respuestas:

13

Si entiendo bien, el siguiente es el caso:

  • Copió un número (presumiblemente grande) de archivos en un directorio existente y necesita / desea revertir la acción.
  • El directorio de destino contiene una serie de otros archivos, que debe mantener allí, lo que hace que sea imposible eliminar simplemente todos los archivos del directorio

El siguiente script se ve en el directorio original (fuente) y enumera esos archivos. Luego, busca en el directorio en el que copió los archivos y elimina solo los archivos enumerados, tal como existen en el directorio de origen.

El tryelemento se agrega para evitar errores, por ejemplo, en caso de que ya haya eliminado algunos archivos manualmente, o si no todos los archivos del directorio de origen se copiaron en el destino. Si necesita privilegios de sudo, simplemente ejecute el script con "sudo" (ver más abajo).

La secuencia de comandos

#!/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

Cómo utilizar

  • Pegue el script en un archivo vacío, guárdelo como reverse.py,
  • Inserte las rutas correctas para la carpeta de origen y destino,
  • Hazlo ejecutable por razones de conveniencia,
  • Ejecútelo con el comando:

    [sudo] /path/to/reverse.py
    

Advertencia

¡Primero intente en un directorio de prueba si entendí bien lo que necesita lograr!


Si el directorio fuente es "plano"

En caso de que el directorio fuente no tenga subdirectorios, el script puede ser incluso más simple:

#!/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

Si la acción de copiar sobrescribió (reemplazó) un archivo con un nombre similar en el destino, el archivo se eliminará, pero el archivo original (por supuesto) no será recuperado por el script. La suposición es que no hay enfrentamientos de nombres.

Jacob Vlijm
fuente
Funciona, gracias. Lo paso corriendo sudo python3 reverse.py. Acepto tu respuesta
αғsнιη
¡Perfecto! Gracias por una buena pregunta, que describe una situación en la que probablemente todos hemos estado :)
Jacob Vlijm
Nota: Si algún archivo en el origen sobrescribió un archivo en el destino porque tenía el mismo nombre, entonces simplemente no puede deshacerlo, y este script simplemente lo eliminará. La suposición aquí es que no hubo conflictos de nombres cuando hiciste la inicial cp.
thomasrutter
@neon_overload Correcto, lo edité en mi respuesta.
Jacob Vlijm
Esto no funciona para cp -r. Cambié os.removecon print, y está generando un montón de archivos que no existen. Supongo que faltan los subdirectorios y solo enumero los archivos al final.
Typewar
7

TL; DR:

Todos los archivos que están presentes en ambos srcy destse pueden eliminar de destesta manera:

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

Para una explicación paso a paso, ver más abajo.

Simplificando el problema:

Para comprender qué hizo realmente el comando que queremos deshacer, comenzamos por simplificarlo:

El comando que queremos deshacer es

sudo cp From_SOURCE/* To_DESTINATION/

Para entender cómo deshacer, sudono es relevante.

Usaré los nombres de directorio srcpara From_SOURCEy destpara To_DESTINATION.

Ahora, nuestro comando es:

cp src/* dest/

Si srccontiene los archivos f1, f2y f3, y los directorios d1y d2, el shell expande ese comando para:

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

Sin opciones como -r, -Ro -a, el comando cpno copia directorios.
Eso significa que el comando los dejará fuera, mostrando un error para cada uno de ellos:

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

Eso significa que solo hemos copiado archivos simples, y no directorios, a dest.

Decidir qué archivos eliminar:

Posiblemente había archivos con destel mismo nombre que los archivos src. En este caso, los archivos se sobrescribieron (1). Es demasiado tarde para ellos, lo siento. Recuperarlos de la última copia de seguridad.

Con respecto a los archivos que están allí, queremos eliminar solo los archivos que se han copiado. Estos archivos existen en ambos directorios, con el mismo nombre y el mismo contenido.

Entonces buscamos estos archivos:

En primer lugar, cden src, porque hace que los siguientes findcomandos mucho más simples, y establecer una variable en la ruta absoluta del dest:

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

Luego, enumeramos todos los archivos en src:

$ find . -maxdepth 1 -type f

y, para cada archivo encontrado, use cmppara verificar si hay un archivo con el mismo contenido en dest:

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

Eliminando los archivos, cuidadosamente:

Estos archivos son los que queremos eliminar. Pero para estar seguros, primero los movemos a un directorio diferente, y echamos un vistazo a los comandos antes de ejecutarlos:

$ 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/`

¡Se ve bien! Ahora podemos dejar echode ejecutar los mvcomandos reales :

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

Todos los archivos desde destque fueron copiados de src, y todavía en realidad la misma en srcy dest, ahora están en /tmp/toDelete/, y pueden ser removidos después de tomar una última mirada.


Notas:
(1) Verifique si cpes un alias cp -io no, eso habría evitado sobrescribir archivos preguntando primero.

Volker Siegel
fuente
5

Esto es antiguo, pero solo quería publicar una respuesta bash pura:

Primero cambie al directorio donde copió los archivos.

cd dest

Luego, lsel directorio de origen y canalizar la salida enrm

ls source | xargs rm

Michael Goldstein
fuente
1
análisis lssuele ser una mala idea. mywiki.wooledge.org/ParsingLs No rechazaré su publicación, pero hay que tenerla en cuenta.
Sergiy Kolodyazhnyy