Почему -r рекурсивно необходимо при копировании каталога в Linux?

47

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

$ cp -r dir1 copyDir1

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

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

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

Мадлен П. Винсент
источник
Разве вам не нужно копировать файлы и папки в нем тоже?
QuyNguyen2013
Если вы думаете, что это будет улучшением, вы можете опубликовать этот запрос на канале разработчиков. В противном случае это, вероятно, было запрограммировано давным-давно.
блогер
@blogger Это было запрограммировано давно, но по причине. Это означает, что если кто-то хочет выполнять основную работу в среде командной строки, его задача должна быть настолько простой, насколько трудно избежать сбоя системы. Это означает, что существуют веские причины, по которым существуют некоторые соглашения о взаимодействии пользователя с командной строкой. Я расширяю эту концепцию в своем ответе.
JakeGould
2
Этот вопрос спросил и ответил на unix.SE .
dotancohen
То же самое и дляrm

Ответы:

58

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

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

$ 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, создание прямой копии этого файла будет эквивалентно тому, как будет работать жесткая ссылка. Который не тот, кто хочет.

JakeGould
источник
19
Я лично думаю, что «защитный» аспект этого не проходит тест на запах. Некоторые люди могут так же легко напечатать, cp -r /binкак cp-r ~/bin. Сам флаг не предотвращает ошибок и не обязательно делает кого-то лучше, когда он осторожен. Если вы хотите предотвратить ошибки, команда cp может так же легко посмотреть на рассматриваемый узел и выдать подсказку, что-то вроде «Это каталог, хотите ли вы скопировать все содержимое в указанное место (y / п)?» Это было бы защитной сеткой . Требование -r для каталогов удерживает поток кода больше, чем что-либо еще.
JDL
12
Плохая аналогия с люком. Как сказал @JDL, рассматриваемый флаг не делает ничего, чтобы предотвратить опечатку в пути. Я был бы счастлив принять последовательность с другими командами в качестве причины, но у меня есть ощущение, что настоящая причина в том, что «так было написано в первый раз, и теперь многие вещи полагаются на такое поведение, изменить его невозможно».
Basic
7
Когда мы перемещаем каталог, нам не нужен -r. Я думаю, что связанный ответ на unix.stackexchange.com гораздо более важен. Эквивалентом нерекурсивной копии будет наличие второго каталога и жестких ссылок для всех файлов в дереве каталогов.
Gerrit
2
Если я не ошибаюсь, -r был расширением GNU - я не думаю, что у исторического cp UNIX была рекурсивная копия - и это было одной из причин команды rsync .
Мэй
2
@ Джейк, могу ли я понять основные понятия. Однако я не согласен с идеей, что флаг -r в рассматриваемой команде обеспечивает дополнительную безопасность. Исходя из моего опыта, команды командной строки linux исторически небезопасны. Безопасность была добавлена ​​сверхурочно, если вообще. Безопасность, присущая дизайну Linux, исходит от системы разрешений и отказа от запуска от имени пользователя root. Не запускаться с правами root также является соглашением, а не дизайном. Соглашение поддерживается в большинстве новых инсталляторов Linux, но не всегда.
JDL
19

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

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

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

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

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

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


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

goncalopp
источник
2
+1 «… делай одно и делай это хорошо…» Спасибо за это!
JakeGould
5

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

Например:

$ 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
MBB
источник
3

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

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

Из Открытой группы Base Specification Issue 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, когда диск был значительно дороже. Миллионы сценариев оболочки зависят от его работы таким образом, и уже слишком поздно его менять.

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

pjc50
источник
3

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

Interneci
источник
2

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

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

пижон
источник
1
Имеет смысл. Но почему кто-то копирует каталог, в котором нет хотя бы одного файла? Почему бы тогда просто не использовать mkdir?
JakeGould
1
@ JakeGould возможно, потому что им может понадобиться сохранить права собственности и разрешения?
Руслан
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. Он всегда сохраняет все метаданные (т. Е. Копирует время изменения файла, правильно копирует символические ссылки и т. Д.), И если вы пытаетесь скопировать каталог, он спрашивает, следует ли ему копировать его (рекурсивно).

basic6
источник