¿Por qué es necesario -r recursivo al copiar un directorio en Linux?

47

Mi pregunta es ¿por qué es necesario usar el -rindicador (recursivo) al hacer una copia de un directorio? Es decir, por qué hacer esto:

$ cp -r dir1 copyDir1

¿Cuándo no querría este comportamiento al copiar un directorio?

¿No es una copia recursiva de un directorio realmente el comportamiento "predeterminado"; El comportamiento que queremos casi todo el tiempo?

Parece que esta es una bandera superflua.

Madeleine P. Vincent
fuente
¿No necesitarías copiar también los archivos y la carpeta?
QuyNguyen2013
Si cree que esto sería una mejora, puede volver a publicar esta solicitud en el canal de desarrolladores. De lo contrario, probablemente fue programado hace mucho tiempo.
blogger
@blogger Fue programado hace mucho tiempo, pero por una razón. Es decir, si alguien quiere hacer un trabajo básico en un entorno de línea de comandos, su tarea solo debe ser tan fácil como evitar un fallo del sistema. Lo que significa que hay buenas razones por las que existen algunas convenciones de interacción del usuario en la línea de comandos. Expando este concepto en mi respuesta.
JakeGould
2
Esta pregunta ha sido formulada y respondida en unix.SE .
dotancohen
Lo mismo va pararm

Respuestas:

58

En la forma en que funcionan los sistemas de archivos, un directorio no es en realidad una carpeta que contiene archivos, sino que un directorio es un archivo que contiene punteros de inodo a archivos "secundarios" conectados a él. Es decir, desde la perspectiva del sistema de archivos, un archivo es un archivo, pero un directorio es solo un archivo que contiene una lista de archivos conectados.

Entonces, desde la perspectiva de la línea de comando, haciendo esto:

$ cp dir1 copyDir1

Básicamente significaría copiar el archivo llamado dir1a un nuevo archivo llamado copyDir1. Y en lo que respecta al sistema de dir1archivos, de todos modos es solo un archivo; el hecho de que sea un "directorio" solo será evidente cuando el sistema de archivos realmente verifique dir1qué es realmente ese montón de bits.

La -rbandera le dice al sistema de archivos que despliegue recursivamente el árbol de archivos / directorios y copie cualquier contenido que pueda ser un "hijo" de ese archivo a un nuevo lugar.

Ahora, por qué eso puede parecer superfluo o redundante, esto realmente se reduce a métodos históricos para tratar con sistemas de archivos. Además de crear un sistema que esté a salvo de todo tipo de errores relacionados con el usuario; accidental así como intencional.

Es decir, supongamos que tiene un ~/binarchivo en su directorio de inicio que desea copiar pero que accidentalmente dejó de lado, ~porque es un ser humano y comete errores, así que es /binasí:

cp /bin/ ~/copy_of_bin

Con la "red de seguridad" de /binser un directorio combinado con la necesidad del -rindicador, evitará copiar accidentalmente toda la raíz binaria del sistema en el que se encuentra en su directorio de inicio. Si esa red de seguridad no existiera, ocurriría un desastre menor, o posiblemente mayor.

La lógica aquí es que en los días previos a las GUI (interfaces gráficas de usuario) las convenciones lógicas / de comportamiento deben establecerse para evitar que el usuario cree contratiempos que potencialmente pueden matar un sistema. Y usar la -rbandera ahora es uno de ellos.

Si eso parece superfluo, entonces no necesita buscar más allá del sistema GUI moderno que uno puede colocar por encima de los sistemas de archivos Linux. Una GUI aborda problemas básicos del usuario como este al permitir arrastrar y soltar archivos y directorios con facilidad.

Pero en el caso de las interfaces basadas en texto, gran parte de la "experiencia del usuario" dentro de ese mundo es básicamente un bache de camino lógico y basado en huerística que ayuda a mantener al usuario bajo control para evitar posibles desastres.

De manera similar, esta es la razón por la cual los sistemas de archivos Linux / Unix no tienen 777permisos y sudoderechos establecidos de manera predeterminada y cómo los administradores de sistemas reales hacen una mueca cuando un usuario establece 777permisos o otorga sudoderechos a todos . Estas son las cosas básicas que uno hace para garantizar que el sistema sea estable y lo más "a prueba del usuario" posible; cualquiera que se apresure a cortocircuitar esas convenciones probablemente causará daños a su sistema sin siquiera saberlo.

INFORMACIÓN ADICIONAL: Otra respuesta aquí en el sitio Unix Stack Exchange da una buena explicación de por qué una copia no recursiva de un directorio es problemática; El énfasis es mío.

Bueno, sin el indicador -R, solo es posible copiar archivos, porque es bastante inusual que alguien quiera copiar un directorio de forma no recursiva : una copia no recursiva solo daría como resultado un segundo nombre para el directorio, apuntando directamente a misma estructura de directorio Debido a que rara vez eso es lo que la gente quiere, y en realidad hay un programa separado que hace esto (ln), no se permite una copia no recursiva de directorios.

Entonces, si un directorio es realmente un archivo con elementos de inodo dentro de él, hacer una copia directa de ese archivo sería el equivalente de cómo funcionaría un enlace duro. Que no es lo que nadie quiere.

JakeGould
fuente
19
Personalmente, creo que el aspecto de "protección" no pasa la prueba del olor. Algunos humanos podrían escribir cp -r /bintan fácilmente como cp-r ~/bin. La bandera en sí misma no evita errores ni necesariamente hace que nadie sea más cuidadoso. Si desea evitar errores, el comando cp podría mirar fácilmente el nodo en cuestión y proporcionar un mensaje, algo así como "Este es un directorio, ¿desea copiar todo el contenido en la ubicación especificada (y /norte)?" Eso sería una red de seguridad . Requerir -r para directorios mantiene la hinchazón de código más baja que cualquier otra cosa.
JDL
12
Mala analogía con la boca de acceso. Como dijo @JDL, la bandera en cuestión no hace nada para evitar un error tipográfico en el camino. Sería más feliz aceptar la coherencia con otros comandos como una razón, pero tengo la sensación de que la verdadera razón es "así fue escrito por primera vez y ahora muchas cosas dependen de ese comportamiento, cambiarlo es imposible".
Básico
77
Cuando movemos un directorio no necesitamos -r. Creo que la respuesta vinculada en unix.stackexchange.com es mucho más importante. El equivalente a una copia no recursiva sería tener un segundo directorio y enlaces duros para todos los archivos dentro del árbol de directorios.
gerrit
2
Si no me equivoco, -r era una extensión de GNU, no creo que el cp UNIX histórico tuviera una copia recursiva, y esa fue parte de la razón del comando rsync .
Mei
2
@JakeGould entiendo los conceptos básicos. Sin embargo, argumento en contra de la idea de que una bandera -r en el comando en cuestión proporciona seguridad adicional. Desde mi experiencia, los comandos de línea de comandos de Linux son históricamente inherentemente inseguros. La seguridad se ha agregado horas extras, si es que lo ha hecho. La seguridad inherente al diseño de Linux proviene del sistema de permisos y de dejar de ejecutarse como root. No ejecutarse como root también es algo que es más una convención que un diseño. La convención es compatible con la mayoría de los nuevos instaladores de Linux, pero no siempre fue así.
JDL
19

Es muy cierto que este es el comportamiento que queremos casi todo el tiempo. Sin embargo, esto no significa necesariamente que copiar de forma recursiva sea el comportamiento predeterminado.

Creo que las razones cpactúan como si tuviera raíces en la filosofía de Unix . Unix favorece los programas que hacen una cosa y lo hacen bien , así como los programas que son simples tanto en la interfaz como en la implementación (a veces llamado peor es mejor ).

La pieza clave del rompecabezas aquí es darse cuenta de que cpno copia directorios: cpcopia archivos (y solo archivos). Si desea copiar un directorio, se cp llama a sí mismo de forma recursiva , para copiar los archivos en cada directorio.

Por supuesto, la diferencia entre "copiar directorios" y "copiar archivos recursivamente" no es absolutamente nada, desde la perspectiva del usuario, pero tener esta interfaz ayuda a que la implementación siga siendo simple .

Si cppudo copiar directorios, pronto estaría tentado de agregar más funciones que solo tengan sentido para los directorios; por ejemplo, es posible que desee copiar solo los nombres de archivo que terminaron en .sh. Inevitablemente, esto conduce a la distorsión de la hinchazón y las características a las que estamos acostumbrados en otros sistemas operativos, lo que hace que el software sea lento, complejo y propenso a errores.

Otra ventaja es que tener -rtambién ayuda al usuario a comprender lo que realmente está sucediendo debajo de la interfaz. Un buen efecto secundario de esto es que aprender el concepto de operación recursiva le ahorrará algo de trabajo cuando aprenda sobre otras herramientas que lo soportan (como grep, por ejemplo)


Algunas personas seguramente le dirán que exponer los detalles de implementación al usuario es malo , y que tener más funciones es bueno . Mi intención aquí es simplemente explicar la lógica de este comportamiento, por lo que no intentaré discutir de ninguna manera.

goncalopp
fuente
2
+1 "... haz una cosa y hazlo bien ..." ¡ Gracias por decir esto!
JakeGould
5

Las interacciones con los directorios aseguran que sepa que está interactuando con un directorio y NO con un solo archivo.

Por ejemplo:

$ tree
.
└── folder1
    └── sub1
        └── subsub1

3 directories, 0 files
$
$ cp folder1/ folder2
cp: folder1/ is a directory (not copied).
$
$ mkdir blah
$ cp blah/ blah2
cp: blah/ is a directory (not copied).
$ rm blah/
rm: blah/: is a directory

Entonces, si desea copiar con éxito una carpeta, ya que implica tanto la carpeta como los objetos relacionados con la referencia de la carpeta, debe tratarla como si fuera una colección de archivos:

$ cp -r folder1/ folder2
$ rm -rf folder1
mbb
fuente
3

La consecuencia de cambiar el valor predeterminado sería que miles de scripts de shell se romperían. Esto lleva a los requisitos POSIX y SUS para el comportamiento predeterminado bien conocido.

La razón es el desarrollo histórico de los comandos cp, ln y mv (todos los mismos binarios en la mayoría de los sistemas UNIX antiguos) en varias ramas UNIX. Cuando -rapareció (temprano cpno tenía una opción para copiar directorios; aquí hay una página de manual de cp temprana sin -ro -R), hubo varias diferencias en el manejo de archivos especiales, enlaces simbólicos y otros caprichos del sistema de archivos.

De The Open Group Base Especificaciones Número 7 :

Las versiones anteriores de este estándar incluían soporte para la opción -r para copiar jerarquías de archivos. La opción -r es una práctica histórica en sistemas BSD y derivados de BSD. POSIX.1-2008 ya no especifica esta opción, pero puede estar presente en algunas implementaciones. La opción -R se agregó como un sinónimo cercano a la opción -r, seleccionada por coherencia con todas las demás opciones en este volumen de POSIX.1-2008 que realizan el descenso recursivo de directorios.

La diferencia entre -R y la opción -r eliminada está en el tratamiento por cp de los tipos de archivos que no sean regular y directorio. Se definió la implementación de cómo la opción - trató los archivos especiales para permitir tanto las implementaciones históricas como las que eligieron soportar -r con las mismas capacidades que -R definidas por este volumen de POSIX.1-2008. El indicador -r original, por razones históricas, no manejaba los archivos especiales de manera diferente a los archivos normales, pero siempre leía el archivo y copiaba su contenido. Esto tenía problemas obvios en presencia de tipos de archivos especiales; por ejemplo, dispositivos de caracteres, FIFO y sockets.

De hecho, todavía verá que algunas personas usan regularmente:

cd dir1 ; tar -cf - . | (cd dir2 ; tar -xpf -)

Porque no confían en que la cp -rimplementación sea lo que están acostumbrados en una máquina arbitraria; O porque quieren el tarcomportamiento.

kael
fuente
3

Puede ser una interfaz de usuario subóptima hoy en día, pero fue una decisión tomada en algún momento alrededor de 1970 durante el diseño de UNIX cuando el disco era considerablemente más caro. Millones de scripts de shell dependen de que funcione de esa manera, y es demasiado tarde para cambiarlo.

Vea este artículo para la información de diseño original.

pjc50
fuente
3

Una clara ventaja del -rindicador es que puede cp * /target/dircopiar solo todos los archivos del directorio de origen en el directorio de destino, omitiendo (aunque con una advertencia) todos los directorios contenidos en él. cp -r * /target/dircopiaría todo en su lugar, incluidos los subdirectorios.

Interneci
fuente
2

Necesita este indicador solo cuando cpes un comando para copiar archivos y directorios y no solo directorios.

Si hubiera un comando especial para la copia de directorios, el comportamiento "predeterminado" seguramente sería una copia recursiva.

tipo
fuente
1
Tiene sentido. Pero, ¿por qué alguien estaría copiando un directorio que no tiene al menos un archivo? ¿Por qué no simplemente usar mkdir?
JakeGould
1
¿@JakeGould quizás porque pueden necesitar preservar la propiedad y los permisos?
Ruslan
1

Como otros han mencionado, un directorio es básicamente otro tipo de archivo (a diferencia de un archivo normal), que generalmente "contiene" (apunta a) otros archivos. Podría contener subdirectorios, para lo cual se aplica lo mismo ...

Entonces, si está copiando un directorio (perspectiva del usuario), realmente está copiando un montón de archivos (perspectiva del sistema de archivos) (archivos normales, archivos de directorio, enlaces simbólicos, ...) y para cada archivo de directorio, está repitiendo eso recursivamente proceso. Como copiar un directorio es, por definición, un proceso recursivo, se llama el argumento de cp --recursive.

Por supuesto, es muy fácil crear un acceso directo de comando en su entorno de usuario (póngalo en su archivo .profile / .bashrc para que esté disponible permanentemente):

alias cpr='cp -r'

O tal vez mejor:

alias cpa='cp -av'

De esa manera, puede copiar un directorio utilizando cpa dir1 copyDir1y no solo imprimirá lo que se está copiando, sino que también aplicará permisos de archivo.

Y dado que alguien mencionó que cp podría detectar teóricamente que el archivo fuente es un directorio y preguntar si debería copiarlo recursivamente, aquí hay una sugerencia rápida:

cp()
{
    if [ ! -e "$1" ]; then
        echo missing source file
        return 1
    fi
    arg="-d --preserve=all -v"
    if [ -d "$1" ]; then
        read -p "Copy directory recursively? " -n 1 -r
        if [ "$REPLY" == "y" ]; then
            arg="$arg -r"
        fi
        echo
    fi
    /usr/bin/cp $arg "$@"
}

Esto es solo un envoltorio de cp barato. Siempre conserva todos los metadatos (es decir, copia el tiempo de modificación del archivo, copia correctamente los enlaces simbólicos, etc.) y si está intentando copiar un directorio, le pregunta si debe copiarlo (recursivamente).

básico6
fuente