3

In a folder, I have thousand of files, such as:

cat eats mouse.jpg
cat chases mouse.jpg
gorilla eats banana.jpg
gorilla is sleeping.jpg
elephant is huge.jpg
elephant has trunk.jpg
(and so on...)

I want a bash script that will move these files based on the first word in the file name (cat, gorilla, elephant, etc) into folders, also created based on the first word, so that:

  • 'cat' folder is created and all files that begin with 'cat' are moved into this folder.
  • 'gorilla' folder is created and all files that begin with 'gorilla' are moved into this folder.
  • 'elephant' folder is created and all files that begin with elephant are moved into this folder.
  • (And so on..)

2 Answers 2

7

The following oneliner should do the job:

find . -maxdepth 1 -type f -name '*.jpg' -exec perl -e '$a="{}"; $a =~ /(\w+)/; `mkdir -p $1 && mv "$a" $1`' \;

Explanation

  • find . -maxdepth 1 -type f -name '*.jpg' will look for files (-type f) only in the current folder (. -maxdepth 1) with the jpg extension.

  • -exec will call the perl command for every matching files. The string {} is replaced by the current file name being processed.

  • $a="{}"; saves the current file

  • $a =~ /(\w+)/ is a regular expression test, the special variable $1 will contain the first word of the filename stored in $a.

  • finally mkdir -p $1 && mv "$a" $1 is executed as a system command with /bin/sh or its equivalent. Note the -p option of mkdir, it won't fail if the directory already exists.

enter image description here


Update

To prevent code injection in perl (thanks @geirha), use the quotemeta function as follow:

find . -maxdepth 1 -type f -name '*.jpg' -exec perl -e '$a="\Q@ARGV"; $a =~ /(\w+)/; `mkdir -p $1 && mv $a $1`' "{}" \;
5
  • 1
    "I have minimal knowledge in scripting, so be easy on me." can you explain for him/her? Thanks Commented Oct 18, 2014 at 5:40
  • @KasiyA: Done, sorry I was a bit in a hurry on Friday. Commented Oct 19, 2014 at 14:32
  • I know, all we can not reply quickly and I didn't downvote, but +1 Thanks Commented Oct 19, 2014 at 15:11
  • 1
    Note that it fails if the filename contains " quotes. This is because you inject data into code, which is something you generally want to avoid (think Bobby Tables). Even worse, it may run arbitrary code by specially crafted filenames: e.g. if you run touch 'dog ".`mktemp`.".jpg' before that find command, mktemp will actually be run.
    – geirha
    Commented Oct 19, 2014 at 18:23
  • @geirha: Thanks a lot for this warning. I've updated my answer to avoid this problem. Commented Oct 19, 2014 at 21:51
4

I would use a for loop for this.

for file in ./*" "*.jpg; do
    word=${file%% *}
    mkdir -p "$word" &&
    mv "$file" "$word"
done

You can run this in an interactive shell, or put it in a script. Bash is very useful to know, so I recommend learning it by reading the Bash Guide when you have the time.

You must log in to answer this question.

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