bash сценарий: как справиться с различным поведением cp? - PullRequest
0 голосов
/ 08 мая 2020

Среди множества вопросов cp я не нашел ничего об этой разнице в поведении (проверено на Ubuntu 18.04). Извините за потерянный пост, но настройка немного сложна.

Случай 1: Это ожидаемое поведение

Учитывая следующую папку

 source                     
    file1.cpp
    file2.cpp

после запуска этого скрипта

 #!/bin/bash
 cp -r source/ target/   

Я получаю этот результат

 source                     # same as above, plus a copy in "target"
    file1.cpp
    file2.cpp
 target  
    file1.cpp
    file2.cpp

Случай 2: Используя тот же скрипт, исходная папка существует и пуста в целевой папке

Здесь есть одна дополнительная пустая папка source
file1. cpp file2. cpp target source

и запустите тот же сценарий

 #!/bin/bash
 cp -r source/ target/     

, что дает другой, нежелательный результат

 source                     # same as above, plus a copy in "target"
    file1.cpp
    file2.cpp
 target  
    source
       file1.cpp
       file2.cpp

Нормальное решение для Case1 и Case2

 cp -r source/  target/       # works only for Case 1
 cp -r source/* target/       # works only for Case 2

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

Неразрешенная ситуация для Case2

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

 #!/bin/bash
 SOURCE="source"
 TARGET="target"

 if [ -d "$TARGET" ]; then
   cp -r $SOURCE $TARGET     
 else
   cp -r $SOURCE/* $TARGET     # note "$SOURCE/*" would fail.
 fi

, и у меня есть путь $ SOURCE с пробелами.

SOURCE = "исходный код"

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

Как я могу решить эту проблему для Case2?

EDIT

Чтобы прояснить проблему немного подробнее. Это

SOURCE="source" 
cp -r "$SOURCE/*" $TARGET

завершается с ошибкой «не удается статистический источник / : нет такого файла или каталога». Я думаю, это означает, что bash не может заменить / списком файлов, и cp получает его как литерал файла. Очевидно, что файла или папки с именем «источник / *» не существует. Но, может быть, я думаю слишком просто, и то, что делает bash, отличается.

Ответы [ 3 ]

1 голос
/ 08 мая 2020

Может быть, мы используем CP для отдельных файлов и wse rsyn c для каталогов?

rsync -vazh /source /destination

(включает каталог 'source')

rsync -vazh /source/ /destination

(не включает 'source' dir)

1 голос
/ 08 мая 2020

Хотя ваша основная проблема касается команды cp, я также хотел бы кое-что сказать о вашей попытке. Итак, давайте рассмотрим его шаг за шагом.

Проблема 1: cp поведение

Команда cp ведет себя иначе, когда целевая папка содержит папку с тем же именем, что и исходная папка. Такое поведение задумано, но его можно избежать, используя параметр -T согласно man .

Здесь вы можете найти расширенное объяснение опции -T.

Таким образом, вы можете просто выполнить:

cp -rT source/ target/

Проблема 2: Пути, содержащие пробелы

В своей попытке вы упоминаете проблемы при обработке путей с использованием пробелов. Хотя в предыдущем решении вам не нужен специальный сценарий, я хочу подчеркнуть, что переменные с путями, содержащими пробелы, требуют, чтобы все обращения были заключены в двойные кавычки. Содержимое переменной должно быть заключено в двойные кавычки, а не звездочка (поскольку вы хотите, чтобы глобус расширялся, а не воспринимался буквально). Следовательно, ваш предыдущий сценарий будет выглядеть так:

#!/bin/bash

SOURCE=${1:-"source"}
TARGET=${2:-"target"}

if [ -d "$TARGET" ]; then
  cp -r "$SOURCE" "$TARGET"     
else
  cp -r "$SOURCE"/* "$TARGET"    # note "$SOURCE/*" would fail.
fi
0 голосов
/ 08 мая 2020
#!/bin/bash
cp -r --copy-contents source/ target/

- copy-contents означает копирование только содержимого источника

, чтобы лучше понять, что случилось в вашем случае, проверьте этот скрипт:

#!/bin/bash
pwd

вы можете сделать что-то вроде что, если этот pwd напишет разные места:

#!/bin/bash
cd SOURCE_PARENT
cp -r --copy-contents source/ target/

если у вас есть пробелы в имени цели или источника, имейте это в виду cp [OPTION] ... SOURCE ... DIRECTORY

target name <= "target realTarget"
cp -r source target realTarget

cp -r source target realTarget полагаю, что вы хотите скопировать источник и цель в realTarget, он не понимает, что "target realTarget" - это полное имя, если вам нужно скопировать его вам необходимо использовать двойные кавычки в команде

cp -r source "target realTarget"

cp -r source "target realTarget" теперь "target realTarget" используется как имя папки

с исходным кодом, то же самое, используйте двойные кавычки, и вы решите проблему

-> с дополнительным: (избегайте кавычек, используя \)

SOURCE="\"source realsource\"" 
cp -rT --copy-contents $SOURCE $TARGET

-T, --no-target-directory обрабатывать DEST как обычный файл

...