0

I have a lot of files with names in the same format – four alphanumeric characters, .pdb, and a digit – and I would like to copy them into corresponding directories.

For example, in the directory home/Desktop/chain/file I have these files:

1b47.pdb1
3rty.pdb1
3rty.pdb3
7yt5.pdb1
7yt5.pdb2
7yt5.pdb5

Now I have created a lot of directories under home/Desktop/chain; the names of the directories are:

1b47_A
1b47_B
3rty_H
7yt5_2
7yt5_5
7yt5_L

I would like to copy the files in the file directory into the directories with the same base name.  The result would be this:

1b47_A -> 1b47.pdb1
1b47_B -> 1b47.pdb1
3rty_H -> 3rty.pdb1 3rty.pdb3
7yt5_2 -> 7yt5.pdb1 7yt5.pdb2 7yt5.pdb5
7yt5_5 -> 7yt5.pdb1 7yt5.pdb2 7yt5.pdb5
7yt5_L -> 7yt5.pdb1 7yt5.pdb2 7yt5.pdb5

I’m not very familiar with programming languages but I tried with this code:

cd home/Desktop/chain/file
for name in ????; do cp -r $name.pdb? home/Desktop/chain/????; done

But I got a different and not desired result. Could someone help me please?

I use a script and I don't do this manually because there are many files and directories.

2 Answers 2

2
#!/bin/bash

shopt -s nullglob

cd home/Desktop/chain/file || exit 1

for name in ./*.pdb*; do
    for dir in "../${name%.pdb*}"*/; do
        cp "$name" "$dir"
    done
done

This would loop over all the PDB files in the home/Desktop/chain/file directory, with $name holding a name in each iteration. If you are certain that you only have a single character after .pdb at the end of the names, then the pattern ./*.pdb* should be changed to ./*.pdb?, and the pattern used in the parameter substitution in the inner loop should be changed accordingly.

For each file, the file would be copied to each of the directories corresponding to the name with the .pdb* suffix removed in the home/Desktop/chain directory (${name%.pdb*} is the name of the file with the suffix matching .pdb* removed). This is done in an inner loop.

The inner loop is necessary as it's not possible to use cp with multiple target directories, like you're trying to do.

Setting the nullglob shell option at the start ensures that a pattern is removed rather than retained unexpanded if it doesn't match anything.

Example (this shows the view of the chain directory)

$ tree -F
.
|-- 1b47_A/
|-- 1b47_B/
|-- 3rty_H/
|-- 7yt5_2/
|-- 7yt5_5/
|-- 7yt5_L/
`-- file/
    |-- 1b47.pdb1
    |-- 3rty.pdb1
    |-- 3rty.pdb3
    |-- 7yt5.pdb1
    |-- 7yt5.pdb2
    `-- 7yt5.pdb5

7 directories, 6 files

Loop is run here. The result:

$ tree -F
.
|-- 1b47_A/
|   `-- 1b47.pdb1
|-- 1b47_B/
|   `-- 1b47.pdb1
|-- 3rty_H/
|   |-- 3rty.pdb1
|   `-- 3rty.pdb3
|-- 7yt5_2/
|   |-- 7yt5.pdb1
|   |-- 7yt5.pdb2
|   `-- 7yt5.pdb5
|-- 7yt5_5/
|   |-- 7yt5.pdb1
|   |-- 7yt5.pdb2
|   `-- 7yt5.pdb5
|-- 7yt5_L/
|   |-- 7yt5.pdb1
|   |-- 7yt5.pdb2
|   `-- 7yt5.pdb5
`-- file/
    |-- 1b47.pdb1
    |-- 3rty.pdb1
    |-- 3rty.pdb3
    |-- 7yt5.pdb1
    |-- 7yt5.pdb2
    `-- 7yt5.pdb5

7 directories, 19 files

You could potentially save space by instead of copying the files create hard links. You would do this by simply changing cp to ln in the script.

You would not do this if you intend to modify the files in a directory independently of the files with the same name in other directories. A hard link is simply an additional name for a file.

2
  • This script works, but only if the directories I want to copy the files to are in the same directory as the files themselves. Actually, my files are under this PATH:home/Desktop/chain/file while the directory of destination are under this PATH: home/Desktop/chain/file. If I run the script in the directory "file" I have to specify the path of the each final directory. It is clear? How can I do this?
    – Tommaso
    May 14, 2020 at 20:08
  • @Tommaso You listed the same path twice. Do you mean that your destination directories are under home/Desktop/chain while you files are in home/Desktop/chain/file? I have updated the answer.
    – Kusalananda
    May 14, 2020 at 20:25
0

You can do this without having nested loops:

cd home/Desktop/chain
for dir in *_?
do
        base=${dir%_?}
        cp  file/"$base".*  "$dir"
done

This assumes that there are no files under home/Desktop/chain whose names have a _ as the penultimate (second-to-last) character.  If there are, use

cd home/Desktop/chain
for dir in *_?/
do
        base=${dir%_?/}
        cp  file/"$base".*  "$dir"
done

in which we append / to the *_? pattern to get only directories.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .