3

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).

E.g.:

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

7

With zsh:

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

Or:

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%.*}/"
done

(note that it excludes hidden files).

2
  • 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
    Commented Apr 3, 2014 at 9:27
  • Better than mine too!
    – Josh Jolly
    Commented Apr 3, 2014 at 10:11
4

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}"; 
done
3
  • 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.
    Commented Apr 3, 2014 at 8:59
  • That fails if filenames contain backslash or newline characters. Commented 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. Commented Apr 3, 2014 at 21:59
2
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
2
  • 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.
    Commented 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
    Commented 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 .