Linux: скопіюйте та створіть призначення dir, якщо його не існує


348

Я хочу команду (або, можливо, опцію для cp), яка створює каталог призначення, якщо він не існує.

Приклад:

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


5
Прийнята відповідь невірна (є така опція для cp). Дивіться другу відповідь Пола Віппа.
Ronenz

1
@Ronenz, я погоджуюся, що в GNU cp(на який відповів Пол Віпп) є варіант, який варто згадати , але він не такий загальний, як вимагав ОП. Він може копіювати a/foo/bar/fileдо , b/foo/bar/fileале він не може скопіювати fileв b/foo/bar/file. (тобто працює лише для створення батьківських каталогів, які вже існували у випадку першого файлу, але він не може створити довільні каталоги)
Sudo Bash,

Це виглядає як приємний запит на функцію cp. Будь-який шанс, що ми могли б отримати такий перемикач командного рядка найближчим часом?
Арно

Відповіді:


328
mkdir -p "$d" && cp file "$d"

(для такої опції немає cp).


62
Я не думаю, що вам потрібне test -d: mkdir -pвсе ще вдається, навіть якщо каталог вже існує.
ефеміент

ой, правильно. Але тоді тест може бути вбудованим баштом (особливо, якщо він написаний як [[-d "$ d"]]) і mkdir не може ;-)
Майкл Крелін - хакер

1
mkdir- це вбудований у BusyBox;)
ефеміент

7
@holms $d- це змінна, яка, імовірно, містить відповідний каталог. Я маю на увазі, якщо вам доведеться повторити його тричі, ви, швидше за все, спочатку призначите його змінній.
Майкл Крелін - хакер

237

Якщо істинне та інше:

  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

4
Це не працює на Mac OS X, тому я думаю, що це специфічно для Linux.
OLT

7
Я б сказав, специфічний для gnu. Якщо у вас є, macportsви можете встановити coreutils та його gcp.
Майкл Крелін - хакер

16
Людина, у Linux є все
Ziggy

19
Я відчуваю, що це відповідь на дещо інше питання, навіть якщо це відповідь, яку я шукав (а отже, і той, кого я схвалив). Я думаю, що це рішення, припускаючи, що ви хочете, щоб цільові батьківські каталоги були такими ж, як вихідні каталоги батьків. Це, мабуть,
корисний

7
Це передбачає, що "bar" каталогу вже існує, але початкове запитання передбачає, як автоматично створити "bar", коли він надається як пункт призначення, а він ще не існує. Відповідь на це дає @ MichaelKrelin-хакер.
туан

69

Коротка відповідь

Для копіювання 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"спеціальний параметр", який може бути зручним для уникнення повторення довгих аргументів (наприклад, до файлових шляхів) без необхідності зберігати їх у змінній. Відповідно до посібника Bash , це:

розширюється до останнього аргументу до попередньої команди

У цьому випадку /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" "$_"

16
Убік: Мене зазвичай розчаровує відсутність деталей у питаннях команд оболонки Bash або Unix на Stack Overflow, які часто викидають складний і надзвичайно щільний однолінійний склад, який важко вилучити, якщо ви вже не майстер Unix. Тут я намагався піти на протилежний край, і обидва пояснюють кожну деталь синтаксису та посилаються на відповідні місця, щоб знайти документацію про те, як працює цей матеріал. Я сподіваюся, що це допомагає принаймні деяким людям. Крім того, заслуга, де належно: я підписав цей підхід від цієї відповіді на повторне запитання: stackoverflow.com/a/14085147/1709587
Марк Амерді

Я ніколи раніше не бачив $ _. про що йдеться? папку, використану в останній команді?
thebunnyrules

9
@thebunnyrules Але ... але ... є "Як це працює?" розділ моєї відповіді, який буквально містить кілька абзаців, спеціально присвячених питанню, яке ви тільки що задали. Він відчуває себе ігнорованим і нелюбимим. :(
Марк Амері

Вибач за те. Ви абсолютно праві. Я вже знав усе інше у вашій відповіді, тому просто швидко просканував це. Я мав би прочитати це уважніше.
thebunnyrules

Нічого собі, ти справді багато поклав на цю відповідь ... Дякую за це. Приємно було дізнатися про: $ _. Я бачу себе, використовуючи це зовсім небагато. Я написав сценарій, щоб автоматизувати весь процес і вмістити в цю нитку. Дайте йому йти, може бути , вам сподобається: stackoverflow.com/questions/1529946 / ...
thebunnyrules

39

Таке давнє запитання, але, можливо, я можу запропонувати альтернативне рішення.

Програму можна installскопіювати, щоб скопіювати файл і створити шлях призначення "на льоту".

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

Однак слід врахувати деякі аспекти:

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

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


І ось це безсоромне посилання на відповідь, на яку мене надихнуло =)


Насправді не моє використання, але крутий трюк! Я буду мати на увазі
thebunnyrules

зауважте, що ця команда створює цільові файли з дозволами 755( rwxr-xr-x), що, ймовірно, не бажано. Ви можете вказати щось інше за допомогою -mперемикача, але я не зміг знайти спосіб просто зберегти дозволи файлу :(
törzsmókus

19

Функція оболонки, яка робить те, що ви хочете, називаючи її "похоронною" копією, оскільки вона викопує отвір для файлу, в якому живе:

bury_copy() { mkdir -p `dirname $2` && cp "$1" "$2"; }

1
Ви повинні мати в лапки dirname $2теж
Tarnay Кальман

4
@Kalmi, для правильної цитати ви також хочете навести його $2як аргумент dirname. Як mkdir -p "$(dirname "$2")". Без цього цитування $2в cpдаремно;)
Майкл Крелін - хакер

3
Зауважте, що ця відповідь передбачає, що $ 2 - це вже не цільовий каталог, інакше ви просто хочете "mkdir -p" $ 2 "&& cp" $ 1 "" $ 2 "як функціональний орган
user467257

Я думаю, що це помилка, як вказує @ user467257 Я не думаю, що це можна виправити, оскільки $ 2 неоднозначно між цільовим режимом та цільовим файлом у цьому сценарії. Я розмістив альтернативну відповідь, яка вирішує це.
Багатий

@Rich, я не погоджуюся, що це не можна виправити. Такі файли спрацьовують як для файлів, так і для каталогів: mkdir -p dirname "$2"&& cp -r "$ 1" "$ 2"; Зауважте, що підсипки навколо них dirname $2не відображаються, оскільки SO аналізує їх як маркери коду.
Юрій

11

Ось один із способів зробити це:

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 потрібна базова директорія.

Перевага цього over -parents полягає в тому, що він працює для випадку, коли останнім елементом шляху призначення є ім'я файлу.

І він буде працювати на OS X.


6

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


1
насправді це зовсім не те саме. він вимагає, щоб ви два рази передавали ім'я файлу (звідси прапор '-t'), і він встановлював біти виконання файлу (звідси приклад '-m'). його не повинно бути головною відповіддю, оскільки це насправді не відповідає на питання - тоді як моє.
Spongman

1
Це працює для одного файлу. Просто врахуйте, що thereце кінцевий файл, а не остаточний підкаталог.
kvantour

6

При всій повазі до відповідей вище, я вважаю за краще використовувати rsync так:

$  rsync -a directory_name /path_where_to_inject_your_directory/

приклад:

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

Я спробував це і перейшов до цього результату: rsync -a bull / some / hoop / ti / doop / ploop RESULT rsync: mkdir "/ some / hoop / ti / doop / ploop" не вдалося: Немає такого файлу чи каталогу (2) rsync error : помилка у файлі IO (код 11) у main.c (675) [Приймач = 3.1.2]
thebunnyrules

@thebunnyrules вам доведеться перевірити шлях за допомогою команди pwd (дивіться: [посилання] ( access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/… )) і правильно вставити його у свою інструкцію
marcdahan

Якщо я правильно зрозумів вашу пропозицію, ви пропонуєте мені вказати повний шлях до джерела, який використовується: "$ (pwd) / bull" у моїй команді, а не просто ім'я файлу: bull? Справа в тому, що помилка полягає в тому, що rsync створює призначення dir, а не джерело (bull). Мені здається, rsync використовує простий mkdir, а не mkdir -p.
thebunnyrules

1
До речі, велика корисність, я можу почати використовувати у своїх сценаріях. Дякуємо, що рекомендували його.
thebunnyrules

1
rsync є більш передбачуваним, ніж cp. Наприклад, якщо я хочу, щоб cp /from/somedir /to-dircp поводився інакше, якщо він /to-dirвже існує: Якщо /to-dirіснує, тоді cpбуде створено /to-dir/some-dir, інакше /from/somedirрозміщується просто вміст /to-dir. Однак rsyncповодиться так само у випадку, /to-dir/some-dirякщо завжди буде створено.
JoeAC

5

Просто відновити і дати повне робоче рішення, в один рядок. Будьте уважні, якщо ви хочете перейменувати свій файл, вам слід включити спосіб надання чистого режиму dir до mkdir. $ fdst може бути файлом або dir. Наступний код повинен працювати в будь-якому випадку.

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

або баш-специфічний

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

Дякую! Це те, що мені було потрібно. У моєму випадку я не знаю, має місце призначення чи ні, і чи існує каталог, чи ні, тому цей код дає мені батьківський каталог, і тоді я можу створити його безпечно, якщо його немає
xbmono

4

Просто додайте наступне у свій .bashrc, налаштуйте, якщо вам потрібно. Працює в Ubuntu.

mkcp() {
    test -d "$2" || mkdir -p "$2"
    cp -r "$1" "$2"
}

Наприклад, якщо ви хочете скопіювати тестовий файл у каталог призначення "d",

mkcp test a/b/c/d

mkcp спочатку перевірить, чи існує каталог призначення чи ні, якщо ні, то зробіть його та скопіюйте вихідний файл / каталог.


Як інші коментували іншу відповідь, вам не потрібно перевіряти, чи існує каталог перед викликом mkdir -p. Це вдасться, навіть якщо воно вже є.
bfontaine

3

Це робить це для мене

cp -vaR ./from ./to

Погодьтеся. В man cp:-R If source_file designates a directory, cp copies the directory and the entire subtree connected at that point.
borracciaBlu

3

Я написав скрипт підтримки для cp, званий CP (замініть великі літери), який призначений зробити саме це. Сценарій перевірятиме помилки в шляху, який ви ввели (крім останнього, який є пунктом призначення), і якщо все добре, він зробить крок mkdir -p, щоб створити шлях до призначення до початку копіювання. У цей момент звичайна утиліта cp бере на себе і будь-які комутатори, які ви використовуєте з CP (наприклад, -r, -p, -rpL, передаються прямо на cp). Перш ніж використовувати мій сценарій, вам потрібно зрозуміти кілька речей.

  • всю інформацію тут можна отримати, зробивши CP --help. CP --help-all включає перемикачі cp.
  • звичайний cp не виконує копію, якщо він не знайде шлях до місця призначення. У вас немає такої мережі безпеки для помилок друку з CP. Ваш пункт призначення буде створений, тому якщо ви неправильно написали призначення, як / usrr / share / значки або / usr / share / icon, це саме те, що буде створено.
  • звичайний cp схильний моделювати свою поведінку на існуючому шляху: cp / a / b / c / d буде залежати від того, існує чи ні. якщо d - наявна папка, cp скопіює b у неї, зробивши / c / d / b. Якщо d не існує, b буде скопійовано в c та перейменовано в d. Якщо d існує, але це файл, а b - файл, його буде замінено копією b. Якщо c не існує, cp не робить копію і не виходить.

У CP немає розкоші приймати підказки з існуючих шляхів, тому він повинен мати дуже тверді моделі поведінки. CP передбачає, що елемент, який ви копіюєте, викидається у пункт призначення та не є самим пунктом призначення (він же перейменований примірник вихідного файлу / папки). Значення:

  • "CP / a / b / c / d" призведе до / c / d / b, якщо d - папка
  • "CP / a / b / c / b" призведе до / c / b / b, якщо b в / c / b - папка.
  • Якщо обидва b і d є файлами: CP / a / b / c / d призведе до / c / d (де d - копія b). Те саме для CP / a / b / c / b за тієї ж обставини.

Цю поведінку CP за замовчуванням можна змінити за допомогою перемикача "--rename". У цьому випадку передбачається, що

  • "CP - ім'я / a / b / c / d" - це копіювання b в / c і перейменування копії в d.

Кілька завершальних приміток: Як і у випадку з cp, CP може одночасно копіювати декілька елементів, перелічуючи останній шлях, який вважається пунктом призначення. Він також може обробляти шляхи з пробілами, якщо ви використовуєте лапки.

CP перевірять шляхи, які ви вводите, і переконайтеся, що вони існують, перш ніж зробити копію. У суворому режимі (доступний через - строгий перемикач) всі файли / папки, що копіюються, повинні існувати або копія не відбувається. У розслабленому режимі (- з повторним релексом) копіювання буде продовжено, якщо існує хоча б один із перелічених вами елементів. Розслаблений режим є типовим, ви можете тимчасово змінити режим за допомогою перемикачів або назавжди, встановивши змінну easy_going на початку сценарію.

Ось як її встановити:

У некорінному терміналі виконайте:

sudo echo > /usr/bin/CP; sudo chmod +x /usr/bin/CP; sudo touch /usr/bin/CP
gedit admin:///usr/bin/CP 

В gedit вставте утиліту CP та збережіть:

#!/bin/bash
#Regular cp works with the assumption that the destination path exists and if it doesn't, it will verify that it's parent directory does.

#eg: cp /a/b /c/d will give /c/d/b if folder path /c/d already exists but will give /c/d (where d is renamed copy of b) if /c/d doesn't exists but /c does.

#CP works differently, provided that d in /c/d isn't an existing file, it assumes that you're copying item into a folder path called /c/d and will create it if it doesn't exist. so CP /a/b /c/d will always give /c/d/b unless d is an existing file. If you put the --rename switch, it will assume that you're copying into /c and renaming the singl item you're copying from b to d at the destination. Again, if /c doesn't exist, it will be created. So CP --rename /a/b /c/d will give a /c/d and if there already a folder called /c/d, contents of b will be merged into d. 

#cp+ $source $destination
#mkdir -p /foo/bar && cp myfile "$_"

err=0 # error count
i=0 #item counter, doesn't include destination (starts at 1, ex. item1, item2 etc)
m=0 #cp switch counter (starts at 1, switch 1, switch2, etc)
n=1 # argument counter (aka the arguments inputed into script, those include both switches and items, aka: $1 $2 $3 $4 $5)
count_s=0
count_i=0
easy_going=true #determines how you deal with bad pathes in your copy, true will allow copy to continue provided one of the items being copied exists, false will exit script for one bad path. this setting can also be changed via the custom switches: --strict and --not-strict
verbal="-v"


  help="===============================================================================\
    \n         CREATIVE COPY SCRIPT (CP) -- written by thebunnyrules\
    \n===============================================================================\n
    \n This script (CP, note capital letters) is intended to supplement \
    \n your system's regular cp command (note uncapped letters). \n
    \n Script's function is to check if the destination path exists \
    \n before starting the copy. If it doesn't it will be created.\n    
    \n To make this happen, CP assumes that the item you're copying is \
    \n being dropped in the destination path and is not the destination\
    \n itself (aka, a renamed copy of the source file/folder). Meaning:\n 
    \n * \"CP /a/b /c/d\" will result in /c/d/b \
    \n * even if you write \"CP /a/b /c/b\", CP will create the path /a/b, \
    \n   resulting in /c/b/b. \n
    \n Of course, if /c/b or /c/d are existing files and /a/b is also a\
    \n file, the existing destination file will simply be overwritten. \
    \n This behavior can be changed with the \"--rename\" switch. In this\
    \n case, it's assumed that \"CP --rename /a/b /c/d\" is copying b into /c  \
    \n and renaming the copy to d.\n
    \n===============================================================================\
    \n        CP specific help: Switches and their Usages \
    \n===============================================================================\n
    \
    \n  --rename\tSee above. Ignored if copying more than one item. \n
    \n  --quiet\tCP is verbose by default. This quiets it.\n
    \n  --strict\tIf one+ of your files was not found, CP exits if\
    \n\t\tyou use --rename switch with multiple items, CP \
    \n\t\texits.\n
    \n  --relaxed\tIgnores bad paths unless they're all bad but warns\
    \n\t\tyou about them. Ignores in-appropriate rename switch\
    \n\t\twithout exiting. This is default behavior. You can \
    \n\t\tmake strict the default behavior by editing the \
    \n\t\tCP script and setting: \n
    \n\t\teasy_going=false.\n
    \n  --help-all\tShows help specific to cp (in addition to CP)."

cp_hlp="\n\nRegular cp command's switches will still work when using CP.\
    \nHere is the help out of the original cp command... \
    \n\n===============================================================================\
    \n          cp specific help: \
    \n===============================================================================\n"

outro1="\n******************************************************************************\
    \n******************************************************************************\
    \n******************************************************************************\
    \n        USE THIS SCRIPT WITH CARE, TYPOS WILL GIVE YOU PROBLEMS...\
    \n******************************************************************************\
    \n******************************* HIT q TO EXIT ********************************\
    \n******************************************************************************"


#count and classify arguments that were inputed into script, output help message if needed
while true; do
    eval input="\$$n"
    in_=${input::1}

    if [ -z "$input" -a $n = 1 ]; then input="--help"; fi 

    if [ "$input" = "-h" -o "$input" = "--help" -o "$input" = "-?" -o "$input" = "--help-all" ]; then
        if [ "$input" = "--help-all" ]; then 
            echo -e "$help"$cp_hlp > /tmp/cp.hlp 
            cp --help >> /tmp/cp.hlp
            echo -e "$outro1" >> /tmp/cp.hlp
            cat /tmp/cp.hlp|less
            cat /tmp/cp.hlp
            rm /tmp/cp.hlp
        else
            echo -e "$help" "$outro1"|less
            echo -e "$help" "$outro1"
        fi
        exit
    fi

    if [ -z "$input" ]; then
        count_i=$(expr $count_i - 1 ) # remember, last item is destination and it's not included in cound
        break 
    elif [ "$in_" = "-" ]; then
        count_s=$(expr $count_s + 1 )
    else
        count_i=$(expr $count_i + 1 )
    fi
    n=$(expr $n + 1)
done

#error condition: no items to copy or no destination
    if [ $count_i -lt 0 ]; then 
            echo "Error: You haven't listed any items for copying. Exiting." # you didn't put any items for copying
    elif [ $count_i -lt 1 ]; then
            echo "Error: Copying usually involves a destination. Exiting." # you put one item and no destination
    fi

#reset the counter and grab content of arguments, aka: switches and item paths
n=1
while true; do
        eval input="\$$n" #input=$1,$2,$3,etc...
        in_=${input::1} #first letter of $input

        if [ "$in_" = "-" ]; then
            if [ "$input" = "--rename" ]; then 
                rename=true #my custom switches
            elif [ "$input" = "--strict" ]; then 
                easy_going=false #exit script if even one of the non-destinations item is not found
            elif [ "$input" = "--relaxed" ]; then 
                easy_going=true #continue script if at least one of the non-destination items is found
            elif [ "$input" = "--quiet" ]; then 
                verbal=""
            else
                #m=$(expr $m + 1);eval switch$m="$input" #input is a switch, if it's not one of the above, assume it belongs to cp.
                switch_list="$switch_list \"$input\""
            fi                                  
        elif ! [ -z "$input" ]; then #if it's not a switch and input is not empty, it's a path
                i=$(expr $i + 1)
                if [ ! -f "$input" -a ! -d "$input" -a "$i" -le "$count_i" ]; then 
                    err=$(expr $err + 1 ); error_list="$error_list\npath does not exit: \"b\""
                else
                    if [ "$i" -le "$count_i" ]; then 
                        eval item$i="$input" 
                        item_list="$item_list \"$input\""
                    else
                        destination="$input" #destination is last items entered
                    fi
                fi
        else
            i=0
            m=0
            n=1                     
            break
        fi      
        n=$(expr $n + 1)
done

#error condition: some or all item(s) being copied don't exist. easy_going: continue if at least one item exists, warn about rest, not easy_going: exit.
#echo "err=$err count_i=$count_i"
if [ "$easy_going" != true -a $err -gt 0 -a $err != $count_i ]; then 
    echo "Some of the paths you entered are incorrect. Script is running in strict mode and will therefore exit."
    echo -e "Bad Paths: $err $error_list"
    exit
fi

if [ $err = $count_i ]; then
    echo "ALL THE PATHS you have entered are incorrect! Exiting."
    echo -e "Bad Paths: $err $error_list"
fi

#one item to one destination:
#------------------------------
#assumes that destination is folder, it does't exist, it will create it. (so copying /a/b/c/d/firefox to /e/f/firefox will result in /e/f/firefox/firefox
#if -rename switch is given, will assume that the top element of destination path is the new name for the the item being given.

#multi-item to single destination:
#------------------------------
#assumes destination is a folder, gives error if it exists and it's a file. -rename switch will be ignored.

#ERROR CONDITIONS: 
# - multiple items being sent to a destination and it's a file.
# - if -rename switch was given and multiple items are being copied, rename switch will be ignored (easy_going). if not easy_going, exit.
# - rename option but source is folder, destination is file, exit.
# - rename option but source is file and destination is folder. easy_going: option ignored.

if [ -f "$destination" ]; then
    if [ $count_i -gt 1 ]; then 
        echo "Error: You've selected a single file as a destination and are copying multiple items to it. Exiting."; exit
    elif [ -d "$item1" ]; then
        echo "Error: Your destination is a file but your source is a folder. Exiting."; exit
    fi
fi
if [ "$rename" = true ]; then
    if [ $count_i -gt 1 ]; then
        if [ $easy_going = true ]; then
            echo "Warning: you choose the rename option but are copying multiple items. Ignoring Rename option. Continuing."
        else
            echo "Error: you choose the rename option but are copying multiple items. Script running in strict mode. Exiting."; exit
        fi
    elif [ -d "$destination" -a -f "$item1" ]; then
        echo -n "Warning: you choose the rename option but source is a file and destination is a folder with the same name. "
        if [ $easy_going = true ]; then
            echo "Ignoring Rename option. Continuing."
        else
            echo "Script running in strict mode. Exiting."; exit
        fi
    else
        dest_jr=$(dirname "$destination")
        if [ -d "$destination" ]; then item_list="$item1/*";fi
        mkdir -p "$dest_jr"
    fi
else
    mkdir -p "$destination"
fi

eval cp $switch_list $verbal $item_list "$destination"

cp_err="$?"
if [ "$cp_err" != 0 ]; then 
    echo -e "Something went wrong with the copy operation. \nExit Status: $cp_err"
else 
    echo "Copy operation exited with no errors."
fi

exit

Це, можливо, надмірне, але досить ефективне, працює як очікувалося, дякую добрий пане.
Xedret

2

cp має кілька разів:

$ cp --help
Usage: cp [OPTION]... [-T] SOURCE DEST
  or:  cp [OPTION]... SOURCE... DIRECTORY
  or:  cp [OPTION]... -t DIRECTORY SOURCE...
Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.

@ Відповідь ЕндіРосса працює для

cp SOURCE DEST

стиль cp, але робить неправильну справу, якщо ви використовуєте

cp SOURCE... DIRECTORY/

стиль cp .

Я думаю, що "DEST" є неоднозначним без останньої косої риски в цьому використанні (тобто там, де цільовий каталог ще не існує), саме тому cp , ніколи не було додано для цього варіанту.

Отже, ось моя версія цієї функції, яка нав'язує кінцеву косу рису на dest dir:

cp-p() {
  last=${@: -1}

  if [[ $# -ge 2 && "$last" == */ ]] ; then
    # cp SOURCE... DEST/
    mkdir -p "$last" && cp "$@"
  else
    echo "cp-p: (copy, creating parent dirs)"
    echo "cp-p: Usage: cp-p SOURCE... DEST/"
  fi
}

2

Просто було те саме питання. Мій підхід полягав у тому, щоб просто націлити файли в архів так:

tar cf your_archive.tar file1 /path/to/file2 path/to/even/deeper/file3

tar автоматично зберігає файли у відповідній структурі в архіві. Якщо ти біжиш

tar xf your_archive.tar

файли витягуються в потрібну структуру каталогу.


1

Як запропоновано вище help_asap та spongeman, ви можете скористатися командою 'install' для копіювання файлів у існуючі каталоги або створення нових каталогів призначення, якщо вони ще не існують.

Варіант 1 install -D filename some/deep/directory/filename
копіює файл у новий або існуючий каталог та надає ім'я файлу 755 дозволів за замовчуванням

Варіант 2 install -D filename -m640 some/deep/directory/filename
за варіантом 1, але дає ім'я файлу 640 дозволів.

Варіант 3 install -D filename -m640 -t some/deep/directory/
згідно з Варіантом 2, але націлює ім'я файлу в цільовий каталог, тому ім'я файлу не потрібно записувати як у вихідному, так і в цільовому.

Варіант 4 install -D filena* -m640 -t some/deep/directory/
відповідно до Варіанту 3, але використовує підстановку для декількох файлів.

Це добре працює в Ubuntu і поєднує два етапи (створення каталогу, а потім копія файлу) в один крок.


1

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


0
rsync file /path/to/copy/file/to/is/very/deep/there

Це може спрацювати, якщо у вас є правильний вид rsync.


Це не працює для мене - я отримав "Немає такого файлу чи каталогу" на dest dir
Rich

0

Скопіюйте з джерела на неіснуючий шлях

mkdir p /destination && cp r /source/ $_

ПРИМІТКА. Ця команда копіює всі файли

cp –r для копіювання всіх папок та його вмісту

$_ працювати як пункт призначення, створений в останній команді


0

Простий

cp -a * /path/to/dst/

повинен зробити трюк.


Це не так. '/ Шлях / до / dst /' повинен існувати заздалегідь.
Shital Shah

-1

Скажімо, ви робите щось подібне

cp file1.txt A / B / C / D / file.txt

де A / B / C / D - це каталоги, які ще не існують

Можливе рішення полягає в наступному

DIR=$(dirname A/B/C/D/file.txt)
# DIR= "A/B/C/D"
mkdir -p $DIR
cp file1.txt A/B/C/D/file.txt

сподіваюся, що допомагає!

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