1. Overview

The cp command is a tool for copying files and directories. Generally, copying runs without warnings. However, there are some scenarios where cp may need to alert us.

In this tutorial, we’ll look at some messages that the cp command might show when we use it, along with the find command. These include the “same file” warning message and a few others. After that, we’ll learn methods to avoid such warnings.

2. cp Warning Messages

The cp command displays a warning message when it encounters some specific conditions:

  • cp: ‘source_file’ and ‘destination_file’ are the same file
  • cp: will not overwrite just-created ‘destination_file’ with ‘source_file’
  • cp: cannot create regular file ‘destination_file’: Permission denied
  • cp: cannot stat ‘source_file’: No such file or directory
  • cp: omitting directory ‘directory_name’

When cp gives a warning, the copy operation can be considered a failure. In the case of a multi-file copy operation, warnings may indicate a partial failure.

We’ll review the first three warnings from the list above. For brevity, we’ll shorten them to “same file“, “just-created“, and “cannot create regular file“, respectively, to make them easier references.

All commands mentioned in this guide have been tested on 64-bit Debian 11 (Bullseye), running GNU Bash 5.1.4, cp 8.32, and find 4.8.0.

3. Example Setup

In general, let’s consider a scenario where we need to copy all of our .txt files from a directory to one of its subdirectories.

First, we’ll create a sample directory structure. Then, we’ll use the find command to locate some files within that directory.

3.1. Sample Directory Structure

First, let’s use mkdir and touch to create a populated directory tree for our example:

$ mkdir data target
$ touch file00.txt data/file01.txt data/file02.txt
$ tree
.
├── data
│   ├── file01.txt
│   └── file02.txt
├── file00.txt
└── target

2 directories, 3 files

To reiterate, we’ll use the directory structure returned by tree above as our workspace. Within it, target is our main interest.

3.2. Finding the .txt Files With find

Next, let’s employ find to locate all of the .txt files:

$ find . -name '*.txt' -type f -print
./data/file02.txt
./data/file01.txt
./file00.txt

The find command traversed the current directory structure (represented by the dot character), looking for files (-type -f) with a .txt extension (-name ‘*.txt’), and then printed the full file name with a newline-delimiter (-print).

Notably, we use find to locate the files from the current directory, and the target directory is inside the current directory.

4. Avoiding the “same file” Warning Message

Now that we have a list of files found using the find command, let’s copy them to the target directory using the cp command:

$ find . -name '*.txt' -type f -print0 | xargs -0 cp -t ./target/
$ tree
.
├── data
│   ├── file01.txt
│   └── file02.txt
├── file00.txt
└── target
    ├── file00.txt
    ├── file01.txt
    └── file02.txt

2 directories, 6 files

The command was executed successfully. However, the subsequent attempt fails:

$ find . -name '*.txt' -type f -print0 | xargs -0 cp -t ./target/
cp: './target/file02.txt' and './target/file02.txt' are the same file
cp: './target/file00.txt' and './target/file00.txt' are the same file
cp: './target/file01.txt' and './target/file01.txt' are the same file

We received the “same file” warning message because we tried to copy a file to itself. In this case, we tried to copy files already in the target directory to the same target directory.

The command consists of four parts:

  • find looks for .txt files like before, but this time uses –**print0 to print the output with a NULL delimiter, filenames containing newlines
  • pipe symbol (|) redirects the output of the find command to the input of the cp command
  • xarg reads the NULL-delimited input (-0) from the pipe and passes it as arguments to the cp command
  • cp performs the copy operation with -t setting the target directory as ./target (./ means the current directory)

Considering this scenario, let’s see what we can do to work around it.

If we run the find command part, it will now generate a list of files that includes the newly-copied ones in the target directory:

$ find . -name '*.txt' -type f -print
./target/file02.txt
./target/file00.txt
./target/file01.txt
./data/file02.txt
./data/file01.txt
./file00.txt

To exclude the target directory from the find command, we can use the not operator:

$ find . -name '*.txt' -type f -not -path "*/target/*" -print
./data/file02.txt
./data/file01.txt
./file00.txt

Now that we have excluded the target directory, we should be able to rerun the command without getting any warnings:

$ find . -name '*.txt' -type f -not -path '*/target/*' -print0 | xargs -0 cp -t ./target/

The command above will copy the files to the target directory.

If we want the copy operation to happen only when the source file is newer than the destination file or when the destination file is missing, we can use the -u (or –update) option of cp:

$ echo "This is source file00.txt" > file00.txt
$ echo "This is target file00.txt" > target/file00.txt
$ find . -name '*.txt' -type f -not -path '*/target/*' -print0 | xargs -0 cp -u -t ./target/
$ cat file00.txt
This is source file00.txt
$ cat target/file00.txt
This is target file00.txt

As we can see from the output above, target/file00.txt didn’t get overwritten by the command because it’s newer than the source file.

5. Avoiding the “just-created” Warning Message

The “just-created” warning occurs when the find command sees more than one file with the same name under the source directory:

$ mkdir data target
$ touch file00.txt data/file00.txt data/file01.txt
$ tree
.
├── data
│   ├── file00.txt
│   └── file01.txt
├── file00.txt
└── target

2 directories, 3 files
$ find . -name '*.txt' -type f -not -path '*/target/*' -print
./data/file00.txt
./data/file01.txt
./file00.txt
$ find . -name '*.txt' -type f -not -path '*/target/*' -print0 | xargs -0 cp -t ./target/
cp: will not overwrite just-created './target/file00.txt' with './file00.txt'

The find command found two file00.txt files below the source directory (./data/file00.txt and ./file00.txt). As a result, cp copied the one that find found first (./data/file00.txt). However, cp refused to overwrite it with the second one (./file00.txt).

One way to avoid the “just-created” warning message is by preserving the source directory structure in the target directory using the –parents option:

$ find . -name '*.txt' -type f -not -path '*/target/*' -print0 | xargs -0 cp -u --parents -t ./target/
$ tree
.
├── data
│   ├── file00.txt
│   └── file01.txt
├── file00.txt
└── target
    ├── data
    │   ├── file00.txt
    │   └── file01.txt
    └── file00.txt

3 directories, 6 files

Similar to the previous section, if we want the copy operation to happen only when the source file is newer than the destination file or when the destination file is missing, we can use the -u (or –update) option with cp.

6. Avoiding the “cannot create regular file” Warning Message

The “cannot create regular file” warning usually indicates that the cp command doesn’t have the necessary permissions to create or overwrite the destination file.

Let’s simulate it by removing the write permission for the current user from the target directory:

$ mkdir data target
$ touch file00.txt data/file00.txt data/file01.txt
$ ls -l
total 8
drwxr-xr-x 2 baeldung baeldung 4096 Jun 7 23:11 data
-rw-r--r-- 1 baeldung baeldung 0    Jun 7 23:11 file00.txt
drwxr-xr-x 2 baeldung baeldung 4096 Jun 7 23:43 target
$ sudo chmod u-w target
[sudo] password for baeldung:
$ ls -l
total 8
drwxr-xr-x 2 baeldung baeldung 4096 Jun 7 23:11 data
-rw-r--r-- 1 baeldung baeldung 0    Jun 7 23:11 file00.txt
dr-xr-xr-x 2 baeldung baeldung 4096 Jun 7 23:43 target

After removing the write permission, the ls command displays the target directory metadata as dr-xr-xr-x, indicating that the current user no longer has write permission to the target directory.

As a result, when we try to copy files to the target directory, the cp command returns the “cannot create regular file” warning:

$ find . -name '*.txt' -type f -not -path '*/target/*' -print0 | xargs -0 cp -u --parents -t ./target/
cp: cannot make directory './target/./data': Permission denied
cp: cannot make directory './target/./data': Permission denied
cp: cannot create regular file './target/./file00.txt': Permission denied

To avoid this warning, we simply need to add the write permission for the current user back to the target directory:

$ sudo chmod u+w target
$ ls -l
total 8
drwxr-xr-x 2 baeldung baeldung 4096 Jun 7 23:11 data
-rw-r--r-- 1 baeldung baeldung 0    Jun 7 23:11 file00.txt
drwxr-xr-x 3 baeldung baeldung 4096 Jun 7 23:49 target
$ find . -name '*.txt' -type f -not -path '*/target/*' -print0 | xargs -0 cp -u --parents -t ./target/
$ tree
.
├── data
│   ├── file00.txt
│   └── file01.txt
├── file00.txt
└── target
    ├── data
    │   ├── file00.txt
    │   └── file01.txt
    └── file00.txt

3 directories, 6 files

Alternatively, we can use sudo to run the command, which means we’re using our account to execute commands with superuser privileges. However, updating the target directory permissions is often the preferable approach.

By having the appropriate permissions, we avoided the “cannot create regular file” warning message.

7. Conclusion

In this article, we looked at some of the warning messages that may appear when copying a file found using the find command, along with the methods to avoid them.

It’s crucial to pay attention to these warnings in order to ensure our files are copied correctly.