I have a directory with a list of files in it. All these files have spaces in their names.

I would like to create a directory, for each file, with the name of the file except the extension (which is .doc for all of them).


My directory content

first file.doc 
second file.doc 
third file.doc

Expected result

first file/first file.doc 
second file/second file.doc
third file/third file.doc

I have this command to know the directory name I have to create:

find . -maxdepth 1 -name "*.doc" | awk -F ".doc" '{print $1}'

With this, the result is:

first file
second file
third file

I know need to create a directory per entry.

I've tried the following with no success:

find . -maxdepth 1 -name "*.doc" | awk -F ".doc" '{print $1}' -exec mkdir '{}' \;
#error with awk: it cannot open the file -exec because it does not exist

mkdir `find . -maxdepth 1 -name "*.doc" | awk -F ".doc" '{print $1}'`
#it creates several directories per name because there are spaces in the names

mkdir `"find . -maxdepth 1 -name "*.doc" | awk -F ".doc" '{print $1}'"`
#-bash: find . -maxdepth 1 -name *.doc | awk -F .doc '{print }': command not found

mkdir "`find . -maxdepth 1 -name "*.doc" | awk -F ".doc" '{print $1}'`"
#it tries to create a unique directory with all the file names as a single name, which is not good of course

If I could, I would just remove all the spaces in the file names, but I need to keep them for project constraints :/

Once I can create these directories, I will have to figure out how to move all the files in their matching directories. It all can be done in a single command, I'll take it :)

I'm using bash and RedHat 2.6.

Thanks for your help. Laurent

3 Answers 3


With zsh:

mkdir -p -- *.doc(:r)


for f (*.doc) {
  mkdir -p -- $f:r &&
    mv -- $f $f:r/

The bash equivalent (though it will also work in all POSIX shells and zsh):

for f in *.doc; do
  mkdir -p -- "${f%.*}" &&
    mv -- "$f" "${f%.*}/"

(note that it excludes hidden files).

  • This is a better answer than mine - it's pointless to use the |pipe and it resulted in a needless escape issue. I don't know why I didn't just use a for loop - it amounts to the same thing in this case, just less complicated.
    – mikeserv
    Apr 3, 2014 at 9:27
  • Better than mine too!
    – Josh Jolly
    Apr 3, 2014 at 10:11

Spaces in a file/directory name are fine as long as those names are properly quoted:

find . -maxdepth 1 -name "*.doc" | while IFS= read -r f; do 
    mkdir -- "${f%.doc}"; 
  • Thanks, that worked fine. I did te following for the move: while read f; do mv "$f" "${f%.doc}"/"$f"; done <<< "$(find . -maxdepth 1 -name "*.doc")"
    – Laurent C.
    Apr 3, 2014 at 8:59
  • That fails if filenames contain backslash or newline characters. Apr 3, 2014 at 9:00
  • Or get rid of the pipe and replace it by a loop. find just makes things more complex for no reason. @LaurentC. Apr 3, 2014 at 21:59
cd /path/to/dir
(   set -- *\ *
    printf 'd="%s" ; mkdir ./"${d%%.doc}"\n' "$@"
) | . /dev/stdin

You can set a subshell's parameters with shell globs and then feed a pipe with printf, and . source the pipe as a script.

And if you want to move the files in each directory:

cd /path/to/dir
(   set -- *\ *
    printf 'f="%s" ; d="${f%%.doc}"
        mkdir ./"$d" ; echo "mv ./\"$f\" ./\"$d\"/\"$f\""\n' "$@"
) | . /dev/stdin

Note: I've intentionally hamstrung the above with echo because I want you test the output before you dive-in.

FIXED - I forgot to use two %percents for printf.

I tested this, by the way:

% printf 'touch ./"%s file.doc"\n' first second third fourth fifth |
    . /dev/stdin
% ls
> fifth file.doc  first file.doc  fourth file.doc  second file.doc  third file.doc

% ( set -- *\ *
    printf 'f="%s" ; d="${f%%.doc}"
    mkdir ./"$d" ; mv ./"$f" ./"$d"\n' "$@"
) | . /dev/stdin

% ls
> fifth file  first file  fourth file  second file  third file

% cd fifth\ file ; ls
> fifth file.doc
  • Thank you for your reply. It is a bit complicated for me and I did not manage to do it (I tried before your edit). I however took inspiration for the move command :)
    – Laurent C.
    Apr 3, 2014 at 9:00
  • @LaurentC. No problem. It was a bonehead mistake and I'm sorry. The reason it didn't work before is I used the percent to strip the .doc from the tail of $d, but I forgot that if you use that in a printf statement you have to escape the %percent with another %percent because that's what printf uses for formatting. Sorry, man.
    – mikeserv
    Apr 3, 2014 at 9:12

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .