Як скопіювати папку рекурсивно у безвідмовний спосіб за допомогою cp?


46

Коли я користуюся

cp -R inputFolder outputFolder

результат залежить від контексту :

  1. якщо outputFolderйого немає, він буде створений, і шлях клонованої папки буде outputFolder.
  2. якщо outputFolderіснує, то створений клон будеoutputFolder/inputFolder

Це жахливо , тому що я хочу створити якийсь інсталяційний скрипт, і якщо користувач запустить його двічі помилково, він outputFolderстворив би перший раз, то під час другого запуску всі речі будуть створені ще раз у outputFolder/inputFolder.

  • Я хочу завжди першої поведінки: створити клона поруч з оригіналом (як брати-сестри).
  • Я хочу використовувати cpпортативний (наприклад, MINGW не rsyncпостачається)
  • Я перевірив, cp -R --parentsале це відтворює шлях на всьому дереві каталогів (так що клон не буде, outputFolderале some/path/outputFolder)
  • --remove-destinationабо --updateякщо 2 нічого не змінить, все-таки копіюються речіoutputFolder/inputFolder

Чи є спосіб це зробити, не попередньо перевіряючи наявність outputFolder(якщо папка не існує тоді ...) або використовуючи rm -rf outputFolder?

Який узгоджений, портативний спосіб UNIX зробити це?



2
Деякі з цих варіантів не є POSIX, тому ті, що ви спробували, не дуже портативні. Якщо ви можете жити з невеликою відсутністю портативності, чому б не використовувати rsync?
муру

1
Якщо вам не потрібно використовувати cp, прокладка між двома tarкомандами є приємним надійним способом копіювання дерев.
R ..

@R .. Ви можете навести приклад? @muru Я хотів би, щоб це працювало в MINGW, який не rsyncпостачається.
jakub.g

З GNU tar, tar -C input -cf - . | tar -C output -xf -працює. -Cзмінює робочий каталог, але це не стандартний варіант, тому, якщо він не підтримується, для початку потрібно запустити два тари у потрібних робочих каталогах, наприклад, ( cd input && tar -cf - . ) | ( cd output && tar -xf - )якщо ви маєте справу з Windows, я просто використав би відповідь roaima.
R ..

Відповіді:


54

Використовуйте це замість:

cp -R inputFolder/. outputFolder

Це працює точно так само, як, скажімо, cp -R aaa/bbb cccпрацює: якщо cccйого не існує, він створюється як копія bbbта його вміст; але якщо cccвже існує, то ccc/bbbстворюється як копія bbbта його вміст.

Майже в будь-якому випадку bbbце призводить до небажаної поведінки, яку ви зазначили у своєму запитанні. Однак у цій конкретній ситуації bbbце справедливо ., так і aaa/bbbє насправді aaa/., що, в свою чергу, є справді лише aaaіншою назвою. Отже, у нас є два такі сценарії:

  1. ccc не існує:

    Команда cp -R aaa/. cccозначає "створити cccта скопіювати вміст aaa/.у ccc/., тобто скопіювати aaaв ccc.

  2. ccc чи існує:

    Команда cp -R aaa/. cccозначає "скопіювати вміст aaa/.у ccc/., тобто скопіювати aaaв ccc.


3
Так, це для мене нове, але, здається, працює! Це це документується де-небудь?
Ільмарі Каронен

1
Я протестував inputFolder/.замість цього inputFolderі справді це працює для мене на Windows / Git Bash. (Однак це не працює лише з проривом /, мені також потрібна крапка після косої риски) Я дав +1, оскільки це цікаво, хоча, мабуть, це занадто хитро, щоб використовувати його у відкритому коді :)
jakub.g

@IlmariJaronen це не потрібно явно документувати. Дотримуйтесь пояснення, і ви побачите, як воно просто «дотримується правил».
roaima

18

Не копіюйте папку, а лише скопіюйте вміст:

## Create the target directory. The -p suppresses error messages
## if the directory already exists
mkdir -p outputFolder

## Copy the contents recursively, this will not recreate the parent
cp -R inputfolder/* outputfolder/

Таким чином, ви обидва забезпечуєте створення цільового каталогу при першому запуску скрипту та уникнення проблеми при його запуску вдруге.

Кріс Даун дуже правильно вказує, що в баші це буде пропускати файли, ім'я яких починається з а .. Щоб цього уникнути, можна запустити shopt -s dotglobперед запуском команди вище.

І -pдля,mkdir і -Rдляcp визначені POSIX, тому це повинно бути ідеально портативним.


6
Примітка. Це не буде копіювати файли, починаючи з .. В bash, ви можете використовувати dotglobдля цього.
Кріс Даун

2
Зауважте, що цей метод, ймовірно, не вдасться, якщо кількість записів каталогів у вхідній папці велика, оскільки розширення глобальної групи може перевищувати максимальну довжину командного рядка.
Тім Каттс

11

Спробуйте вибрати -Tваріант cp. Це існує у GNU coreutils cpверсії 8.22; він може не бути портативним поза цим.


1
Так, здається, що саме цього я хотів: cp -T( cp --no-target-directory). unix.stackexchange.com/questions/94831/… На жаль, моя версія cpнабагато старша.
jakub.g

1
+1 Використовував це лише сьогодні. -u- чудовий варіант додати, щоб зробити його ледачим.
PSkocik

4

Ви також можете використовувати rsync:

rsync -uav inputFolder/ outputFolder/

(зверніть увагу на косої риси, особливо після першої)


0

На мою думку, немає нічого простішого і більш портативного, ніж rm -rf outputFolder. Отже, я завжди дотримуюся цього. Я розумію, що ваше питання інше, але я думаю, що це найкраща практика.


Це не вдається, якщо на цілі були конкретні дозволи або права власності, які потрібно було зберегти. Або якщо це було симпосилання.
roaima

1
Питання хоче, щоб результат cpбув однаковим, як тоді, коли каталог не існує, так і коли він існує. Отже, про що ти говориш?
dashohoxha

cpКоманди , як зазначено в ОП не зберігається дозволу (або мітки).
roaima

0

Ви можете використовувати -tопцію cpкоманди як:

cp -R inputFolder -t outputFolder

Тепер, якщо цільової папки не існує, вона видасть помилку:

cp: failed to access ‘outputFolder’: No such file or directory

Примітка вище команда буде копіювати inputFolderразом зі своїм вмістом (а не лише вмістом всередині нього)

якщо ви хочете скопіювати лише вміст, inputFolderвін стає дещо складним (оскільки вам слід бути обережними під час використання гліббінгу оболонки під час використання зірочки *)

cp -R  -t outputFolder/ -- inputFolder/*

Тепер, якщо цільової папки не існує, вона видасть помилку:

cp: failed to access ‘outputFolder’: No such file or directory

працює з cp (GNU coreutils) 8.23

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.