delete specific files and retain some - linux

so I have this directory that includes these .js and .yml files and one folder named as config
pogi#gwapo-pah:~$ ls
index.cat.js
index.bird.js
index.dog.js
index.monkey.js
function.yml
config
I would like to execute a one-liner bash command that would perform these
find if "index.dog.js" exists, and if none then exit
find if "index.dog.js" exists, and if present then remove only the
other *.js files and retain index.dog.js, function.yml and the folder config
if command is success then the files from folder shold look like this:
index.dog.js
function.yml
config
these is so far I tried however I'm not able to continue the missing logic
if [ -f index.dog.js ] ; then echo 'exists' ; fi

shopt -s extglob
[[ -f index.dog.js ]] && rm !(index.dog).js

Another way using find command:
[ -f "index.dog.js" ] && find . -maxdepth 1 -name \*.js -not -name index.dog.js -delete
find command search in current directory any file with js extension but index.dog.js and remove it.
replace . with folder name if you are not inside directory where are file.

Test if "index.dog.js" exists, if it does, use find to yield all *.js files (but not index.dog.js), and delete them.
EDIT As John Kugelman correctly advises, best to avoid ls due to possible inconsistencies with it.
[ -f "index.dog.js" ] && \
find . -type f -not -name "index.dog.js" -name \*.js -exec rm {} +

test -f index.dog.js && find . -name \*.js -not -name index.dog.js -exec rm {} +
Explanation:
test is a way to do if without all the extra syntax, if you don't need the else.
&& is the "short circuit" (exit) you want if there is no dog file.
find looks for files using multiple criteria. In this case files whose name match *.js but are not the dog file.
find can then execute a command against the found files. The {} is a stand-in for the found files. The + means put all the filenames on one rm command, rather than running one command per file.

Related

Unix shell loop to check if a directory exists inside multiple directories

I have folder structure like this:
/home/
/folder1/
/backup/
/folder2/
/backup/
/folder3/
/folder4/
/backup/
/folder5/
(As you can see, no all directories "folder" have a directory "backup")
I need to check if the directory "backup" exists in the "folder"s and delete it.
I am using this command:
for d in /home/* ;
do [ -d "$d/backup" ]
&& echo "/backup exists in $d"
&& rm -rf "$d/backup"
&& echo "/backup deleted in $d" ;
done
But it is not working. Please help.
find . -type d -name "backup" -delete -print
Obviously, all content under backup directories will be lost.
This will recurse down into your directories. If you need to limit it to only the first level, you can do:
find . -maxdepth 1 -type d -name "backup" -delete -print
Both commands will print the deleted directories. No output == no directory found, nothing done.
Lastly, you want to avoid looping on files or directory names like you attempted, since you might have files or directories with spaces in their names. A complete discussion and solutions are available here: https://mywiki.wooledge.org/BashFAQ/001

Moving files with a pattern in their name to a folder with the same pattern as its name

My directory contains mix of hundreds of files and directories similar to this:
508471/
ae_lstm__ts_ 508471_detected_anomalies.pdf
ae_lstm__508471_prediction_result.pdf
mlp_508471_prediction_result.pdf
mlp__ts_508471_detected_anomalies.pdf
vanilla_lstm_508471_prediction_result.pdf
vanilla_lstm_ts_508471_detected_anomalies.pdf
598690/
ae_lstm__ts_598690_detected_anomalies.pdf
ae_lstm__598690_prediction_result.pdf
mlp_598690_prediction_result.pdf
mlp__ts_598690_detected_anomalies.pdf
vanilla_lstm_598690_prediction_result.pdf
vanilla_lstm_ts_598690_detected_anomalies.pdf
There are folders with an ID number as their names, like 508471 and 598690.
In the same path as these folders, there are pdf files that have this ID number as part of their name. I need to move all the pdf files with the same ID in their name, to their related directories.
I tried the following shell script but it doesn't do anything. What am I doing wrong?
I'm trying to loop over all the directories, find the files that have id in their name, and move them to the same dir:
for f in ls -d */; do
id=${f%?} # f value is '598690/', I'm removing the last character, `\`, to get only the id part
find . -maxdepth 1 -type f -iname *.pdf -exec grep $id {} \; -exec mv -i {} $f \;
done
#!/bin/sh
find . -mindepth 1 -maxdepth 1 -type d -exec sh -c '
for d in "$#"; do
id=${d#./}
for file in *"$id"*.pdf; do
[ -f "$file" ] && mv -- "$file" "$d"
done
done
' findshell {} +
This finds every directory inside the current one (finding, for example, ./598690). Then, it removes ./ from the relative path and selects each file that contains the resulting id (598690), moving it to the corresponding directory.
If you are unsure of what this will do, put an echo between && and mv, it will list the mv actions the script would make.
And remember, do not parse ls.
The below code should do the required job.
for dir in */; do find . -mindepth 1 -maxdepth 1 -type f -name "*${dir%*/}*.pdf" -exec mv {} ${dir}/ \;; done
where */ will consider only the directories present in the given directory, find will search only files in the given directory which matches *${dir%*/}*.pdf i.e file name containing the directory name as its sub-string and finally mv will copy the matching files to the directory.
in Unix please use below command
find . -name '*508471*' -exec bash -c 'echo mv $0 ${0/508471/598690}' {} \;
You may use this for loop from the parent directory of these pdf files and directories:
for d in */; do
compgen -G "*${d%/}*.pdf" >/dev/null && mv *"${d%/}"*.pdf "$d"
done
compgen -G is used to check if there is a match for given glob or not.

Find pattern of the file, create a folder with that pattern and copy the files to that folder - Bash script

I have a task, to find the pattern of the file, create a folder with the pattern name and copy the file to that folder. I am able to create the folders.
folders=`find /Location -type f -name "*.pdf" -printf "%f\n" | cut -f 1 -d '_' | sort -u`
for i in $folders
do
mkdir -p /LocationToCreateTheFolder/$i
done
Not able to go further on how to copy the files.
maybe try?
for i in $folders do mkdir -p /LocationToCreateTheFolder/$i && cp ./$i.pdf ./$i/
This will do the finding and the copying:
find Location -type f -name '*.pdf' -exec bash -c 'f=${1##*/}; d="LocationToCreateTheFolder/${f%%_*}"; mkdir -p "$d" && cp "$1" "$d"' None {} \;
This is safe for difficult file names even ones that contain spaces, tabs, or newlines in their names.
How it works
find Location -type f -name '*.pdf' -exec bash -c '...' None {} \;
This will find the pdf files under directory Location and, for each one found, the bash commands inside '...' will be executed with $1 set to the name of the file found. ($0 is set to None. We don't use $0.)
f=${1##*/}
This removes the directory names from the name of the file. This is an example of prefix removal: everything in $1 up to and including the last / is removed.
d="LocationToCreateTheFolder/${f%%_*}"
This creates the name of the directory to which we want to send the file.
${f%%_*}" is an example of suffix removal. Everything in $f from the first _ and after is removed.
mkdir -p "$d" && cp "$1" "$d"
This makes sure that the directory exists and then copies the file to it.

Find and delete file but not specific path

I am writing a script to cleanup user dir on "/srv". At present every user keeps some temp files on "/srv/$USER".
Following is my script :
for x in $(cut -d: -f1 /etc/passwd); do
if [ -d "/srv/${x}" ]; then
echo "/srv/${x}"
find /srv/${x} -mindepth 1 -type f -not -amin -10080 -exec rm {} \;
fi
done
So I tried this script replacing rm with ls
/srv/abc
/srv/abc/2015-04-20-11-multi-interval.json
/srv/abc/2015-04-20-10-mimic.json
/srv/xyz
/srv/xyz/magnetic_hadoop/fabfile.py
here i want to exclude /srv/abc which is parent dir and delete only files, So I added -mindepth 1, but still I didn't get what I want.
Then I added -not -path /srv/${x} but no difference.
Anyone know what am I missing here ?
Thanks
the '-type f' means that you will get only files. and your output shows that: after the folder name which comes from the echo command, only files are shown.
unless you want to leave user folders intact, you don't want the '-mindepth 1' option; it does not change the fact that '-type f'

bash on Linux, delete files with certain file extension

I want to delete all files with a specific extension - ".fal", in a folder and its subfolders, except the one named "*Original.fal". The problem is that I want to delete other files that have the same extension:
*Original.fal.ds
*Original.fal.ds.erg
*Original.fal.ds.erg.neu
There are other ".fal"s that I want to delete as well, that don't have "Original" in them.
Names vary all the time, so I can't delete specific names. The *Original.fal doesn't vary.
I can only get up to here:
$find /disk_2/people/z183464/DOE-Wellen -name "*.fal" \! -name "*Original.fal" -type f -exec echo rm {} \;
It would be great if the command can delete only in the folder (and it's subfolders) where it has been called (executed)
When I run the code it gives me an error:
/disk_2/people/z183464/DOE-Wellen: is a directory
If you do not want find to dive too deep, you can restrict it with -maxdepth.
You can use a simple for loop for that. This command shows all the files you might want to delete. Change echo with rm to delete them.
cd /disk_2/people/z183464/DOE-Wellen && for I in `find . -name "*.fal" ! -name "*Original.fal"`; do echo $I; done
With "find ... | grep ..." you can use regex too, if you need more flexibility.

Resources