'(solved) bash script: how to cope with the different cp behaviors?
Among the tons of cp questions I have not found anything about this difference in behaviour (tested on Ubuntu 18.04). Sorry for the lost post, but the setting is a bit complex.
Case 1: This is the expected behaviour
Given the following folder
source
file1.cpp
file2.cpp
after running this script
#!/bin/bash
cp -r source/ target/
I do get this result
source # same as above, plus a copy in "target"
file1.cpp
file2.cpp
target
file1.cpp
file2.cpp
Case 2: Using the same script, source folder exists and is empty in the target folder
Here there is one additional, empty folder
source
file1.cpp
file2.cpp
target
source
and run the same script
#!/bin/bash
cp -r source/ target/
which gives me a different, undesired result
source # same as above, plus a copy in "target"
file1.cpp
file2.cpp
target
source
file1.cpp
file2.cpp
Normal Solution for Case1 and Case2
cp -r source/ target/ # works only for Case 1
cp -r source/* target/ # works only for Case 2
Used in the wrong case, one will cause an error, the other yield the wrong result which can be very confusing. This means for each copy action I must check if the target folder exists and use a different command. That is very cumbersome but I am not aware of a more simple solution.
Unresolved situation for Case2
However, the problem I have is this one: When I use variables for the source and target my script looks like this
#!/bin/bash
SOURCE="source"
TARGET="target"
if [ -d "$TARGET" ]; then
cp -r $SOURCE $TARGET
else
cp -r $SOURCE/* $TARGET # note "$SOURCE/*" would fail.
fi
and I have a $SOURCE path with spaces.
SOURCE="source code"
Since I can not use quotations for the source variable, this causes two 'directory not found errors'.
How can I solve this for Case2?
EDIT
To clarify the problem a bit more. This
SOURCE="source"
cp -r "$SOURCE/*" $TARGET
fails with the error "cannot stat source/: No such file or directory". I think that means that bash can not replace the / with the file list and cp gets this as a file literal. A file or folder with the name "source/*" obviously does not exist. But maybe I am thinking too simple and what bash does is different.
Solution 1:[1]
Although your main issue concerns the cp
command there is also something I would like to say about your try. So lets get it step by step.
Issue 1: cp
behaviour
The cp
command behaves differently when the target folder contains a folder with the same name than the source folder. This behaviour is intended but can be avoided using the -T
option according to man.
Here you can find an extended explanation of the -T
option.
Thus, you can just execute:
cp -rT source/ target/
Issue 2: Paths containing spaces
In your try you mention issues when handling paths using spaces. Although with the previous solution you don't need a custom script, I want to highlight that variables with paths containing spaces require all accesses to be double-quoted. The variable content must be double-quoted, not the star (since you want the globstar to expand, not to be taken literally). Hence, your previous script would look like this:
#!/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
Solution 2:[2]
Maybe us CP for individual files and wse rsync for directories ?
rsync -vazh /source /destination
(includes 'source' dir)
rsync -vazh /source/ /destination
(does not include 'source' dir)
Solution 3:[3]
#!/bin/bash
cp -r --copy-contents source/ target/
--copy-contents means copy only the content of source
for understand better that happend in your case test this script:
#!/bin/bash
pwd
you can do some like that if this pwd write different places:
#!/bin/bash
cd SOURCE_PARENT
cp -r --copy-contents source/ target/
if you have spaces in the target or source name have this in mind cp [OPTION]... SOURCE... DIRECTORY
target name <= "target realTarget"
cp -r source target realTarget
cp -r source target realTarget believe that you want copy source & target in realTarget it don't understand that "target realTarget" is a full name if you need copy it you have to use the double quotation marks in the comandad
cp -r source "target realTarget"
cp -r source "target realTarget" now "target realTarget" is taken as folder name
with source happend the same use the double quotation marks and you will solve the problem
->with your extra: (escape the quotes using \
)
SOURCE="\"source realsource\""
cp -rT --copy-contents $SOURCE $TARGET
-T, --no-target-directory treat DEST as a normal file
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | Mike Q |
Solution 3 |