Bash - create subfolders for folders recently created - bash

I want to "join" these two tasks:
for dir in /blabla/bleble/*; do (cd "$dir" && mkdir -p Folder1/Folder1a && mkdir -p Folder2); done
and
find -amin -10
How can I do this?
I've tried this, but it doesn't work:
find -amin -2 -exec sh -c '
for dir in /blabla/bleble/*; do (cd "$dir" && mkdir -p Folder1/Folder1a && mkdir -p Folder2);
done' sh {} +

Something like this is might do:
find /a/b/ -mindepth 1 -maxdepth 1 -type d -amin -2 \
-exec sh -c 'for f; do mkdir -p -- "$f/Folder1/Folder1a" "$f/Folder2; done"' "" {} +
Breakdown:
-mindepth 1 -maxdepth 1 will limit result to current directory only.
-type d makes sure you only list directories
-exec foo "" {} + will execute foo with matches as arguments:
foo "" "/a/b/c" "/a/b/john" "a/b/doe"
for f will iterate over all positional arguments ($1, $2, ...)
for f; do
mkdir -p -- "$f/Folder1/Folder1a" "$f/Folder2"
done
Running sh -c 'code' arg1 arg2 will set $0 to arg1, and $1 to arg2, therefore the empty argument: foo "" {} +:
% sh -c 'echo $0' john
john

Assuming there aren't so many folders in /blabla/bleble that you overflow the command line, you can use find to search the target directory. -prune prevents recursing into the directories.
find /blabla/bleble/* -prune -type d -amin -10 -exec mkdir -p {}/Folder1/Folder1a {}/Folder2 \;
If you are using GNU find or another version that supports them, use -mindepth and -maxdepth instead to find the top-level subdirectories, no matter how many there are.
find /blabla/bleble -maxdepth 1 -mindepth 1 -type d -amin -10 -exec mkdir -p {}/Folder1/Folder1a {}/Folder2 \;

Related

Rename file if it is the only one with the extension in directory

This works however I would like to do it only if it is the only .jpg for the given directory, the one below will just rename them all to folder.jpg, overwriting the other files:
find . -type f -name '*.jpg' -execdir mv {} 'folder.jpg' \;
I guess find cannot filter by the number of matches, but you can always exec a shell which does more elaborate checks for you:
find . -type f -name '*.jpg' -execdir sh -c '[ $# = 1 ] && mv "$1" folder.jpg' sh {} +

Using command substitution in find -exec

How can I use command substitution in find … -exec … to avoid using xargs in the following command?
find -L -- /path/to/directory -mindepth 2 -maxdepth 2 -type d -exec dirname '{}' \; | xargs basename -a
I tried the following using command substitution, but it output . for each result instead of the desired output:
find -L -- /path/to/directory -mindepth 2 -maxdepth 2 -type d -exec basename "$(dirname '{}')" \;
Your first command will return strange results if a path contains whitespace.
Use a small shell script:
find -L -- . -mindepth 2 -maxdepth 2 -type d -exec sh -c 'basename "$(dirname "{}")"' \;
Alternative syntax to pass one path argument to the script:
find -L -- . -mindepth 2 -maxdepth 2 -type d -exec sh -c 'basename "$(dirname "$1")"' sh {} \;
Or pass as many arguments to the script as possible:
find -L -- . -mindepth 2 -maxdepth 2 -type d -exec sh -c '
for path do
basename "$(dirname "$path")"
done
' sh {} +
With GNU utilities it's possible to output NUL-terminated strings with dirname passed to xargs -0. The basename command is not run if there are no arguments (-r):
find -L -- . -mindepth 2 -maxdepth 2 -type d -exec dirname -z {} + | xargs -r0 basename -a

Deleting all the subfolders having fewer than X files

I would like to remove all the subfolders having less than X file in a folder
The following code search those subfolders with less than X file:
$ find . -type d -exec sh -c 'set -- "$0"/*; [ $# -le 10 ]' {} \; -print
./digna_1919
./digna_2040
./digna_1682
(more output omitted)
So I can find them! But if I do this, I get "Directory not empty":
$ find . -type d -exec sh -c 'set -- "$0"/*; [ $# -le 10 ]' {} \; -delete
find: cannot delete ‘./digna_1919’: Directory not empty
find: cannot delete ‘./digna_2040’: Directory not empty
find: cannot delete ‘./digna_1682’: Directory not empty
(more output omitted)
And if I do this, I get "No such file or directory":
$ find . -type d -exec sh -c 'set -- "$0"/*; [ $# -le 10 ]' {} \; -exec rm -r "{}" \;
find: ‘./digna_1919’: No such file or directory
find: ‘./digna_2040’: No such file or directory
find: ‘./digna_1682’: No such file or directory
(more output omitted)
Where am I doing wrong? Thanks a lot!
Source of the code
Use -depth to have find process each directory's contents before the directory itself. You'll want to do this any time you delete items.
$ find . -depth -type d -exec sh -c 'set -- "$0"/*; [ $# -le 10 ]' {} \; -exec rm -r {} \;

xargs how to put result {} into $(cmd {})?

for example:
find /usr/lib -maxdepth 1 -type l -iname "*libblas*"|xargs -I{} echo "{} =>" $(realpath {})
I would like it to output:
/usr/lib/libblas.so.3gf=>/usr/lib/libblas/libblas.so.3gf.0
/usr/lib/libblas.so=>/usr/lib/libblas/libblas.so.3gf.0
/usr/lib/libblas.a=>/usr/lib/libblas/libblas.a
This will not work because the value in $() is expanded before the script actual running.
Is there any way I can achieve this result? without loop in bash?
Alternatively:
find /usr/lib -maxdepth 1 -type l \
-exec echo -n '{} =>' \; \
-exec realpath '{}' \;
Have xargs call the shell:
find /usr/lib -maxdepth 1 -type l -iname "*libblas*"|xargs -I{} sh -c 'echo "{} =>" $(realpath {})'
You need the command substitution to happen after the file name is known. So you need xargs to call a shell and do the substitution there.
Since you're running the command on a single file at a time, using xargs is a useless complication (and it also mangles some file names). Use -exec!
find /usr/lib -maxdepth 1 -type l -iname "*libblas*" -exec sh -c 'echo "$0 => $(realpath "$0")' {} \;
You could make this slightly faster and not less clear by not using a command substitution:
find /usr/lib -maxdepth 1 -type l -iname "*libblas*" -exec sh -c 'echo -n "$0 => "; realpath "$0"' {} \;
To make things a little faster, don't invoke a new shell process for every file:
find /usr/lib -maxdepth 1 -type l -iname "*libblas*" -exec sh -c 'for x; do echo -n "$x => "; realpath "$x"; done' _ {} +
(You can do the same with xargs, but just drop xargs and stick to the simpler, faster, more robust -exec.)
Try to convert each filename separately using line-by-line "while" loop:
find ... | while read f; do echo "$f" '=>' "$(realpath $f)" ; done
The shortest seems to be using GNU Parallel:
find /usr/lib -maxdepth 1 -type l -iname "*libblas*"|parallel echo {} '=\> $(readlink -f {})'

modify shell script to delete folders as well as files

My shell script:
#!/bin/bash
if [ $# -lt 2 ]
then
echo "$0 : Not enough argument supplied. 2 Arguments needed."
echo "Argument 1: -d for debug (lists files it will remove) or -e for execution."
echo "Followed by some path to remove files from. (path of where to look) "
exit 1
fi
if test $1 == '-d'
then
find $2 -mmin +60 -type f -exec ls -l {} \;
elif test $1 == '-e'
then
find $2 -mmin +60 -type f -exec rm -rf {} \;
fi
Basically this will find files in a given directory provided as second argument and either list (-d for argument 1) or remove (-e for argument 1) files modified >60 minutes ago.
How can I rework this to also remove folders ?
Remove -type f
changing ls -l to ls -ld
Change 1 will list everything and not just files. This includes links as well. If you are not fine with listing/deleting anything other than files and directories then you need to separately list/delete files and directories as:
if test $1 == '-d'
then
find $2 -mmin +60 -type f -exec ls -ld {} \;
find $2 -mmin +60 -type d -exec ls -ld {} \;
elif test $1 == '-e'
then
find $2 -mmin +60 -type f -exec rm -rf {} \;
find $2 -mmin +60 -type d -exec rm -rf {} \;
fi
Change 2 is needed as ls -l on a directory will list the files in the directories.
#!/bin/bash
if [ $# -lt 2 ]
then
echo "$0 : Not enough argument supplied. 2 Arguments needed."
echo "Argument 1: -d for debug (lists files it will remove) or -e for execution."
echo "Followed by some path to remove files from. (path of where to look) "
exit 1
fi
if test $1 == '-d'
then
find $2 -mmin +60 -type d -exec ls -l {} \;
find $2 -mmin +60 -type f -exec ls -l {} \;
elif test $1 == '-e'
then
find $2 -mmin +60 -type d -exec rm -rf {} \;
find $2 -mmin +60 -type f -exec rm -rf {} \;
fi
That should work for you.

Resources