2

I have directory with the next structure:

example
├───1
│   └───01-01-2015.txt
├───2
│   └───02-02-2016.txt
├───3
│   └───03-03-2017.txt
└───script.sh

I'm trying to write a script which will find all .txt files and will create besides them a new folder with a name based on the name of .txt file in particular folder.

So result will be:

example
├───1
│   ├───01-01-2015
│   └───01-01-2015.txt
├───2
│   ├───02-02-2016
│   └───02-02-2016.txt
├───3
│   ├───03-03-2017
│   └───03-03-2017.txt
└───script.sh

So in folder "3" subfolder "03-03-2017", in folder "2" subfolder "02-02-2016" etc.

I understand that find all .txt files is pretty simple but can't figure out how to combine it with creating directory and performing substring for every search result.

Thanks in advance!!

3

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.

4
find example -name \*.txt | sed 's/\.txt$//' | xargs -n 1 mkdir

Warning: this requires that your paths do not contain newlines.

Edit: see terdon's comment if you are using GNU tools.

  • 3
    You can make it safe for newlines if you're using GNU tools with find example -name \*.txt -print0 | sed -z 's/\.txt$//' | xargs -0 -n 1 mkdir – terdon Jan 19 '17 at 18:05
  • Thank you, I did not know the -z option to GNU sed. – kmkaplan Jan 19 '17 at 18:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.