Moving files into dirs corresponding to dates

I am trying to find a way to move files into corresponding date files.

i=0
while read line
do
    array[ $i ]="$line"
    (( i++ ))
done < <(ls)

cd  $(echo ${array[1]})
echo ${array[1]}}
pwd
#cd "$(array[1]}"
[[ -d 2015 ]] || mkdir 2015
cd "2015"
[[ -d 02-February ]] || mkdir 02-February
[[ -d 03-March ]] || mkdir 03-March
[[ -d 04-April ]] || mkdir 04-April
[[ -d 05-May ]] || mkdir 05-May
[[ -d 06-June ]] || mkdir 06-June
[[ -d 07-July ]] || mkdir 07-July
[[ -d 08-August ]] || mkdir 08-August
[[ -d 09-September ]] || mkdir 09-September
[[ -d 10-October ]] || mkdir 10-October
[[ -d 11-November ]] || mkdir 11-November
[[ -d 12-December ]] || mkdir 12-December
cd ".."

Here is where I want to move the files. I can make it work by using something like this while in directory that contains the files:

for file in *; do
if [[ "$file" =~ 03-[0-9][0-9]-2015 ]]; then
varx=`echo $file|head -1|cut -d- -f 4|sed 's/.txt//g'`;
vary=`echo $file|head -1|cut -d ' ' -f 2|sed 's/-.*//'`;
jx=$(varz=`echo $vary`; if [[ $varz =~ $vary ]]; then echo $varz-March;fi)
echo mv "$file" /tmp/Safe\ Dirs/${array[1]}/$varx/$jx
fi
done

However, I need to to go through every directory that has date files and move them into the /tmp/Safe\ Dirs/subdir1 subdir2 subdir3 etc folders.

So we have

 /tmp/Safe\ Dirs/subdir1
/tmp/Safe\ Dirs/subdir2
/tmp/Safe\ Dirs/subdir3
/tmp/Safe\ Dirs/subdir4

and every file that corresponds to a certain month needs to be moved to the 2015/02-February etc. folder
This works, but I have to indicate the index of the array in each statement. I'm not sure how to move the files that are in each directory into the directories represented by the element of the array.

So these are a sample of the directories:

/tmp/Safe\ Dirs/vault/file-02-12-15 
/tmp/Safe\ Dirs/vault/file-03-15-15
/tmp/Safe\ Dirs/lock/file-02-12-15 
/tmp/Safe\ Dirs/lock/file-03-18-15

These need to be moved into:

/tmp/Safe\ Dirs/vault/02-February
/tmp/Safe\ Dirs/vault/03-March

and

/tmp/Safe\ Dirs/lock/02-February
/tmp/Safe\ Dirs/lock/03-March

So what happens is that I need to use these statements, etc.

 mv "$file" /tmp/Safe\ Dirs/${array[1]}}/$varx/$jx
 mv "$file" /tmp/Safe\ Dirs/${array[2]}}/$varx/$jx
 mv "$file" /tmp/Safe\ Dirs/${array[3]}}/$varx/$jx

I'm sure there is a much easier way to do this. Any suggestions?

cant you use like this

 cp /tmp/Safe\ Dirs/vault/file-02* /tmp/Safe\ Dirs/vault/02-February 

What shell are you using?

As a comment: spaces in file names or directory names are a problem waiting to happen.
Also, /tmp is not meant for long term storage and is often implemented as part of RAM for performance reasons. Consider parking data files elsewhere unless these are going away soon.

Depending on your shell, parameter substitution can make your code work in just a few lines.

To make it simpler - I want to use an array to cd to different directories. I do want it to be in array format.

#! /bin/bash
i=0
while read line
do
    array[ $i ]="$line"
    (( i++ ))
done < <(ls)

I have tried various ways, I just get "directory not found." I assume this is because cd is a built in command. However, if I do this, I do get the directory to change

#! /bin/bash

i=0
while read line
do
    array[ $i ]="$line"
    (( i++ ))
done < <(ls)

cd  $(echo ${array[1]})
echo ${array[1]}}
pwd

Are there any ideas of how I can get it to run? At this point I can get this to work by doing cd $(echo ${array[1]}) cd $(echo ${array[2]}) cd $(echo ${array[3]}) and then typing the command below that, etc. But there are 100 elements of the array and this is too much work to cd each time.

This in bash

declare -a array=( $(ls) )

does what this tries to do:

i=0
while read line
do
    array[ $i ]="$line"
    (( i++ ))
done < <(ls)

This

cd  $(echo ${array[1]})

should be:

cd  ${array[1]}

To print the whole array:

for(( i=0; i< ${#array[*]}; i++ ))
do
   echo ${array}
done

That should get you further down the road.

In both bash and a 1993 or later version of ksh :

declare -a array=( $(ls) )

can be simplified to just:

array=( * )

and I would strongly suggest changing:

   echo ${array}

to:

   printf '%s\n' "${array}"

to be safe in cases where a filename in the current directory:

  • starts with a hyphen,
  • contains one or more backslash characters, or
  • contains one or more whitespace characters.
1 Like

Yes - quoting a variable is alwayds the best idea. Thanks. However I'm not that sure about globbing is a better choice than ls. If that were the case, then the use of simple ls would be completely obviated. IMO.

Consider a directory containing the following files:

-rwxr-xr-x  1 dwc  staff  1361 Jun 25 13:06 Scrutinizer
-rw-r--r--  1 dwc  staff     0 Jun 25 14:14 a b
-rwxr-xr-x  1 dwc  staff   326 Jun 25 14:12 tester

And assume that tester contains the script:

#!/bin/bash
array=( * )
printf 'There are %d elements in array.\n' ${#array[@]}
for((i = 0; i < ${#array[@]}; i++))
do	printf 'array[%d]:"%s"\n' $i "${array}"
done
array=( $(ls) )
printf 'There are %d elements in array.\n' ${#array[@]}
for((i = 0; i < ${#array[@]}; i++))
do	printf 'array[%d]:"%s"\n' $i "${array}"
done

And note the difference in the output produced by running ./tester when * and $(ls) are used to initialize the array, respectively:

There are 3 elements in array.
array[0]:"Scrutinizer"
array[1]:"a b"
array[2]:"tester"
There are 4 elements in array.
array[0]:"Scrutinizer"
array[1]:"a"
array[2]:"b"
array[3]:"tester"

Adding a command substitution to the array initializer makes the shell lose track of filename boundaries.

The ls command with no arguments is great as an interactive tool. In a shell script, it is seldom needed and occasionally leads to the wrong results.

Point taken - thank you!

Thanks for all your responses, they were really helpful!

I was also hoping to run this kind of a command in the array:

for i in `ls -d */  ; do (cd $i; echo $i && ls);done

What this does is it cds to all of the directories and then prints out the files within them.

Is this also possible using the array created that contains all the directories? I have been unable to get the array to perform what the above command does.

First, note that the command substitution marked in red above is incomplete (no closing quote). Second, there is no need for a command substitution there at all. Third, unless you are doing this in a directory where you know beforehand that there is no possibility of whitespace characters or characters that are special in a filename matching pattern in a directory name, you need to quote your uses of the loop variable. And, finally unless you know that there aren't any backslash characters in your directory names and that none of them start with a hyphen, printf is safer than echo . So, I would change the above code to:

for i in */; do (cd "$i"; printf '%s\n' "$i" && ls);done

And, if you have an array of directories to process, try:

dirs=( */ )
for i in "${dirs[@]}"; do (cd "$i"; printf '%s\n' "$i" && ls);done

If I was writing this for my personal use, I'd probably change the printf format string to '\n%s\n' to add a little visual separation between directory listings. But, since I don't mind also have a colon added to the ends of the lines denoting directories, I would replace these loops with:

ls */
 and
dirs=( */ )
ls "${dirs[@]}"

respectively.