How to copy list of folders keeping the partial folder structure that of parent directory - linux

Say I want to copy some folders from find /40/AD/GWAS_data/Source_Plink/2021_ADGC_EOAD -name "plink_data*" which has these folders:
/40/AD/GWAS_data/Source_Plink/2021_ADGC_EOAD/ADGC_NHW/ADNI/TOPMEDr2/vcffile/plink_data
/40/AD/GWAS_data/Source_Plink/2021_ADGC_EOAD/ADGC_ASIAN/BIOCARD/TOPMEDr2/vcffile/plink_data
into /40/AD/GWAS_data/Source_Plink/2021_ADGC_EOAD/NEW_DIR/, but I want the new directory to have :
/40/AD/GWAS_data/Source_Plink/2021_ADGC_EOAD/NEW_DIR/ADGC_NHW/ADNI/TOPMEDr2/vcffile/plink_data
/40/AD/GWAS_data/Source_Plink/2021_ADGC_EOAD/NEW_DIR/ADGC_ASIAN/BIOCARD/TOPMEDr2/vcffile/plink_data
I tried this but it copies the whole path: find /40/AD/GWAS_data/Source_Plink/2021_ADGC_EOAD -name "plink_data*" -exec cp --parents {} /target \;
How do I go about doing it? Thanks!
UPDATE: I was able to perform my task with cp using answer from #Cyrus, but not with mv. I thought applying cp and mv in this command would not be any different, but I was wrong. In fact, I needed to use both mv and cp for different tasks, so I resorted to using a loop
for line in $(find . -name "*plink_data*"); do
new_FOLD="$(echo $line| cut -d"." -f2-)"
mkdir -p "NEW_DIR/${new_FOLD}"
cp/mv $line "NEW_DIR/${new_FOLD}"
done

I suggest:
cd /40/AD/GWAS_data/Source_Plink/2021_ADGC_EOAD
mkdir -p NEW_DIR
find . -name "plink_data" -not -path "./NEW_DIR/*" -exec cp --parent {} NEW_DIR \;

Related

Shell cp: cannot stat no such file or directory

I was trying to use cp to copy files from one directory to another by globing
for files in index/*
do
file=$(echo $files|cut -d'/' -f2)
cp -r "$files" ".target/file"
done
However, cp will give this warning if the directory is empty. I tried 2>/dev/null to mute this message but it did not work. I wonder how I could fix it.
What about this: (not tested)
find /index -maxdepth 1 -type f -exec cp {} .target/ \;
-maxdepth 1 : only look in this directory
-type f : only take the files
-exec cp {} .target/ \; : execute a "file copy" action

How to "rm -rf" with excluding files and folders with the "find -o" command

I'm trying to use the find command, but still can't figure out how to pipe the find ... to rm -rf
Here is the directory tree for testing:
/path/to/directory
/path/to/directory/file1_or_dir1_to_exclude
/path/to/directory/file2_or_dir2_to_exclude
/path/to/directory/.hidden_file1_or_dir1_to_exclude
/path/to/directory/.hidden_file2_or_dir2_to_exclude
/path/to/directory/many_other_files
/path/to/directory/many_other_directories
Here is the command for removing the whole directory:
rm -rf /path/to/directory
But how to rm -rf while excluding files and folders?
Here is the man help for reference:
man find
-prune True; if the file is a directory, do not descend into it. If
-depth is given, then -prune has no effect. Because -delete im‐
plies -depth, you cannot usefully use -prune and -delete to‐
gether.
For example, to skip the directory `src/emacs' and all files
and directories under it, and print the names of the other files
found, do something like this:
find . -path ./src/emacs -prune -o -print
What's the -o in this find command? Does it mean "or"? I can't find the meaning of -o in the man page.
mkdir -p /path/to/directory
mkdir -p /path/to/directory/file1_or_dir1_to_exclude
mkdir -p /path/to/directory/file2_or_dir2_to_exclude
mkdir -p /path/to/directory/.hidden_file1_or_dir1_to_exclude
mkdir -p /path/to/directory/.hidden_file2_or_dir2_to_exclude
mkdir -p /path/to/directory/many_other_files
mkdir -p /path/to/directory/many_other_directories
I have tried to use this find command to exclude the .hidden_file1_or_dir1_to_exclude and then pipe it to rm, but this command does not work as expected.
cd /path/to/directory
find . -path ./.hidden_file1_or_dir1_to_exclude -prune -o -print | xargs -0 -I {} rm -rf {}
The meaning of rm -rf is to recursively remove everything in a directory tree.
The way to avoid recursively removing everything inside a directory is to get find to enumerate exactly the files you want to remove, and nothing else (and then of course you don't need rm at all; find knows how to remove files, too).
find . -depth -path './.hidden_file1_or_dir1_to_exclude/*' -o -delete
Using -delete turns on the -depth option, which disables the availability of -prune; but just say "delete if not in this tree" instead. And indeed, as you seem to have discovered already, -o stands for "or".
The reason -delete enables -depth should be obvious; you can't traverse the files inside a directory after you have deleted it.
As an aside, you need to use -print0 if you use xargs -0. (This facility is a GNU extension, and generally not available on POSIX.)
You need to separate files from directories to exclude:
find . -mindepth 1\
\( -path ./dir_to_exclude -o\
-path ./.hidden_dir_to_exclude \) -type d -prune\
-o\
! \( -path ./file_to_exclude -o\
-path ./.hidden_file_to_exclude \)\
-exec echo rm -rf {} \;
You can remove the echo once tested.

Copy all directories except one

I'm copying all subdirectories with its contents to my current directory as follows:
cp -r dirToCopy/* .
But in the folder dirToCopy, there is one subfolder called dirNotToCopy which should not be copied.
How can I filter that particular folder out of my expression?
Use extended globbing:
shopt -s extglob
cp -r dirToCopy/!(dirNotToCopy) .
Well if you want to do it in single line:
find /path_to/dirToCopy -mindepth 1 -type d ! -name dirNotToCopy -exec cp -r {} . \;
One more way of doing the same.
find /path_to/dirToCopy -maxdepth 1 -type d ! -name dirNotToCopy -exec cp -r {} . \;
Instead of using mindepth suggested in the other answer, we should use maxdepth.
(I can't comment or edit another answer since I do not have enough reputation yet)
Also note that this command only copies the subdirectories, not the files in the directory.

Linux find folder and rename

I want to rename all .hg_gg folders in /var/www to .hg. How can I do it?
I know how to rename .hg to .hg_gg.
find /var/www -name ".hg" -exec bash -c 'mv $0 $0_gg' {} \;
but don't know how to make reverse change.
Try this:
find /var/www -name ".hg_gg" -execdir bash -c 'mv {} .hg' \;
You need to use a special syntax defined by find: {} is the placeholder for the current file name. Check the man page for that. Also it is important to use -execdir instead of -exec. execdir changes the current working directory to the folder where the found directory is located. Otherwise it would do something like this mv /var/www/.hg_gg ./.hg
You can speed up things a bit when restricting find to find folders only using -type d:
find /var/www -type d -name ".hg_gg" -execdir bash -c 'mv {} .hg' \;
Consider this find command with -execdir and -prune options:
find /var/www/ -type d -name ".hg_gg" -execdir mv '{}' '.gg' \; -prune
-execdir will execute the command in each subdirectory
-prune causes find to not descend into the current file
Not a one liner, but you could do this:
for file in `find /var/www -name ".hg_gg"`; do
mv $file `echo $file | sed 's/hg_gg$/hg/'`
done

How to find in linux and delete directories that not match a name? !#Bash

I need to delete unpacked directories from my /source tree keeping the others with .tar and .patch extensions,
how to do please?
This should work:
find . -not -name "*.tar" -not -name "*.patch" -type f -exec rm {} \;
This is using only one command not using pipes.
Note. This will proceed recursively into subdirectories. If this is unwanted, use the maxdepth switch:
find . -maxdepth 1 -not -name "*.tar" -not -name "*.patch" -type f -exec rm {} \;
BACKUP YOUR DIRECTORY FIRST, I HAVE NOT TESTED THIS, AND IT HAS A BUG AS NOTED IN THE COMMENTS.
WHILE IN THE ACTUAL /source DIRECTORY:
ls|fgrep -v -e .tar -e .patch|xargs rm -rf
You probably want to use the "put echo after xargs" trick to see what this would actually do, before running it:
ls|fgrep -v -e .tar -e .patch|xargs echo rm -rf

Resources