Comments 63
И тут мы столкнёмся с неоднозначностью поведения cp. Если папки /target не существует, то мы получим то, что нам нужно.
Однако, если папка target существует, то файлы будут скопированы в папку /source/target.
А не /target/source?
Но технические ошибки, влияющие на результат описываемой процедуры или на понимание процесса, должны корректироваться публично.
Это логично для случая, когда вы пропустите поправку в личке, у читающего статью останется шанс дополнить картину чтением комментов.
Мне кажется вместо этого лучше использовать опцию -T
:
cp -a -T /source /target
-T, --no-target-directory
treat DEST as a normal file
В некоторых случаях ещё полезен флаг -t
, только тогда source
и target
меняются местами:
SYNOPSIS
cp [OPTION]... [-T] SOURCE DEST
cp [OPTION]... SOURCE... DIRECTORY
cp [OPTION]... -t DIRECTORY SOURCE...
Это меньше похоже на хак, более явно указывается на то, что DEST
это именно имя в которое нужно копировать. Плюс у некоторых файловых систем может не быть директорий ..
и .
Когда-то натыкался на инфу, что некоторые файловые системы, могут не иметь этих хардлинков (помню в пример приводились фс для оптических дисков типа UDF) и, вроде, были опции для монтирования с их эмуляцией.
Вполне возможно, что это уже пережиток прошлого и такого теперь не бывает.
Вполне возможно, что это уже пережиток прошлого и такого теперь не бывает.Не бывает. В этом легко убедиться.
$ dd if=/dev/zero of=tmpfile bs=1M count=1 1+0 records in 1+0 records out 1048576 bytes (1.0 MB, 1.0 MiB) copied, 0.00239023 s, 439 MB/s $ mkfs.fat tmpfile mkfs.fat 4.1 (2017-01-24) $ mmd -i tmpfile test $ mdir -i tmpfile test Volume in drive : has no label Volume Serial Number is D0A1-1DD1 Directory for ::/test . <DIR> 2019-10-14 14:41 .. <DIR> 2019-10-14 14:41 2 files 0 bytes 1 026 048 bytes free $ mkdir tmpdir $ sudo mount -o loop tmpfile tmpdir $ ls -al tmpdir/test/ total 18 drwxr-xr-x 2 root root 2048 Oct 14 2019 . drwxr-xr-x 3 root root 16384 Jan 1 1970 ..
Как легко заметить информация про
..
— разная для mdir
и ls
. Почему? Потому что ядро игнорирует .
и ..
, которые могут существовать (а могут и не существовать) на диске. Вместо этого .
и ..
эмулируются внутри ядра.Так что в Linux вы никогда не увидите файловых систем без
.
. В Windows — да, возможно.В каком смысле "в Windows — да, возможно"? Разве .
и ..
не точно так же эмулируются?
В Linux это делается на уровне VFS (и всегда делалось на уровне VFS) и до драйвера дело просто не доходит…
Мне не удалось найти точной информации содержится ли запись ..
в директории NTFS, но думаю что вряд ли — это слишком расточительно.
В NTFS, в отличии от других систем, первичным хранилищем информации о файлах являются не записи в директории, а записи в MFT. Содержимое директорий же — лишь B-tree индекс, как в базах данных. И у каждого файла есть по атрибуту $FILE_NAME на каждую директорию, в которой тот находится.
Если бы в директориях были записи ..
— это бы означало, что у каждой директории есть столько атрибутов $FILE_NAME, сколько у неё субдиректорий. А поскольку все атрибуты хранятся в плоском массиве — это бы убило всю идею B-tree индексов.
Так что, если только NTFS делали не полные идиоты, физически ..
как запись директории там точно не хранится.
А вот через API эта запись ещё как возвращается, так что...
А вот через API эта запись ещё как возвращается, так что...Совешенно не «так что». Кроме FAT и NTFS есть ведь всякие ISO 9660, UFS и прочие всякие BTRFS. И вот вопрос: всегда ли они эмулируют
.
и ..
— или это от драйвера зависит?По логике-то должно быть как в Linux:
.
и ..
эмулируются VFS, частью ядра, до драйвера дело не доходит в принципе… но я видел много мест в Windows, где есть подобные layering violations, так что ответить на этот вопрос не могу.И даже в вашем случае вы отлично можете сделать что-нибудь типа
mkdir ./figvam
— и файлик отлично создастся. Более того, даже если вы удалите ..
— вы всё равно сможете сделать ls ..
Но да, с коррапнутыми файловыми системами возможны чудеса… вплодо до kernel oops… Неопределённое поведение — оно такое.
Для cp
так действительно короче, но бывают программы где склеивание опций приводит к склеиванию ласт неожиданным результатам.
Так вроде стандарт же, однобуквенные опции перечисляются после одного минуса без пробелов, а перед многобуквенными опциями ставят 2 минуса.
Это скорее не стандарт, а обычай, и далеко не все ему следуют (взять хотя бы firefox с его многобуквенными опциями с одним дефисом). И даже по этому стандартному обычаю есть опции, которым требуется аргумент, и не дай божа случайно засунуть другую однобуквенную опцию между многобуквенной и её аргументом (чтобы уточнить поведение, например: cp -aTi /source /target
).
Ну и некоторые программы могут вообще не распознать склеенные аргументы, даже если они однобуквенные безаргументные, ибо разработчику интересно прогать, а не аргументы ваши клееные парсить.
Это ваш странный обычай называется POSIX. https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html, частью которого является команда cp
https://pubs.opengroup.org/onlinepubs/9699919799/utilities/cp.html .
[pfemidi@pfemidi ~]$ java -version
openjdk version "1.8.0_232"
OpenJDK Runtime Environment (build 1.8.0_232-b09)
OpenJDK 64-Bit Server VM (build 25.232-b09, mixed mode)
[pfemidi@pfemidi ~]$ java --version
Unrecognized option: --version
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.
[pfemidi@pfemidi ~]$
> cp /a /b
> cp /a/ /b/
> cp /a/* /b
Как правильно скопировать все значимое содержимое одной папки в другую, при этом находясь в третьей?
Между первой и второй строкой разницы нет.
А вот в третьей мы приплетаем shell, и если в папке нет файлов или есть начинающиеся с точки, то копирование будет произведено не полностью или с ошибкой.
Между первой и второй строкой разницы нет.
В BSD Coreutils (как минимум в FreeBSD и OS X) это работает несколько не так: / после первого аргумента копирует не директорию, а ее содержимое
$ mkdir -p a/b/c d
$ cp -r a/ d && ls d
b
$ cp -r a d && ls d
a b
А в Linux (GNU coreutils) именно так, как сказал я:
mkdir -p a/b/c d
cp -r a/ d && ls d
a
cp -r a d && ls d
a
Установка coreutils на Мак.
Первый и второй варианты отличаются в случае, если a — символьная ссылка.
Тогда первая команда скопирует ссылку, а вторая — будет копировать файлы.
А для вашего вопроса есть простой ответ. Нужно использовать cp так:
rsync -a source_dir target_dir
Попутно можно использовать параметр --progress, если хочется видеть проценты выполнения задачи.
В этой статье речь именно о cp.
Вообще-то я хочу в следующих статьях затронуть и ваши вопросы.
Просто это слишком много для одной статьи.
А надо так:
rsync -a source_dir/ target_dir
И то есть риск потерять разные атрибуты или хардлинки. Чтоб совсем ничего не потерять
rsync -aHAX
Это поведение самого rsync'a, который прямо ему говорит — копируй содержимое директории, а не включай тут вангу с определением "а существует ли в точке назначения такая-то директория, если да то тудааа, если нет то создаёёёём"...
Эдакий source/.
, упомянутый в начале статьи, только чуть удобнее.
Вместо cp можно использовать tar. Как-то так
tar cf — . | tar xvf — -C /dest
Рекомендую вообще нафиг отказаться от использования вайлдкардов для подстановок в автоматических скриптах. В простейшем случае в имени может оказаться просто пробел и это порвет аргументы и приведет к неожиданному поведению. В более сложном примере имя файла может быть коротким и начинаться с минуса и команда интерпретирует имя как параметры выполнения.
Вот простой пример, когда имя файла интерпретируется как опция.
$ mkdir test
$ cd test
$ echo 123 > --help
$ cp * 124
Usage: cp [OPTION]… [-T] SOURCE DEST
Потыкал в разные дистрибутивы через докер и реально везде работает.
Вот пример как тыкал
sudo docker run ubuntu:18.04 bash -l -c 'touch "myfile1 1"; stat myfile*'
Спасибо
$ touch 'a a'
$ for file in *; do stat $file ; done
stat: cannot stat 'a': No such file or directory
stat: cannot stat 'a': No such file or directory
$ for file in *; do stat "$file" ; done
File: a a
Size: 0 Blocks: 16 IO Block: 4096 regular empty file
Device: 35h/53d Inode: 13125328 Links: 1
И дело не только в шелле, а в том, что куча софта по разному видит проблему имени файла.
Кубунту и Манджаро.
<зануда>
В файловой системе директории, а не папки.
<\зануда>
cp -dpRx /source /target
cp -ax /source /target
За исключение того, что ваша версия не сохраняет расширенные аттрибуты типа: context, links, xattr, all. А также неоднозначно ведёт себя в зависимости от существования /target.
Для однозначности нужно:
cp -ax /source/. /target
Команда cp: правильное копирование папок с файлами в *nix