I would just use a bash loop for this:
for i in **/*.txt; do mkdir "${i%.txt}"; done
If I run this on your example, I get:
$ tree
.
├── 1
│ └── 01-01-2015.txt
├── 2
│ └── 02-02-2016.txt
└── 3
└── 03-03-2017.txt
3 directories, 3 files
$ shopt -s globstar
$ for i in **/*.txt; do mkdir "${i%.txt}"; done
terdon@oregano foo $ tree
.
├── 1
│ ├── 01-01-2015
│ └── 01-01-2015.txt
├── 2
│ ├── 02-02-2016
│ └── 02-02-2016.txt
└── 3
├── 03-03-2017
└── 03-03-2017.txt
6 directories, 3 files
globstar
is a bash option, explained in man bash
:
globstar
If set, the pattern **
used in a pathname expansion context will
match all files and zero or more directories and subdirectories.
If the pattern is followed by a /
, only directories and subdirectories match.
So, after enabling it with shopt -s globstar
, the pattern **/*.txt
will find all files (or dirs) whose name ends with .txt
.
The ${i%.txt}
is shell syntax to remove a substring. The general format is ${variable%string}
and it will remove the first instance of string
from the end of the variable. So, ${i%.txt}"
will be the file name (including parent directories), minus the .txt
. Therefore, [passing it to mkdir
will create the directory you want.
Personally, I find the syntax above much simpler, but here's how to do it with find
:
find . -name '*.txt' -exec sh -c 'mkdir ${0%.txt}' {} \;
Here, the -exec command {}
will replace {}
with each of the results of find
and then run command
on it. Since the command here is a call to sh -c
, the sh
will take the {}
as its zeroeth positional parameter, $0
. So, we then run the same shell substitution as explained above to create the directory.