Copy recursively multiple folders as symbolic links except one - linux

I need to copy multiple folders in a directory recursively with their files as symbolic links for example :
[root#tests : /app/dirs]#
|-- dir1
|-- dir2
|-- dir3
|-- dir4
|-- dir5
| |-- a.txt
| |-- b.o
| `-- c.txt
I want to copy the content of /app/dirs to an existing directory in the same folder with symbolic links i tried this :
cp -as !(/app/dirs/dir3) ./dir3
I tried to exclude dir3 because you can't copy a folder in itself but now i get an error saying that the symbolic links can't be created for the files in dir5.
the expected result :
/home/barmar/test.dir
├── dir1
├── dir2
├── dir3
│ ├── dir1
│ ├── dir2
│ ├── dir4
│ └── dir5
│ ├── a.txt -> /home/barmar/test.dir/dir5/a.txt
│ ├── b.o -> /home/barmar/test.dir/dir5/b.o
│ └── c.txt -> /home/barmar/test.dir/dir5/c.txt
├── dir4
└── dir5
├── a.txt
├── b.o
└── c.txt
is there anyway to acheive the expected result ?

!(/app/dirs/dir3) doesn't expand as you think. echo !(/app/dirs/dir3) shows that it includes dir3. If the current directory is /app/dirs, you can use /app/dirs/!(dir3) to get what you want.
shopt -s extglob # needed for the extended wildcard
cd /app/dirs
cp -as /app/dirs/!(dir3) dir3
This creates
/home/barmar/test.dir
├── dir1
├── dir2
├── dir3
│   ├── dir1
│   ├── dir2
│   ├── dir4
│   └── dir5
│   ├── a.txt -> /home/barmar/test.dir/dir5/a.txt
│   ├── b.o -> /home/barmar/test.dir/dir5/b.o
│   └── c.txt -> /home/barmar/test.dir/dir5/c.txt
├── dir4
└── dir5
├── a.txt
├── b.o
└── c.txt

Related

Bash brace expansion - operand behavior

When using brace expansion with certain commands, the actual behavior differed from what I expected- a member within the brace was evaluated as an argument in the other brace member's expansion.
For instance,
$ mkdir -p {folder1,folder2,folder3}/{folderA,folderB,folderC}
Works as expected -
$ tree .
.
├── folder1
│   ├── folderA
│   ├── folderB
│   └── folderC
├── folder2
│   ├── folderA
│   ├── folderB
│   └── folderC
└── folder3
├── folderA
├── folderB
└── folderC
However, if we do
$ cp -r folder1/ folder2/{folderA,folderB}
Instead of folder1 being copied to both folder2/folderA and folder2/folderB, 'folderA' is interpreted as a second source. Thus we get -
.
├── folder1
│   ├── folderA
│   ├── folderB
│   └── folderC
├── folder2
│   ├── folderA
│   ├── folderB
│   │   ├── folder1
│   │   │   ├── folderA
│   │   │   ├── folderB
│   │   │   └── folderC
│   │   └── folderA
│   └── folderC
└── folder3
├── folderA
├── folderB
└── folderC
Can anyone explain why this is the case? I would have thought the above to be evaluated as -
$ cp -r folder1/ folder2/folderA
$ cp -r folder1/ folder2/folderB
Brace expansion doesn't result in multiple commands, it's just expanded in place in the original command. So the result is
cp -r folder1/ folder2/folderA folder2/folderB
When you get more than 2 arguments to cp, the last is the destination folder, the rest are source files and folders.
If you want multiple commands, you can use an explicit loop:
for dest in folder2/{folderA,folderB}; do
cp -r folder1/ "$dest"
done

How do you remove an entire directory except for certain subdirectories in linux?

Suppose I run the following command in linux:
$ mkdir -p mp3 jpeg/dir1 jpeg/dir2 txt
$ touch mp3/1.mp3 mp3/2.mp3 mp3/3.mp3
$ touch jpeg/1.jpeg jpeg/2.jpeg jpeg/3.jpeg
$ touch txt/1.txt txt/2.txt txt/3.txt
This will create a directory structure like:
├── jpeg
│ ├── 1.jpeg
│ ├── 2.jpeg
│ └── 3.jpeg
│ └── dir1
│ └── dir2
├── mp3
│ ├── 1.mp3
│ ├── 2.mp3
│ └── 3.mp3
└── txt
├── 1.txt
├── 2.txt
└── 3.txt
How do I invoke the linux "rm" command to remove everything in the "jpeg" directory except for "dir2" subdirectory?
So I'm looking for a command that looks something like:
rm -rf -not dir2 jpeg
But when I run that command on Centos 7, I get the following error message:
rm: invalid option -- 'n'
My target output directory structure should look like:
├── jpeg
│
│
│
│
│ └── dir2
├── mp3
│ ├── 1.mp3
│ ├── 2.mp3
│ └── 3.mp3
└── txt
├── 1.txt
├── 2.txt
└── 3.txt
Would appreciate all/any help from the linux scripting community
You can use this find command to delete everything in jpeg directory except dir2:
find jpeg -mindepth 1 -not -path 'jpeg/dir2' -prune -delete

Renaming files with the same names as directory - bash script

I want to rename my files so that they are name with the same name as the folder.
I have a main folder that has around 1000 folders. each of these folders have another file within it. in that very last folder, I have files with different extentions. and I want to rename the files that have pdb extention.
here's the strcuture of my folders :
pv----|
|--m10\ pk\ result0.pdb result1.pdb result2.pdb
|--m20\ pk\ result0.pdb result1.pdb result2.pdb
|--m30\ pk\ result0.pdb result1.pdb result2.pdb
I want something like this :
pv----|
|--m10\ pk\ m10_result0.pdb m10_result1.pdb m10_result2.pdb
|--m20\ pk\ m20_result0.pdb m20_result1.pdb m20_result2.pdb
|--m30\ pk\ m30_result0.pdb m30_result1.pdb m30_result2.pdb
that's the code I made but It's not working ..
for d in MD_PR2 / * / * /
do
(cd "$d" && for file in *.pdb ; do mv "$file" "${file/result/$d_result}" ; done)
done
my code is deleting "result" of each file's name and I don't know. it becomes 0.pdb , 1.pdb ..etc
thank you very much
Before:
user#pc:~$ tree
.
├── m10
│   └── pk
│   ├── result0.pdb
│   ├── result1.pdb
│   └── result2.pdb
├── m20
│   └── pk
│   ├── result0.pdb
│   ├── result1.pdb
│   └── result2.pdb
└── m30
└── pk
├── result0.pdb
├── result1.pdb
└── result2.pdb
Your code is not working because $d_result is being interpreted as a variable name, not as a concatenation of $d and _result. I suggest using ${d}_result.
However I would suggest another approach, one that doesn't need to cd into each directory.
Code:
shopt -s globstar
for file in **; do
if [[ "$file" =~ ".pdb" ]] ; then
mv "$file" `echo $file | sed -e 's/\(.*\)\/\(.*\)\/\(.*.pdb\)/\1\/\2\/\1_\2_\3/'`;
fi;
done;
After:
user#pc:~$ tree
.
├── m10
│   └── pk
│   ├── m10_pk_result0.pdb
│   ├── m10_pk_result1.pdb
│   └── m10_pk_result2.pdb
├── m20
│   └── pk
│   ├── m20_pk_result0.pdb
│   ├── m20_pk_result1.pdb
│   └── m20_pk_result2.pdb
└── m30
└── pk
├── m30_pk_result0.pdb
├── m30_pk_result1.pdb
└── m30_pk_result2.pdb
Code explanation:
shopt -s globstar: Allow for ** to be expanded into "all files and directories recursively"
Variable "file" contains filenames including directories
Check "file" against "$file" =~ ".pdb" to ignore working with directories
Generate newfilename with sed:
Search and replace: s/search/replace/
Find something like dir1/dir2/smthg.pdb: (.*)/(.*)/(.*.pdb)
Replace with dir1/dir2/dir1_dir2_smthg.pdb: \1/\2/\1_\2_\3 (replace with \1_\2_\3 if you also want to move renamed files into parent dir)
(I removed some backslashes for readability)
mv file to newfilename

Linux: Batch rename multiple files to parent dir + suffix in order of name

I need to batch rename multiple images and want to use the parent directory as base name. To prevent one overwriting the other, a suffix must be added. The order of the renaming process musts follow the timestamp of the file. Because the 'first' file is a featured image for the site I'm using it for.
Tree:
└── main
├── white tshirt
│   ├── IMG_1.jpg
│   ├── IMG_2.jpg
│   ├── IMG_3.jpg
│   └── IMG_4.jpg
├── black tshirt
│   ├── IMG_1.jpg
│   ├── IMG_2.jpg
│   ├── IMG_3.jpg
│   └── IMG_4.jpg
└── red tshirt
├── IMG_1.jpg
├── IMG_2.jpg
├── IMG_3.jpg
└── IMG_4.jpg
Goal:
└── main
├── white tshirt
│   ├── white-tshirt-1.jpg
│   ├── white-tshirt-2.jpg
│   ├── white-tshirt-3.jpg
│   └── white-tshirt-4.jpg
├── black tshirt
│   ├── black-tshirt-1.jpg
│   ├── black-thisrt-2.jpg
│   ├── black-tshirt-3.jpg
│   └── black-tshirt-4.jpg
└── red tshirt
├── red-tshirt-1.jpg
├── red-tshirt-2.jpg
├── red-tshirt-3.jpg
└── red-tshirt-4.jpg
Replacing spaces with dashes is not required, but preferred. Platform: Debian 8
I think this should do the job:
#!/bin/sh
for dir in *; do
if [ ! -d "$dir" ]; then
continue
fi
cd "$dir"
pref=$(echo "$dir" | tr ' ' -)
i=1
ls -tr | while read f; do
ext=$(echo "$f" | sed 's/.*\.//')
mv "$f" "${pref}-${i}.$ext"
i=$(expr $i + 1)
done
cd ..
done
Invoke the script inside your main directory and make sure there are only your target folders in it. Also make sure your files'names do not contain the character '\'

Linux/shell - Remove all (sub)subfolders from a directory except one

I've inherited a structure like the below, a result of years of spaghetti code...
gallery
├── 1
│   ├── deleteme1
│   ├── deleteme2
│   ├── deleteme3
│   └── full
│   ├── file1
│   ├── file2
│   └── file3
├── 2
│   ├── deleteme1
│   ├── deleteme2
│   ├── deleteme3
│   └── full
│   ├── file1
│   ├── file2
│   └── file3
└── 3
├── deleteme1
├── deleteme2
├── deleteme3
└── full
├── file1
├── file2
└── file3
In reality, this folder is thousands of subfolders large. I only need to keep ./gallery/{number}/full/* (i.e. the full folder and all files within, from each numbered directory within gallery), with everything else no longer required and needs to be deleted.
Is it possible to construct a one-liner to handle this? I've experimented with find/maxdepth/prune could not find an arragement which met my needs.
(Update: To clarify, all folders contain files - none are empty)
Using PaddyD answer you can first clean unwanted directories and then delete them:
find . -type f -not -path "./gallery/*/full/*" -exec rm {} + && find . -type d -empty -delete
This can easily be done with bash extglobs, which allow matching all files that don't match a pattern:
shopt -s extglob
rm -ri ./gallery/*/!(full)
How about:
find . -type d -empty -delete

Resources