46

Мой вопрос: почему при создании копии каталога необходимо использовать флаг -r (рекурсивный)? Т.е. зачем это делать:

$ cp -r dir1 copyDir1

Когда бы я не хотел такого поведения при копировании каталога?

Разве рекурсивная копия каталога не является поведением по умолчанию; поведение мы хотим почти все время?

Такое ощущение, что это лишний флаг.

8 ответов8

58

Как работают файловые системы, каталог - это на самом деле не папка, содержащая файлы, а каталог, который представляет собой файл, содержащий указатели inode на «дочерние» файлы, связанные с ним. Это означает, что с точки зрения файловой системы файл - это файл, а каталог - это просто файл, содержащий список подключенных файлов.

Итак, с точки зрения командной строки, делая это:

$ cp dir1 copyDir1

В основном это означало бы копирование файла с именем dir1 в новый файл с именем copyDir1 . А что касается файловой системы, то dir1 в любом случае является просто файлом; тот факт, что это «каталог», будет очевиден только тогда, когда файловая система на самом деле проверяет dir1 чтобы увидеть, какова эта куча битов.

Флаг -r указывает файловой системе рекурсивно свернуть дерево файлов / каталогов и скопировать любое и все содержимое, которое может быть «потомком» этого файла, в новое место.

Теперь о том, почему это может показаться излишним или избыточным, это действительно сводится к историческим методам работы с файловыми системами. А также создание системы, которая защищена от всех типов ошибок, связанных с пользователем; случайный, а также преднамеренный.

То есть, допустим, у вас есть файл ~/bin в вашем домашнем каталоге, который вы хотите скопировать, но случайно пропустили ~ - потому что вы человек и делаете ошибки - так что просто /bin вот так:

cp /bin/ ~/copy_of_bin

Поскольку «сеть безопасности» /bin является каталогом в сочетании с необходимостью флага -r вы избежите случайного копирования всего двоичного корня системы, в которой вы находитесь, в ваш домашний каталог. Если бы эта сеть безопасности не существовала, произошла бы небольшая или, возможно, крупная катастрофа.

Логика здесь заключается в том, что в дни, предшествующие GUI (графические пользовательские интерфейсы), необходимо устанавливать логические / поведенческие соглашения, чтобы избежать создания пользователем ошибок, которые могут потенциально убить систему. И использование флага -r теперь является одним из них.

Если это кажется излишним, то не нужно смотреть дальше, чем современная система графического интерфейса, которую можно поместить над файловыми системами Linux. Графический интерфейс пользователя решает основные пользовательские проблемы, подобные этой, позволяя легко перетаскивать файлы и каталоги.

Но в случае текстовых интерфейсов большая часть «пользовательского опыта» в этом мире - это, в основном, просто логические и основанные на практических условиях дорожные неровности, которые помогают держать пользователя под контролем, чтобы предотвратить потенциальную катастрофу.

Точно так же именно поэтому файловые системы Linux/Unix не имеют разрешений 777 и прав sudo установленных по умолчанию, и то, как настоящие системные администраторы вздрагивают, когда пользователь устанавливает разрешения 777 или предоставляет всем права sudo . Это основные вещи, которые нужно сделать, чтобы система была стабильной и как можно более «пользовательской»; Любой, кто спешит замкнуть эти соглашения, скорее всего, нанесет ущерб их системе, даже не подозревая об этом.

ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ: Другой ответ здесь, на сайте Unix Stack Exchange, дает хорошее объяснение того, почему нерекурсивная копия каталога является проблематичной; акцент мой.

Что ж, без флага -R возможно только копирование файлов, потому что довольно необычно, что кто-то хочет нерекурсивно копировать каталог: нерекурсивная копия просто привела бы ко второму имени для каталога, указывая непосредственно на та же структура каталогов. Поскольку это редко то, что люди хотят, и на самом деле существует отдельная программа, которая делает это (ln), нерекурсивная копия каталогов не допускается.

Таким образом, если каталог - это просто файл с элементами inode, создание прямой копии этого файла будет эквивалентно тому, как будет работать жесткая ссылка. Что не то, что кто-то хочет.

19

Это правда, что такое поведение мы хотим почти все время. Однако это не обязательно означает, что рекурсивное копирование должно быть поведением по умолчанию.

Я думаю, что причины, по которым cp действует так, как это имеет корни в философии Unix. Unix предпочитает программы, которые делают одно и делают это хорошо, а также программы, которые просты как по интерфейсу, так и по реализации (иногда их называют хуже - лучше).

Ключевой частью головоломки здесь является понимание того, что cp не копирует каталоги - cp копирует файлытолько файлы). Если вы хотите скопировать каталог, cp вызывает себя рекурсивно, чтобы скопировать файлы в каждом каталоге.

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

Если вы сделали cp способной копировать каталоги, вы скоро захотите добавить больше функций, которые имеют смысл только для каталогов - например, вы можете захотеть копировать только те имена файлов, которые заканчиваются на .sh . Это неизбежно приводит к раздутию и сбою функций, к которым мы привыкли в других операционных системах, что делает программное обеспечение медленным, сложным и подверженным ошибкам.

Другое преимущество заключается в том, что наличие -r также помогает пользователю понять, что на самом деле происходит под интерфейсом. Приятным побочным эффектом этого является то, что изучение концепции рекурсивной операции избавит вас от некоторой работы, когда вы узнаете о других инструментах, которые ее поддерживают (например, grep , например)


Некоторые люди наверняка скажут вам, что предоставление подробностей реализации пользователю плохо, а наличие дополнительных функций - это хорошо. Мое намерение здесь - просто объяснить причину такого поведения, поэтому я не буду спорить в любом случае.

4

Взаимодействие с каталогами гарантирует, что вы знаете, что взаимодействуете с каталогом, а не с одним файлом.

Например:

$ 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

Итак, если вы хотите успешно скопировать папку, так как она подразумевает как папку, так и объекты, связанные со ссылкой на папку, вы должны обращаться с ней, как будто это набор файлов:

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

Следствием изменения значения по умолчанию будет то, что тысячи сценариев оболочки сломаются. Это приводит к требованиям POSIX и SUS для хорошо известного поведения по умолчанию.

Причина заключается в историческом развитии команд cp, ln и mv (одинаковых двоичных файлов в большинстве старых систем UNIX) в различных ветвях UNIX. При появлении -r (в раннем cp не было возможности копировать каталоги; вот ранняя страница справочника cp без -r или -R), были различные различия в обработке специальных файлов, символических ссылок и других капризов файловой системы.

Из Открытой группы Базовые спецификации, выпуск 7:

Более ранние версии этого стандарта включали поддержку опции -r для копирования файловых иерархий. Опция -r является исторической практикой для систем на основе BSD и BSD. Эта опция больше не указана в POSIX.1-2008, но может присутствовать в некоторых реализациях. Параметр -R был добавлен в качестве близкого синонима к параметру -r, выбранный для согласованности со всеми другими параметрами в этом томе POSIX.1-2008, которые выполняют рекурсивный спуск каталога.

Разница между -R и удаленной опцией -r заключается в обработке cp типов файлов, отличных от обычного и каталога. Было определено реализацией, как опция - обрабатывает специальные файлы, чтобы разрешить как исторические реализации, так и те, которые решили поддерживать -r с теми же возможностями, что и -R, определенные этим томом POSIX.1-2008. Исходный флаг -r по историческим причинам не обрабатывал специальные файлы в отличие от обычных файлов, но всегда считывал файл и копировал его содержимое. Это имело очевидные проблемы при наличии специальных типов файлов; например, символьные устройства, FIFO и сокеты.

На самом деле вы все еще будете видеть некоторых людей, регулярно использующих:

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

Потому что они не верят, что реализация cp -r будет тем, к чему они привыкли на произвольной машине; Или потому, что им нужно поведение tar .

3

Сегодня это может быть неоптимальный пользовательский интерфейс, но это решение было принято примерно в 1970 году во время разработки UNIX, когда диск был значительно дороже. Миллионы сценариев оболочки зависят от его работы таким образом, и уже слишком поздно его менять.

Смотрите эту статью для оригинальной информации о дизайне.

3

Очевидным преимуществом флага -r является то, что вы можете cp * /target/dir скопировать только все файлы в исходном каталоге в целевой каталог, исключив (хотя и с предупреждением) все содержащиеся в нем каталоги. Вместо этого cp -r * /target/dir будет копировать все, включая подкаталоги.

2

Этот флаг нужен вам только тогда, когда cp является командой для копирования файлов и каталогов, а не только каталогов.

Если бы существовала специальная команда для копирования каталогов, поведение по умолчанию, безусловно, было бы рекурсивной копией.

1

Как уже упоминали другие, каталог - это просто другой тип файла (в отличие от обычного файла), который обычно "содержит" (указывает на) другие файлы. Он может содержать подкаталоги, к которым относится то же самое ...

Поэтому, если вы копируете каталог (с точки зрения пользователя), вы действительно копируете кучу файлов (с точки зрения файловой системы) (обычные файлы, файлы каталогов, символические ссылки, ...), и для каждого файла каталога вы рекурсивно повторяете это процесс. Поскольку копирование каталога по определению является рекурсивным процессом, аргумент cp называется --recursive .

Конечно, очень легко создать ярлык команды в вашей пользовательской среде (поместите это в ваш файл .profile/.bashrc, чтобы сделать его постоянно доступным):

alias cpr='cp -r'

Или, может быть, лучше:

alias cpa='cp -av'

Таким образом, вы можете скопировать каталог с помощью cpa dir1 copyDir1 и он будет не только печатать то, что копируется, но и применять права доступа к файлам.

И поскольку кто-то упомянул, что cp может теоретически определить, что исходный файл является каталогом, и спросить, следует ли ему копировать его рекурсивно, вот краткое предложение:

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 "$@"
}

Это всего лишь дешевая оболочка cp. Он всегда сохраняет все метаданные (т. Е. Копирует время изменения файла, правильно копирует символические ссылки и т.д.), И, если вы пытаетесь скопировать каталог, он спрашивает, следует ли копировать его (рекурсивно).

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .