Linux: copie e crie o diretório de destino, se ele não existir

347

Eu quero um comando (ou provavelmente uma opção para cp) que crie o diretório de destino se ele não existir.

Exemplo:

cp -? file /path/to/copy/file/to/is/very/deep/there
flybywire
fonte
5
A resposta aceita está errada (existe uma opção para cp). Veja a segunda resposta de Paul Whipp.
Ronenz
11
@ Ronenz, eu concordo que existe uma opção que vale a pena mencionar no GNU cp(conforme respondida por Paul Whipp), mas não é tão geral quanto o OP solicitou. Pode copiar a/foo/bar/filepara, b/foo/bar/filemas não pode copiar filepara b/foo/bar/file. (ou seja, ele só funciona para criar diretórios pai que já existiam no caso do primeiro arquivo, mas não pode criar diretórios arbitrários)
Sudo Bash
Parece um bom pedido de recurso para cp. Alguma chance de conseguirmos essa opção de linha de comando em um futuro próximo?
Arnaud

Respostas:

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

(não existe essa opção para cp).

Michael Krelin - hacker
fonte
62
Eu não acho que você precise do test -d: mkdir -painda é bem-sucedido, mesmo que o diretório já exista.
519 ephemient
oops, certo. Mas, em seguida, teste pode ser um builtin bash (especialmente se escrito como [[-d "$ d"]]) e mkdir não pode ;-)
Michael Krelin - hacker de
11
mkdiré um builtin no BusyBox;)
ephemient 7/10/09
7
@holms, $dé uma variável que presumivelmente contém o diretório em questão. Quero dizer, se você precisar repeti-lo três vezes, provavelmente atribuirá a variável primeiro.
Michael Krelin - hacker de
237

Se os seguintes itens forem verdadeiros:

  1. Você está usando a versão GNU cp(e não, por exemplo, a versão Mac) e
  2. Você está copiando de alguma estrutura de diretório existente e só precisa dela recriada

então você pode fazer isso com a --parentsbandeira de cp. Na página de informações (visível em http://www.gnu.org/software/coreutils/manual/html_node/cp-invocation.html#cp-invocation ou com info cpou 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.

Exemplo:

/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
Paul Whipp
fonte
4
Isso não funciona no Mac OS X, então acho que é específico do Linux.
OLT
7
Eu diria que é específico para o GNU. Se você possui, macportspode instalar o coreutils e seus gcp.
Michael Krelin - hacker de
16
Man, o Linux tem tudo
Ziggy
19
Eu sinto que esta é a resposta para uma pergunta um pouco diferente, mesmo que seja a resposta que eu estava procurando (e, portanto, a que eu votei). Eu acho que esta é a solução supondo que você quer os diretórios de destino pais para ser o mesmo que os diretórios origem pai, que é provavelmente o caso de uso que a maioria das pessoas lendo isso é interessante em.
David Winiecki
7
Isso pressupõe que o diretório 'bar' já exista, mas a pergunta original implica em como criar automaticamente 'bar' quando ele é fornecido como destino e ele ainda não existe. A resposta para isso é fornecida por @ MichaelKrelin-hacker.
Thdan
69

Resposta curta

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

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

Como é que isso funciona?

Existem alguns componentes para isso, portanto, abordarei toda a sintaxe passo a passo.

O utilitário mkdir , conforme especificado no padrão POSIX , cria diretórios. O -pargumento, per os docs, fará com que mkdir para

Crie qualquer componente de caminho intermediário ausente

o que significa que, ao chamar mkdir -p /foo/bar, o mkdir criará /foo e /foo/bar se /fooainda não existir. (Sem -p, ele lançará um erro.

O &&operador de lista, conforme documentado no padrão POSIX (ou no manual do Bash, se você preferir), tem o efeito de cp myfile.txt $_ser executado apenas se for mkdir -p /foo/barexecutado com êxito. Isso significa que o cpcomando não tentará executar se mkdirfalhar por um dos muitos motivos pelos quais pode falhar .

Finalmente, $_passamos como o segundo argumento para cpum "parâmetro especial" que pode ser útil para evitar repetir argumentos longos (como caminhos de arquivo) sem precisar armazená-los em uma variável. De acordo com o manual do Bash , ele:

expande para o último argumento para o comando anterior

Nesse caso, é para /foo/baronde passamos mkdir. Portanto, o cpcomando se expande para cp myfile.txt /foo/bar, que copia myfile.txtpara o /foo/bardiretório recém-criado .

Note-se que $_é não faz parte do padrão POSIX , então, teoricamente, uma variante Unix pode ter um shell que não suporta esta construção. No entanto, não conheço nenhuma concha moderna que não suporte $_; certamente Bash, Dash e zsh todos fazem.


Uma observação final: o comando que eu dei no início desta resposta assume que os nomes dos diretórios não possuem espaços. Se você estiver lidando com nomes com espaços, precisará citá-los para que as diferentes palavras não são tratados como argumentos diferentes para mkdirou cp. Portanto, seu comando seria realmente parecido com:

mkdir -p "/my directory/name with/spaces" && cp "my filename with spaces.txt" "$_"
Mark Amery
fonte
16
Além disso: geralmente estou decepcionado com a falta de detalhes nas perguntas sobre os comandos do shell Bash ou Unix no Stack Overflow, que frequentemente lançam uma linha complicada e extremamente densa, difícil de escolher se você ainda não é um assistente do Unix. Tentei ir ao extremo oposto aqui e ambos explicam todos os detalhes da sintaxe e vinculam-nos aos locais apropriados para encontrar documentação sobre como essas coisas funcionam. Espero que isso ajude pelo menos algumas pessoas. Além disso, o crédito onde devido: Eu peguei essa abordagem a partir desta resposta a uma pergunta duplicado: stackoverflow.com/a/14085147/1709587
Mark Amery
Eu nunca vi $ _ antes. a que se refere? a pasta usada no último comando?
thebunnyrules
9
@thebunnyrules Mas ... mas ... existe um "Como isso funciona?" seção na minha resposta que literalmente contém vários parágrafos especificamente dedicados à pergunta que você acabou de fazer. Parece ignorado e não amado. :(
Mark Amery
Me desculpe por isso. Você está absolutamente correto. Eu já sabia tudo o mais na sua resposta, então apenas a examinei rapidamente. Eu deveria ter lido com mais cuidado.
thebunnyrules
Uau, você realmente trabalhou muito nessa resposta ... Obrigado por isso. Foi bom aprender sobre: ​​$ _. Eu posso me ver usando um pouco a partir de agora. Eu escrevi um script para automatizar todo o processo e colocar neste tópico. Dê uma chance, talvez você goste: stackoverflow.com/questions/1529946/…
thebunnyrules
39

Uma pergunta tão antiga, mas talvez eu possa propor uma solução alternativa.

Você pode usar o installprograma para copiar seu arquivo e criar o caminho de destino "on the fly".

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

Existem alguns aspectos a serem considerados, no entanto:

  1. você precisa especificar também o nome do arquivo de destino , não apenas o caminho de destino
  2. o arquivo de destino será executável (pelo menos, tanto quanto vi nos meus testes)

Você pode alterar facilmente o nº 2 adicionando a -mopção de definir permissões no arquivo de destino (exemplo: -m 664criará o arquivo de destino com permissões rw-rw-r--, assim como criar um novo arquivo com touch).


E aqui está o link vergonhoso para a resposta pela qual me inspirei =)

help_asap
fonte
Realmente não funcionou para o meu uso, mas truque legal! Eu vou ter em mente.
thebunnyrules
observe que este comando cria os arquivos de destino com permissões 755( rwxr-xr-x), o que provavelmente não é desejado. você pode especificar outra coisa com a -mopção, mas não consegui encontrar uma maneira de manter apenas as permissões de arquivo :(
törzsmókus
19

Função Shell que faz o que você deseja, chamando-a de cópia "enterrada" porque abre um buraco para o arquivo residir:

bury_copy() { mkdir -p `dirname $2` && cp "$1" "$2"; }
Andy Ross
fonte
11
Você deve ter aspas em torno do dirname $2também
Tarnay Kálmán
4
@ Kalmi, para uma cotação adequada, você também gostaria de citar $2como argumento dirname. Like mkdir -p "$(dirname "$2")". Sem essa citação $2em cpé inútil;) #
22413 Michael Krelin - hacker 13/03
3
Note-se que esta resposta assume $ 2 já não é o diretório de destino, caso contrário, você só quer "mkdir -p '$ 2' && cp '$ 1' '$ 2' como o corpo da função
user467257
Eu acho que isso tem um bug, como @ user467257 aponta. Eu não acho que isso possa ser corrigido, pois $ 2 é ambíguo entre o diretório de destino e o arquivo de destino nesse cenário. Eu postei uma resposta alternativa que aborda isso.
Rich
@ Rich, eu discordo que isso não é corrigível. O seguinte funciona bem para arquivos e diretórios: mkdir -p dirname "$2"&& cp -r "$ 1" "$ 2"; Observe que os backticks ao redor dirname $2não aparecem porque o SO os analisa como marcadores de código.
Yury
11

Aqui está uma maneira de fazer isso:

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

dirnamelhe dará o pai do diretório ou arquivo de destino. mkdir -p `dirname ...` criará esse diretório, garantindo que, quando você chamar cp -r, o diretório base correto esteja no lugar.

A vantagem desse sobre-pai é que ele funciona no caso em que o último elemento no caminho de destino é um nome de arquivo.

E funcionará no OS X.

Jamie McCrindle
fonte
6

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

Spongman
fonte
11
na verdade, não é o mesmo. Isso exige que você passe o nome do arquivo duas vezes (daí o sinalizador '-t') e defina os bits de execução no arquivo (daí o exemplo '-m'). a resposta dele não deve ser a principal, pois na verdade não responde à pergunta - enquanto a minha responde.
Spongman
11
Isso funciona para um único arquivo. Basta levar em consideração que thereé o arquivo final e não o subdiretório final.
precisa saber é o seguinte
6

com todo o meu respeito pelas respostas acima, prefiro usar o rsync da seguinte maneira:

$  rsync -a directory_name /path_where_to_inject_your_directory/

exemplo:

$ rsync -a test /usr/local/lib/
marcdahan
fonte
Eu tentei e obtive este resultado: rsync -a bull / some / hoop / ti / doop / ploop RESULTADO rsync: mkdir "/ some / hoop / ti / doop / ploop" falhou: Não há erro de rsync nesse arquivo ou diretório (2) : erro no arquivo IO (código 11) em main.c (675) [Receiver = 3.1.2] #
thebunnyrules
@thebunnyrules, você precisa verificar o caminho com o comando pwd (veja: [link] ( access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/… )) e insira-o corretamente em suas instruções
marcdahan
Se entendi sua sugestão corretamente, você está propondo que eu especifique um caminho completo para a fonte, também conhecido como use: "$ (pwd) / bull" no meu comando, em vez de apenas o nome do arquivo: bull? O problema é que o rsync cria o diretório de destino, não a fonte (bull). Parece-me que o rsync usa um mkdir simples e não um mkdir -p.
precisa saber é o seguinte
11
Grande utilidade, a propósito, posso começar a usar nos meus scripts. Obrigado por recomendar.
Thebunnyrules #
11
rsync é mais previsível que cp. Por exemplo, se eu desejar cp /from/somedir /to-dir, o cp se comportará de maneira diferente, se /to-dirjá existir: Se /to-direxistir, cpcriará /to-dir/some-dir; caso contrário, apenas o conteúdo de /from/somedirserá colocado /to-dir. No entanto, rsyncse comporta da mesma no caso, ou seja, /to-dir/some-dirvai sempre ser criado.
JoeAC 31/01
5

Apenas para retomar e fornecer uma solução de trabalho completa, em uma linha. Cuidado para renomear seu arquivo, você deve incluir uma maneira de fornecer um caminho de diretório limpo para o mkdir. $ fdst pode ser arquivo ou dir. O próximo código deve funcionar em qualquer caso.

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

ou específico do bash

fsrc=/tmp/myfile.unk
fdst=/tmp/dir1/dir2/dir3/myfile.txt
mkdir -p ${fdst%/*} && cp -p ${fsrc} ${fdst}
Kaalahaan
fonte
Obrigado! Era disso que eu precisava. No meu caso, não sei se o destino tem diretório ou não e se o diretório existe ou não, então esse código me fornece o diretório pai, que então eu posso criá-lo com segurança se ele não existir
xbmono
4

Basta adicionar o seguinte no seu .bashrc, ajustar se necessário. Funciona no Ubuntu.

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

Por exemplo, se você deseja copiar o arquivo 'test' para o diretório de destino 'd' Use,

mkcp test a/b/c/d

O mkcp primeiro verificará se o diretório de destino existe ou não; se não, então o cria e copia o arquivo / diretório de origem.

SD.
fonte
Como outros comentaram sobre a outra resposta, você não precisa testar se o diretório existe antes de ligar mkdir -p. Será bem-sucedido mesmo se já existir.
precisa saber é o seguinte
3

Isso faz isso por mim

cp -vaR ./from ./to
Leroy Dunn
fonte
Aceita. Em man cp:-R If source_file designates a directory, cp copies the directory and the entire subtree connected at that point.
borracciaBlu
3

Eu escrevi um script de suporte para o cp, chamado CP (observe letras maiúsculas), destinado a fazer exatamente isso. O script verificará se há erros no caminho que você inseriu (exceto o último que é o destino) e, se tudo estiver bem, ele executará uma etapa mkdir -p para criar o caminho de destino antes de iniciar a cópia. Nesse ponto, o utilitário cp regular assume o controle e todos os switches usados ​​com o CP (como -r, -p, -rpL são canalizados diretamente para o cp). Antes de usar meu script, há algumas coisas que você precisa entender.

  • todas as informações aqui podem ser acessadas através do CP --help. CP --help-all include opções de cp.
  • O cp regular não fará a cópia se não encontrar o caminho de destino. Você não possui uma rede de segurança para erros de digitação no CP. Seu destino será criado; portanto, se você digitar incorretamente o seu destino como / usrr / share / icons ou / usr / share / icon, é isso que será criado.
  • cp regular tende a modelar seu comportamento no caminho existente: cp / a / b / c / d variará se d existe ou não. se d for uma pasta existente, o cp copiará b para ela, criando / c / d / b. Se d não existir, b será copiado para c e renomeado para d. Se d existir mas for um arquivo eb for um arquivo, ele será substituído pela cópia de b. Se c não existir, o cp não fará a cópia e sai.

A CP não se dá ao luxo de buscar pistas de caminhos existentes, por isso precisa ter alguns padrões de comportamento muito firmes. O CP assume que o item que você está copiando está sendo descartado no caminho de destino e não é o próprio destino (também conhecido como cópia renomeada do arquivo / pasta de origem). Significado:

  • "CP / a / b / c / d" resultará em / c / d / b se d for uma pasta
  • "CP / a / b / c / b" resultará em / c / b / b se b in / c / b for uma pasta.
  • Se b e d forem arquivos: CP / a / b / c / d resultará em / c / d (onde d é uma cópia de b). O mesmo para CP / a / b / c / b na mesma circunstância.

Esse comportamento padrão do CP pode ser alterado com a opção "--rename". Nesse caso, supõe-se que

  • "CP --rename / a / b / c / d" está copiando b em / c e renomeando a cópia para d.

Algumas notas finais: Como no cp, o CP pode copiar vários itens por vez, com o último caminho listado assumindo ser o destino. Ele também pode manipular caminhos com espaços, desde que você use aspas.

O CP verificará os caminhos inseridos e garantirá que eles existam antes de fazer a cópia. No modo estrito (disponível através da opção --strict), todos os arquivos / pastas que estão sendo copiados devem existir ou nenhuma cópia ocorre. No modo relaxado (- relaxado), a cópia continuará se pelo menos um dos itens listados existir. O modo relaxado é o padrão. Você pode alterar o modo temporariamente através dos comutadores ou permanentemente, definindo a variável easy_ going no início do script.

Veja como instalá-lo:

Em um terminal não raiz, faça:

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

No gedit, cole o utilitário CP e salve:

#!/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
thebunnyrules
fonte
Talvez isso seja um exagero, mas bastante eficiente, funciona como esperado, obrigado, bom senhor.
Xedret 29/03/19
2

cp tem vários 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.

A resposta de @ AndyRoss funciona para o

cp SOURCE DEST

estilo de cp, mas faz a coisa errada se você usar o

cp SOURCE... DIRECTORY/

estilo de cp.

Eu acho que "DEST" é ambíguo sem uma barra final nesse uso (ou seja, onde o diretório de destino ainda não existe), e talvez seja por isso cpque nunca foi adicionada uma opção para isso.

Então, aqui está a minha versão dessa função que aplica uma barra final no diretório 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
}
Rico
fonte
2

Só tive o mesmo problema. Minha abordagem foi apenas tar os arquivos em um arquivo como este:

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

O tar armazena automaticamente os arquivos na estrutura apropriada dentro do arquivo morto. Se você correr

tar xf your_archive.tar

os arquivos são extraídos na estrutura de diretórios desejada.

Chris
fonte
1

Como sugerido acima por help_asap e spongeman, você pode usar o comando 'install' para copiar arquivos para diretórios existentes ou criar novos diretórios de destino, se eles ainda não existirem.

A opção 1 install -D filename some/deep/directory/filename
copia o arquivo para um diretório novo ou existente e concede permissões 755 padrão ao nome do arquivo

opção 2 install -D filename -m640 some/deep/directory/filename
conforme a Opção 1, mas fornece permissões de nome de arquivo 640.

A opção 3, install -D filename -m640 -t some/deep/directory/
conforme a opção 2, mas direciona o nome do arquivo para o diretório de destino, para que o nome do arquivo não precise ser gravado na origem e no destino.

Opção 4, install -D filena* -m640 -t some/deep/directory/
conforme a Opção 3, mas usa um curinga para vários arquivos.

Ele funciona bem no Ubuntu e combina duas etapas (criação de diretório e cópia de arquivo) em uma única etapa.

Escavação
fonte
1

É muito tarde, mas pode ajudar um novato em algum lugar. Se você precisar criar 'auto' pastas, o rsync deve ser seu melhor amigo. rsync / path / to / sourcefile / path / to / tragetdir / thatdoestexist /

immanski njoro
fonte
0
rsync file /path/to/copy/file/to/is/very/deep/there

Isso pode funcionar, se você tiver o tipo certo de rsync.

danuker
fonte
Isso não funciona para mim - eu tenho "Não existe esse arquivo ou diretório" no diretório de destino
Rich
0

Copiar da origem para um caminho inexistente

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

NOTA: este comando copia todos os arquivos

cp –r para copiar todas as pastas e seu conteúdo

$_ trabalhar como destino que é criado no último comando

developerprashant
fonte
0

Simples

cp -a * /path/to/dst/

deve fazer o truque.

Brad D.
fonte
Não faz. O '/ path / to / dst /' deve existir previamente.
Shital Shah
-1

Digamos que você esteja fazendo algo como

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

onde A / B / C / D são diretórios que ainda não existem

Uma solução possível é a seguinte

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 ajude!

sdinesh94
fonte