Linux: copie y cree el directorio de destino si no existe


348

Quiero un comando (o probablemente una opción para cp) que cree el directorio de destino si no existe.

Ejemplo:

cp -? file /path/to/copy/file/to/is/very/deep/there


55
La respuesta aceptada es incorrecta (existe una opción para cp). Ver segunda respuesta de Paul Whipp.
Ronenz

1
@Ronenz, estoy de acuerdo en que hay una opción que vale la pena mencionar en GNU cp(como respondió Paul Whipp), pero no es tan general como lo solicitó el OP. Se puede copiar a/foo/bar/filea b/foo/bar/filepero no puede copiar filea b/foo/bar/file. (es decir, solo funciona para crear directorios principales que ya existían en el caso del primer archivo, pero no puede crear directorios arbitrarios)
Sudo Bash

Parece una buena solicitud de función para cp. ¿Hay alguna posibilidad de que podamos obtener un cambio de línea de comando en un futuro cercano?
Arnaud

Respuestas:


328
mkdir -p "$d" && cp file "$d"

(no hay tal opción para cp).


6262
No creo que necesite el test -d: mkdir -ptodavía tiene éxito incluso si el directorio ya existe.
ephemient

Uy, cierto. Pero entonces, la prueba puede ser un bash incorporado (especialmente si está escrito como [[-d "$ d"]]) y mkdir no puede ;-)
Michael Krelin - hacker

1
mkdirestá integrado en BusyBox;)
ephemient

77
@holms, $des una variable que presumiblemente contiene el directorio en cuestión. Quiero decir, si tienes que repetirlo tres veces, es probable que primero lo asignes a la variable.
Michael Krelin - hacker

237

Si se cumple lo siguiente:

  1. Está utilizando la versión GNU de cp(y no, por ejemplo, la versión de Mac), y
  2. Está copiando desde alguna estructura de directorios existente y solo necesita volver a crearla

entonces puedes hacer esto con la --parentsbandera de cp. Desde la página de información (visible en http://www.gnu.org/software/coreutils/manual/html_node/cp-invocation.html#cp-invocation o con info cpo man cp):

--parents
     Form the name of each destination file by appending to the target
     directory a slash and the specified name of the source file.  The
     last argument given to `cp' must be the name of an existing
     directory.  For example, the command:

          cp --parents a/b/c existing_dir

     copies the file `a/b/c' to `existing_dir/a/b/c', creating any
     missing intermediate directories.

Ejemplo:

/tmp $ mkdir foo
/tmp $ mkdir foo/foo
/tmp $ touch foo/foo/foo.txt
/tmp $ mkdir bar
/tmp $ cp --parents foo/foo/foo.txt bar
/tmp $ ls bar/foo/foo
foo.txt

44
Esto no funciona en Mac OS X, así que supongo que es específico de Linux.
olt

77
Yo diría que es específico de GNU. Si lo tiene macports, puede instalar coreutils y su gcp.
Michael Krelin - hacker

16
Hombre, Linux lo tiene todo
Ziggy

19
Siento que esta es la respuesta a una pregunta ligeramente diferente, a pesar de que es la respuesta que estaba buscando (y, por lo tanto, la que voté). Creo que esta es la solución si quiere enviar los directorios padre destino a ser los mismos que los directorios de origen de los padres, que es probablemente el caso de usar que la mayoría de quienes lean estas interesantes de.
David Winiecki

77
Esto supone que el directorio 'bar' ya existe, pero la pregunta original implica cómo crear automáticamente 'bar' cuando se proporciona como destino y aún no existe. La respuesta a esto es proporcionada por @ MichaelKrelin-hacker.
Thdoan

69

Respuesta corta

Para copiar myfile.txta /foo/bar/myfile.txt, use:

mkdir -p /foo/bar && cp myfile.txt $_

¿Como funciona esto?

Hay algunos componentes para esto, así que cubriré toda la sintaxis paso a paso.

La utilidad mkdir , como se especifica en el estándar POSIX , crea directorios. El -pargumento, por los documentos, hará que mkdir a

Cree cualquier componente de nombre de ruta intermedio que falte

lo que significa que al llamar mkdir -p /foo/bar, mkdir creará /foo y /foo/bar si /fooaún no existe. (Sin -pembargo, en su lugar arrojará un error.

El &&operador de lista, como se documenta en el estándar POSIX (o el manual Bash si lo prefiere), tiene el efecto de que cp myfile.txt $_solo se ejecuta si se mkdir -p /foo/barejecuta con éxito. Esto significa que el cpcomando no intentará ejecutarse si mkdirfalla por una de las muchas razones por las que podría fallar .

Finalmente, $_pasamos como segundo argumento a cpun "parámetro especial" que puede ser útil para evitar repetir argumentos largos (como rutas de archivos) sin tener que almacenarlos en una variable. Según el manual de Bash :

se expande al último argumento del comando anterior

En este caso, esa es la /foo/barque pasamos mkdir. Entonces el cpcomando se expande a cp myfile.txt /foo/bar, que copia myfile.txten el /foo/bardirectorio recién creado .

Tenga en cuenta que no$_ es parte del estándar POSIX , por lo que, en teoría, una variante de Unix podría tener un shell que no sea compatible con esta construcción. Sin embargo, no conozco ningún caparazón moderno que no sea compatible ; ciertamente Bash, Dash y zsh todos lo hacen.$_


Una nota final: el comando que he dado al comienzo de esta respuesta supone que los nombres de su directorio no tienen espacios. Si está tratando con nombres con espacios, deberá citarlos para que las diferentes palabras no se tratan como argumentos diferentes para mkdiro cp. Entonces su comando realmente se vería así:

mkdir -p "/my directory/name with/spaces" && cp "my filename with spaces.txt" "$_"

16
Aparte: generalmente me decepciona la falta de detalles en las preguntas sobre los comandos de shell Bash o Unix en Stack Overflow, que a menudo arrojan una línea compleja y extremadamente densa que es difícil de eliminar si aún no eres un asistente de Unix. He intentado ir al extremo opuesto aquí y ambos explican cada detalle de la sintaxis y enlazan a los lugares apropiados para encontrar documentación sobre cómo funciona esto. Espero que esto ayude al menos a algunas personas. Además, el crédito es debido: descubrí este enfoque de esta respuesta a una pregunta duplicada: stackoverflow.com/a/14085147/1709587
Mark Amery

Nunca he visto $ _ antes. ¿a que se refiere? la carpeta utilizada en el último comando?
thebunnyrules

99
@thebunnyrules Pero ... pero ... hay un "¿Cómo funciona esto?" sección en mi respuesta que literalmente contiene múltiples párrafos específicamente dedicados a la pregunta que acaba de hacer. Se siente ignorado y no amado. :(
Mark Amery

Lo siento por eso. Estás absolutamente en lo correcto. Ya sabía todo lo demás en tu respuesta, así que lo escaneé rápidamente. Debería haberlo leído más cuidadosamente.
thebunnyrules

Wow, realmente pusiste mucho trabajo en esa respuesta ... Gracias por eso. Fue agradable aprender sobre: ​​$ _. Puedo verme usándolo bastante de ahora en adelante. Escribí un script para automatizar todo el proceso y lo puse en este hilo.
Pruébalo

39

Una pregunta tan antigua, pero tal vez pueda proponer una solución alternativa.

Puede usar el installprograma para copiar su archivo y crear la ruta de destino "sobre la marcha".

install -D file /path/to/copy/file/to/is/very/deep/there/file

Sin embargo, hay algunos aspectos a tener en cuenta:

  1. debe especificar también el nombre del archivo de destino , no solo la ruta de destino
  2. el archivo de destino será ejecutable (al menos, por lo que vi de mis pruebas)

Puede modificar fácilmente el # 2 agregando la -mopción de establecer permisos en el archivo de destino (ejemplo: -m 664creará el archivo de destino con permisos rw-rw-r--, al igual que crear un nuevo archivo con touch).


Y aquí está el enlace descarado a la respuesta que me inspiró =)


Realmente no funcionó para mi uso, ¡pero genial truco! Lo tendré en cuenta.
thebunnyrules

tenga en cuenta que este comando crea los archivos de destino con permisos 755( rwxr-xr-x), lo que probablemente no se desea. puede especificar algo más con el -minterruptor, pero no pude encontrar una manera de mantener los permisos del archivo :(
törzsmókus

19

Función de Shell que hace lo que quiere, llamándolo una copia "enterrada" porque cava un agujero para que el archivo viva:

bury_copy() { mkdir -p `dirname $2` && cp "$1" "$2"; }

1
Debe tener entre comillas el dirname $2demasiado
Tarnay Kálmán

44
@Kalmi, para una cita adecuada, también querrás citarlo $2como argumento dirname. Al igual mkdir -p "$(dirname "$2")". Sin esta cita $2en cpno sirve para nada;)
Michael Krelin - pirata informático

3
Tenga en cuenta que esta respuesta asume que $ 2 no es el directorio de destino, de lo contrario solo querría "mkdir -p" $ 2 "&& cp" $ 1 "" $ 2 "como el cuerpo de la función
usuario467257

Creo que esto tiene un error, como señala @ user467257. No creo que esto sea reparable, ya que $ 2 es ambiguo entre el directorio de destino y el archivo de destino en este escenario. He publicado una respuesta alternativa que aborda esto.
Rico

@ Rich, no estoy de acuerdo con que esto no sea reparable. Lo siguiente funciona bien tanto para archivos como para directorios: mkdir -p dirname "$2"&& cp -r "$ 1" "$ 2"; Tenga en cuenta que los backticks dirname $2no aparecen porque SO los analiza como marcadores de código.
Yury

11

Aquí hay una forma de hacerlo:

mkdir -p `dirname /path/to/copy/file/to/is/very/deep/there` \
   && cp -r file /path/to/copy/file/to/is/very/deep/there

dirnamele dará el padre del directorio o archivo de destino. mkdir -p `dirname ...` creará ese directorio asegurando que cuando llame a cp -r el directorio base correcto esté en su lugar.

La ventaja de este sobreparent es que funciona para el caso donde el último elemento en la ruta de destino es un nombre de archivo.

Y funcionará en OS X.


6

install -D file -m 644 -t /path/to/copy/file/to/is/very/deep/there


1
en realidad, no es lo mismo en absoluto. Esto requiere que pase el nombre del archivo dos veces (de ahí el indicador '-t') y establezca los bits de ejecución en el archivo (de ahí el ejemplo '-m'). la suya no debería ser la respuesta principal, ya que en realidad no responde la pregunta, mientras que la mía sí.
Spongman el

1
Esto funciona para un solo archivo. Solo tenga en cuenta que therees el archivo final y no el subdirectorio final.
kvantour

6

Con todo mi respeto por las respuestas anteriores, prefiero usar rsync de la siguiente manera:

$  rsync -a directory_name /path_where_to_inject_your_directory/

ejemplo:

$ rsync -a test /usr/local/lib/

Lo intenté y obtuve este resultado: rsync -a bull / some / hoop / ti / doop / ploop RESULT rsync: mkdir "/ some / hoop / ti / doop / ploop" falló: No existe tal error de archivo o directorio (2) rsync : error en el archivo IO (código 11) en main.c (675) [Receiver = 3.1.2]
thebunnyrules

@thebunnyrules, debe verificar la ruta con el comando pwd (consulte: [link] ( access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/… )) e insértelo correctamente en su instrucción
marcdahan

Si entiendo su sugerencia correctamente, está proponiendo que especifique una ruta completa a la fuente, alias uso: "$ (pwd) / bull" en mi comando en lugar de solo el nombre de archivo: bull? La cuestión es que el error está en que rsync crea el directorio de destino, no la fuente (bull). Me parece que rsync usa un mkdir simple y no un mkdir -p.
thebunnyrules

1
Gran utilidad, por cierto, podría comenzar a usar en mis scripts. Gracias por recomendarlo.
thebunnyrules

1
rsync Es más predecible que cp. Por ejemplo, si deseo, cp /from/somedir /to-direntonces cp se comporta de manera diferente si /to-dirya existe: si /to-direxiste, cpcreará /to-dir/some-dir, de lo contrario solo /from/somedirse colocará el contenido de /to-dir. Sin embargo, rsyncse comporta de la misma en el caso, es decir, /to-dir/some-dirserá siempre ser creado.
JoeAC

5

Solo para reanudar y dar una solución de trabajo completa, en una línea. Tenga cuidado si desea cambiar el nombre de su archivo, debe incluir una forma de proporcionar una ruta de directorio limpia a mkdir. $ fdst puede ser file o dir. El siguiente código debería funcionar en cualquier caso.

fsrc=/tmp/myfile.unk
fdst=/tmp/dir1/dir2/dir3/myfile.txt
mkdir -p $(dirname ${fdst}) && cp -p ${fsrc} ${fdst}

o golpe específico

fsrc=/tmp/myfile.unk
fdst=/tmp/dir1/dir2/dir3/myfile.txt
mkdir -p ${fdst%/*} && cp -p ${fsrc} ${fdst}

¡Gracias! Esto es lo que necesitaba. En mi caso, no sé si el destino tiene un directorio o no y si el directorio existe o no, por lo que este código me da el directorio principal que luego puedo crear de forma segura si no existe
xbmono

4

Simplemente agregue lo siguiente en su .bashrc, modifíquelo si lo necesita. Funciona en Ubuntu

mkcp() {
    test -d "$2" || mkdir -p "$2"
    cp -r "$1" "$2"
}

Por ejemplo, si desea copiar el archivo 'prueba' al directorio de destino 'd' Use,

mkcp test a/b/c/d

mkcp primero verificará si el directorio de destino existe o no, si no, luego lo hará y copiará el archivo / directorio fuente.


Como otros comentaron sobre la otra respuesta, no necesita probar si el directorio existe antes de llamar mkdir -p. Tendrá éxito incluso si ya existe.
bfontaine

3

Esto lo hace por mi

cp -vaR ./from ./to

De acuerdo. En man cp:-R If source_file designates a directory, cp copies the directory and the entire subtree connected at that point.
borracciaBlu

3

Escribí un script de soporte para cp, llamado CP (nota mayúsculas) que está destinado a hacer exactamente esto. El script verificará si hay errores en la ruta que ha introducido (excepto el último que es el destino) y, si todo está bien, realizará un paso mkdir -p para crear la ruta de destino antes de comenzar la copia. En este punto, la utilidad cp regular se hace cargo y cualquier interruptor que use con CP (como -r, -p, -rpL se canaliza directamente a cp). Antes de usar mi script, hay algunas cosas que debe comprender.

  • Se puede acceder a toda la información aquí haciendo CP --help. CP --help-all incluye los interruptores de cp.
  • El CP regular no realizará la copia si no encuentra la ruta de destino. No tiene una red de seguridad para errores tipográficos con CP. Se creará su destino, por lo que si escribe mal su destino como / usrr / share / icons o / usr / share / icon, eso es lo que se creará.
  • cp regular tiende a modelar su comportamiento en la ruta existente: cp / a / b / c / d variará dependiendo de si d existe o no. si d es una carpeta existente, cp copiará b en ella, haciendo / c / d / b. Si d no existe, b se copiará en c y se le cambiará el nombre a d. Si d existe pero es un archivo yb es un archivo, se sobrescribirá con la copia de b. Si c no existe, cp no hace la copia y sale.

CP no tiene el lujo de tomar señales de los caminos existentes, por lo que debe tener algunos patrones de comportamiento muy firmes. CP asume que el elemento que está copiando se está colocando en la ruta de destino y no es el destino en sí (también conocido como una copia renombrada del archivo / carpeta de origen). Sentido:

  • "CP / a / b / c / d" dará como resultado / c / d / b si d es una carpeta
  • "CP / a / b / c / b" dará como resultado / c / b / b si b en / c / b es una carpeta.
  • Si tanto byd son archivos: CP / a / b / c / d dará como resultado / c / d (donde d es una copia de b). Lo mismo para CP / a / b / c / b en la misma circunstancia.

Este comportamiento de CP predeterminado se puede cambiar con el modificador "--rename". En este caso, se supone que

  • "CP --rename / a / b / c / d" está copiando b en / c y renombrando la copia a d.

Algunas notas de cierre: al igual que con cp, CP puede copiar varios elementos a la vez, asumiendo que la última ruta que figura en la lista es el destino. También puede manejar rutas con espacios siempre que utilice comillas.

CP verificará las rutas que ha introducido y se asegurará de que existan antes de hacer la copia. En modo estricto (disponible mediante el interruptor --strict), todos los archivos / carpetas que se copian deben existir o no se realiza ninguna copia. En modo relajado (--relaxado), la copia continuará si existe al menos uno de los elementos enumerados. El modo relajado es el predeterminado, puede cambiar el modo temporalmente a través de los interruptores o de forma permanente configurando la variable easy_going al comienzo del script.

Aquí se explica cómo instalarlo:

En un terminal no root, haga:

sudo echo > /usr/bin/CP; sudo chmod +x /usr/bin/CP; sudo touch /usr/bin/CP
gedit admin:///usr/bin/CP 

En gedit, pegue la utilidad CP y guarde:

#!/bin/bash
#Regular cp works with the assumption that the destination path exists and if it doesn't, it will verify that it's parent directory does.

#eg: cp /a/b /c/d will give /c/d/b if folder path /c/d already exists but will give /c/d (where d is renamed copy of b) if /c/d doesn't exists but /c does.

#CP works differently, provided that d in /c/d isn't an existing file, it assumes that you're copying item into a folder path called /c/d and will create it if it doesn't exist. so CP /a/b /c/d will always give /c/d/b unless d is an existing file. If you put the --rename switch, it will assume that you're copying into /c and renaming the singl item you're copying from b to d at the destination. Again, if /c doesn't exist, it will be created. So CP --rename /a/b /c/d will give a /c/d and if there already a folder called /c/d, contents of b will be merged into d. 

#cp+ $source $destination
#mkdir -p /foo/bar && cp myfile "$_"

err=0 # error count
i=0 #item counter, doesn't include destination (starts at 1, ex. item1, item2 etc)
m=0 #cp switch counter (starts at 1, switch 1, switch2, etc)
n=1 # argument counter (aka the arguments inputed into script, those include both switches and items, aka: $1 $2 $3 $4 $5)
count_s=0
count_i=0
easy_going=true #determines how you deal with bad pathes in your copy, true will allow copy to continue provided one of the items being copied exists, false will exit script for one bad path. this setting can also be changed via the custom switches: --strict and --not-strict
verbal="-v"


  help="===============================================================================\
    \n         CREATIVE COPY SCRIPT (CP) -- written by thebunnyrules\
    \n===============================================================================\n
    \n This script (CP, note capital letters) is intended to supplement \
    \n your system's regular cp command (note uncapped letters). \n
    \n Script's function is to check if the destination path exists \
    \n before starting the copy. If it doesn't it will be created.\n    
    \n To make this happen, CP assumes that the item you're copying is \
    \n being dropped in the destination path and is not the destination\
    \n itself (aka, a renamed copy of the source file/folder). Meaning:\n 
    \n * \"CP /a/b /c/d\" will result in /c/d/b \
    \n * even if you write \"CP /a/b /c/b\", CP will create the path /a/b, \
    \n   resulting in /c/b/b. \n
    \n Of course, if /c/b or /c/d are existing files and /a/b is also a\
    \n file, the existing destination file will simply be overwritten. \
    \n This behavior can be changed with the \"--rename\" switch. In this\
    \n case, it's assumed that \"CP --rename /a/b /c/d\" is copying b into /c  \
    \n and renaming the copy to d.\n
    \n===============================================================================\
    \n        CP specific help: Switches and their Usages \
    \n===============================================================================\n
    \
    \n  --rename\tSee above. Ignored if copying more than one item. \n
    \n  --quiet\tCP is verbose by default. This quiets it.\n
    \n  --strict\tIf one+ of your files was not found, CP exits if\
    \n\t\tyou use --rename switch with multiple items, CP \
    \n\t\texits.\n
    \n  --relaxed\tIgnores bad paths unless they're all bad but warns\
    \n\t\tyou about them. Ignores in-appropriate rename switch\
    \n\t\twithout exiting. This is default behavior. You can \
    \n\t\tmake strict the default behavior by editing the \
    \n\t\tCP script and setting: \n
    \n\t\teasy_going=false.\n
    \n  --help-all\tShows help specific to cp (in addition to CP)."

cp_hlp="\n\nRegular cp command's switches will still work when using CP.\
    \nHere is the help out of the original cp command... \
    \n\n===============================================================================\
    \n          cp specific help: \
    \n===============================================================================\n"

outro1="\n******************************************************************************\
    \n******************************************************************************\
    \n******************************************************************************\
    \n        USE THIS SCRIPT WITH CARE, TYPOS WILL GIVE YOU PROBLEMS...\
    \n******************************************************************************\
    \n******************************* HIT q TO EXIT ********************************\
    \n******************************************************************************"


#count and classify arguments that were inputed into script, output help message if needed
while true; do
    eval input="\$$n"
    in_=${input::1}

    if [ -z "$input" -a $n = 1 ]; then input="--help"; fi 

    if [ "$input" = "-h" -o "$input" = "--help" -o "$input" = "-?" -o "$input" = "--help-all" ]; then
        if [ "$input" = "--help-all" ]; then 
            echo -e "$help"$cp_hlp > /tmp/cp.hlp 
            cp --help >> /tmp/cp.hlp
            echo -e "$outro1" >> /tmp/cp.hlp
            cat /tmp/cp.hlp|less
            cat /tmp/cp.hlp
            rm /tmp/cp.hlp
        else
            echo -e "$help" "$outro1"|less
            echo -e "$help" "$outro1"
        fi
        exit
    fi

    if [ -z "$input" ]; then
        count_i=$(expr $count_i - 1 ) # remember, last item is destination and it's not included in cound
        break 
    elif [ "$in_" = "-" ]; then
        count_s=$(expr $count_s + 1 )
    else
        count_i=$(expr $count_i + 1 )
    fi
    n=$(expr $n + 1)
done

#error condition: no items to copy or no destination
    if [ $count_i -lt 0 ]; then 
            echo "Error: You haven't listed any items for copying. Exiting." # you didn't put any items for copying
    elif [ $count_i -lt 1 ]; then
            echo "Error: Copying usually involves a destination. Exiting." # you put one item and no destination
    fi

#reset the counter and grab content of arguments, aka: switches and item paths
n=1
while true; do
        eval input="\$$n" #input=$1,$2,$3,etc...
        in_=${input::1} #first letter of $input

        if [ "$in_" = "-" ]; then
            if [ "$input" = "--rename" ]; then 
                rename=true #my custom switches
            elif [ "$input" = "--strict" ]; then 
                easy_going=false #exit script if even one of the non-destinations item is not found
            elif [ "$input" = "--relaxed" ]; then 
                easy_going=true #continue script if at least one of the non-destination items is found
            elif [ "$input" = "--quiet" ]; then 
                verbal=""
            else
                #m=$(expr $m + 1);eval switch$m="$input" #input is a switch, if it's not one of the above, assume it belongs to cp.
                switch_list="$switch_list \"$input\""
            fi                                  
        elif ! [ -z "$input" ]; then #if it's not a switch and input is not empty, it's a path
                i=$(expr $i + 1)
                if [ ! -f "$input" -a ! -d "$input" -a "$i" -le "$count_i" ]; then 
                    err=$(expr $err + 1 ); error_list="$error_list\npath does not exit: \"b\""
                else
                    if [ "$i" -le "$count_i" ]; then 
                        eval item$i="$input" 
                        item_list="$item_list \"$input\""
                    else
                        destination="$input" #destination is last items entered
                    fi
                fi
        else
            i=0
            m=0
            n=1                     
            break
        fi      
        n=$(expr $n + 1)
done

#error condition: some or all item(s) being copied don't exist. easy_going: continue if at least one item exists, warn about rest, not easy_going: exit.
#echo "err=$err count_i=$count_i"
if [ "$easy_going" != true -a $err -gt 0 -a $err != $count_i ]; then 
    echo "Some of the paths you entered are incorrect. Script is running in strict mode and will therefore exit."
    echo -e "Bad Paths: $err $error_list"
    exit
fi

if [ $err = $count_i ]; then
    echo "ALL THE PATHS you have entered are incorrect! Exiting."
    echo -e "Bad Paths: $err $error_list"
fi

#one item to one destination:
#------------------------------
#assumes that destination is folder, it does't exist, it will create it. (so copying /a/b/c/d/firefox to /e/f/firefox will result in /e/f/firefox/firefox
#if -rename switch is given, will assume that the top element of destination path is the new name for the the item being given.

#multi-item to single destination:
#------------------------------
#assumes destination is a folder, gives error if it exists and it's a file. -rename switch will be ignored.

#ERROR CONDITIONS: 
# - multiple items being sent to a destination and it's a file.
# - if -rename switch was given and multiple items are being copied, rename switch will be ignored (easy_going). if not easy_going, exit.
# - rename option but source is folder, destination is file, exit.
# - rename option but source is file and destination is folder. easy_going: option ignored.

if [ -f "$destination" ]; then
    if [ $count_i -gt 1 ]; then 
        echo "Error: You've selected a single file as a destination and are copying multiple items to it. Exiting."; exit
    elif [ -d "$item1" ]; then
        echo "Error: Your destination is a file but your source is a folder. Exiting."; exit
    fi
fi
if [ "$rename" = true ]; then
    if [ $count_i -gt 1 ]; then
        if [ $easy_going = true ]; then
            echo "Warning: you choose the rename option but are copying multiple items. Ignoring Rename option. Continuing."
        else
            echo "Error: you choose the rename option but are copying multiple items. Script running in strict mode. Exiting."; exit
        fi
    elif [ -d "$destination" -a -f "$item1" ]; then
        echo -n "Warning: you choose the rename option but source is a file and destination is a folder with the same name. "
        if [ $easy_going = true ]; then
            echo "Ignoring Rename option. Continuing."
        else
            echo "Script running in strict mode. Exiting."; exit
        fi
    else
        dest_jr=$(dirname "$destination")
        if [ -d "$destination" ]; then item_list="$item1/*";fi
        mkdir -p "$dest_jr"
    fi
else
    mkdir -p "$destination"
fi

eval cp $switch_list $verbal $item_list "$destination"

cp_err="$?"
if [ "$cp_err" != 0 ]; then 
    echo -e "Something went wrong with the copy operation. \nExit Status: $cp_err"
else 
    echo "Copy operation exited with no errors."
fi

exit

Esto es quizás una exageración, pero bastante eficiente, funciona como se esperaba, gracias buen señor.
Xedret

2

cp tiene múltiples usos:

$ cp --help
Usage: cp [OPTION]... [-T] SOURCE DEST
  or:  cp [OPTION]... SOURCE... DIRECTORY
  or:  cp [OPTION]... -t DIRECTORY SOURCE...
Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.

La respuesta de @ AndyRoss funciona para el

cp SOURCE DEST

estilo de cp, pero hace lo incorrecto si usa el

cp SOURCE... DIRECTORY/

estilo de cp.

Creo que "DEST" es ambiguo sin una barra inclinada final en este uso (es decir, donde el directorio de destino aún no existe), por lo que tal vez cpnunca haya agregado una opción para esto.

Así que aquí está mi versión de esta función que impone una barra inclinada final en el directorio de destino:

cp-p() {
  last=${@: -1}

  if [[ $# -ge 2 && "$last" == */ ]] ; then
    # cp SOURCE... DEST/
    mkdir -p "$last" && cp "$@"
  else
    echo "cp-p: (copy, creating parent dirs)"
    echo "cp-p: Usage: cp-p SOURCE... DEST/"
  fi
}

2

Solo tuve el mismo problema. Mi enfoque era simplemente colocar los archivos en un archivo así:

tar cf your_archive.tar file1 /path/to/file2 path/to/even/deeper/file3

tar almacena automáticamente los archivos en la estructura adecuada dentro del archivo. Si tu corres

tar xf your_archive.tar

los archivos se extraen en la estructura de directorios deseada.


1

Como lo sugirió anteriormente help_asap y spongeman, puede usar el comando 'instalar' para copiar archivos a directorios existentes o crear crear nuevos directorios de destino si aún no existen.

La opción 1 install -D filename some/deep/directory/filename
copia el archivo en un directorio nuevo o existente y otorga permisos de nombre de archivo 755 predeterminados

Opción 2 install -D filename -m640 some/deep/directory/filename
según la Opción 1 pero otorga permisos de nombre de archivo 640.

La opción 3 install -D filename -m640 -t some/deep/directory/
según la opción 2, pero dirige el nombre del archivo al directorio de destino, por lo que el nombre del archivo no necesita escribirse tanto en el origen como en el destino.

Opción 4 install -D filena* -m640 -t some/deep/directory/
según la Opción 3 pero usa un comodín para múltiples archivos.

Funciona bien en Ubuntu y combina dos pasos (creación de directorio y copia de archivo) en un solo paso.


1

Esto es muy tarde, pero puede ayudar a un novato en alguna parte. Si necesita 'crear' automáticamente carpetas, rsync debería ser su mejor amigo. rsync / path / to / sourcefile / path / to / tragetdir / thatdoestexist /


0
rsync file /path/to/copy/file/to/is/very/deep/there

Esto podría funcionar, si tiene el tipo correcto de rsync.


Esto no funciona para mí - Obtuve "No existe tal archivo o directorio" en el directorio de destino
Rich

0

Copiar desde el origen a una ruta no existente

mkdir p /destination && cp r /source/ $_

NOTA: este comando copia todos los archivos

cp –r para copiar todas las carpetas y su contenido

$_ trabajar como destino que se crea en el último comando


0

Simple

cp -a * /path/to/dst/

debería hacer el truco.


No lo hace. El '/ ruta / a / dst /' debe existir de antemano.
Shital Shah

-1

Digamos que estás haciendo algo como

cp file1.txt A / B / C / D / file.txt

donde A / B / C / D son directorios que aún no existen

Una posible solución es la siguiente

DIR=$(dirname A/B/C/D/file.txt)
# DIR= "A/B/C/D"
mkdir -p $DIR
cp file1.txt A/B/C/D/file.txt

¡Espero que ayude!

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.