Linux: скопируйте и создайте каталог назначения, если он не существует - PullRequest
265 голосов
/ 07 октября 2009

Мне нужна команда (или, возможно, опция cp), которая создает каталог назначения, если он не существует.

Пример:

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

Ответы [ 19 ]

238 голосов
/ 07 октября 2009
mkdir -p "$d" && cp file "$d"

(такой опции нет для cp).

204 голосов
/ 04 января 2012

Если выполняются оба следующих условия:

  1. Вы используете версию GNU cp (а не, например, версию Mac) и
  2. Вы копируете из какой-либо существующей структуры каталогов, и вам просто нужно создать ее заново

тогда вы можете сделать это с флагом --parents cp. Со страницы информации (доступно для просмотра http://www.gnu.org/software/coreutils/manual/html_node/cp-invocation.html#cp-invocation или info cp или 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.

Пример: * * один тысяча двадцать-одна

/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
42 голосов
/ 16 сентября 2015

Короткий ответ

Чтобы скопировать myfile.txt в /foo/bar/myfile.txt, используйте:

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

Как это работает?

В этом есть несколько компонентов, поэтому я расскажу о синтаксисе шаг за шагом.

Утилита mkdir , , как указано в стандарте POSIX , создает каталоги. Аргумент -p, согласно документам, приведет к mkdir до

Создание любых отсутствующих компонентов промежуточного пути

означает, что при вызове mkdir -p /foo/bar, mkdir создаст /foo и /foo/bar, если /foo еще не существует. (Без -p он выдаст ошибку.

Оператор списка &&, как описано в стандарте POSIX (или, если хотите, Руководство по Bash ), приводит к тому, что cp myfile.txt $_ выполняется только если mkdir -p /foo/bar успешно выполняется. Это означает, что команда cp не будет пытаться выполнить, если mkdir завершится неудачно для , это одна из многих причин, по которой она может завершиться неудачей .

Наконец, $_, который мы передаем в качестве второго аргумента cp, является «специальным параметром», который может быть полезен для избежания повторения длинных аргументов (например, путей к файлам) без необходимости сохранять их в переменной. В Баш ручной , это:

расширяется до последнего аргумента предыдущей команды

В данном случае это /foo/bar, которое мы передали mkdir. Таким образом, команда cp расширяется до cp myfile.txt /foo/bar, что копирует myfile.txt во вновь созданный каталог /foo/bar.

Обратите внимание, что $_ является , а не частью стандарта POSIX , поэтому теоретически вариант Unix может иметь оболочку, которая не поддерживает эту конструкцию. Тем не менее, я не знаю никаких современных оболочек, которые не поддерживают $_; конечно, Bash, Dash и Zsh все делают.


Последнее замечание: команда, которую я дал в начале этого ответа, предполагает, что в именах ваших каталогов нет пробелов. Если вы имеете дело с именами с пробелами, вам нужно заключить их в кавычки так, чтобы разные слова не рассматриваются как разные аргументы mkdir или cp. Таким образом, ваша команда будет выглядеть так:

mkdir -p "/my directory/name with/spaces" && cp "my filename with spaces.txt" "$_"
33 голосов
/ 07 апреля 2015

Такой старый вопрос, но, возможно, я могу предложить альтернативное решение.

Вы можете использовать программу install, чтобы скопировать ваш файл и создать целевой путь "на лету".

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

Однако следует принять во внимание некоторые аспекты:

  1. вам необходимо указать также имя файла назначения , а не только путь назначения
  2. файл назначения будет исполняемым (по крайней мере, насколько я видел из моих тестов)

Вы можете легко изменить # 2, добавив параметр -m, чтобы установить разрешения для файла назначения (например: -m 664 создаст файл назначения с разрешениями rw-rw-r--, так же, как создание нового файла с touch).


И вот это бесстыдная ссылка на ответ, который меня вдохновил =)

15 голосов
/ 08 октября 2009

Функция оболочки, которая делает то, что вы хотите, называя ее «скрытой» копией, потому что она выкапывает дыру в файле для жизни:

bury_copy() { mkdir -p `dirname $2` && cp "$1" "$2"; }
7 голосов
/ 09 февраля 2014

Вот один из способов сделать это:

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

dirname даст вам родителя каталога или файла назначения. Затем mkdir -p `dirname ...` создаст этот каталог, гарантируя, что при вызове cp -r будет создан правильный базовый каталог.

Преимущество этого родительского элемента в том, что он работает в случае, когда последний элемент в пути назначения является именем файла.

И это будет работать на OS X.

5 голосов
/ 28 октября 2016

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

2 голосов
/ 03 февраля 2018

Это делает это для меня

cp -vaR ./from ./to
2 голосов
/ 21 августа 2018

Просто чтобы возобновить и дать полное рабочее решение, в одну строку. Будьте осторожны, если вы хотите переименовать ваш файл, вы должны включить способ предоставить чистый путь к каталогу mkdir. $ fdst может быть файлом или каталогом. Следующий код должен работать в любом случае.

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

или специфичный для bash

fsrc=/tmp/myfile.unk
fdst=/tmp/dir1/dir2/dir3/myfile.txt
mkdir -p ${fdst%/*} && cp -p ${fsrc} ${fdst}
2 голосов
/ 22 сентября 2017

при всем моем уважении к ответам выше, я предпочитаю использовать rsync следующим образом:

$  rsync -a directory_name /path_where_to_inject_your_directory/

пример:

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