A folder flattening script for your files. Takes files and folders in a location & makes the folder tree less deep (if files have same name it will rename a file so that you dont lose any files). can make it even have no subfolders (SS=1). This can be useful to reorganize your media folders (pictures, videos, documents, etc). Dont ever run this on a folder where applications exist as applications rely on their folder structures.

First lets see an example:

So if you flatten the following to keep 1 subfolder deep (SS=2). The subfolder name that is created will be the folder/path right before (to the left) of the filename.

Given the files:
/dst/folder1/folder1/file1.txt
/dst/folder1/folder1/file2.txt
/dst/folder1/folder1/file3.txt
/dst/folder1/folder1/file4.txt
/dst/folder1/folder2/folder1/file.txt
/dst/folder1/folder2/folder1/file2.txt
/dst/folder1/folder2/folder2/file1.txt
/dst/folder1/folder2/file.txt
/dst/folder1/folder3/file2.txt

########
# SS=2 #
########

Lets flatten to the last 1 subfolder (SS=2) and copy contents to /somewhere/

First cd into /dst/folder1, so now the files on the source look like this

cd /dst/folder1
SET variable SS=2
SET variable DSTF="/somewhere/"
SET variable DRY=0 to run the script (to test it set DRY=1)
Then Copy Paste the script

./folder1/file1.txt -> /somewhere/folder1/file1.txt
./folder1/file2.txt -> /somewhere/folder1/file2.txt
./folder1/file3.txt -> /somewhere/folder1/file3.txt
./folder1/file4.txt -> /somewhere/folder1/file4.txt
./folder2/folder1/file.txt -> /somewhere/folder1/file.txt
./folder2/folder1/file2.txt -> /somewhere/folder1/file2-1234.txt
./folder2/folder2/file1.txt -> /somewhere/folder2/file1.txt
./folder2/file.txt -> /somewhere/folder2/file.txt
./folder3/file2.txt -> /somewhere/folder3/file2.txt

/somewhere/folder1/file1.txt
/somewhere/folder1/file2.txt
/somewhere/folder1/file3.txt
/somewhere/folder1/file4.txt
/somewhere/folder1/file.txt
/somewhere/folder1/file2-1234.txt
/somewhere/folder2/file1.txt
/somewhere/folder2/file.txt
/somewhere/folder3/file2.txt

NOTE: notice that ./folder2/folder1/file2.txt got appended -1234, because that file already exists. the -1234 in my script is the current epoch date, so it will be like /somewhere/folder1/file2-1500000000 or somewhere along those lines.

Note that you simply need to first "cd" into a folder thats far enough back. I could of "cd"ed into /dst and got the same results (granted the above listed files are the only files)

cd /dst/
SET variable SS=2
SET variable DSTF="/somewhere/"
SET variable DRY=0 to run the script (to test it set DRY=1)
Then Copy paste the script

/dst/folder1/folder1/file1.txt -> /somewhere/folder1/file1.txt
/dst/folder1/folder1/file2.txt -> /somewhere/folder1/file2.txt
/dst/folder1/folder1/file3.txt -> /somewhere/folder1/file3.txt
/dst/folder1/folder1/file4.txt -> /somewhere/folder1/file4.txt
/dst/folder1/folder2/folder1/file.txt -> /somewhere/folder1/file.txt
/dst/folder1/folder2/folder1/file2.txt -> /somewhere/folder1/file2-1234.txt
/dst/folder1/folder2/folder2/file1.txt -> /somewhere/folder2/file1.txt
/dst/folder1/folder2/file.txt -> /somewhere/folder2/file.txt
/dst/folder1/folder3/file2.txt -> /somewhere/folder3/file2.txt

########
# SS=1 #
########

All of the given files will just go to /somewhere/, no subfolders will be made in /somewhere/. files with similar names will be renamed so that you dont lose any files. It just appends a number or something to the filename (not to the extension, extensions are preserved) to make a new name.

/somewhere/file1.txt
/somewhere/file2.txt
/somewhere/file3.txt
/somewhere/file4.txt
/somewhere/file.txt
/somewhere/file2-1234.txt
/somewhere/file1-1234.txt
/somewhere/file-1234.txt
/somewhere/file2.txt

 Here are the details:

First get into the SRC Directory, then Read the comments, and Set the Variables (I recommend running DRY first to see what happens by setting DRY=1 and then when satisfied run with DRY=0 so that the copies happen)

cd /SourceDirectory

Then once the script below looks good copy paste it into the bash.

Script:

CopyPasteable Script. Source is Current Working Directory. Destination is DSTF variable. SS is the level of folders (1 is no subfolders, 2 is onelayer of subfolders). So cd into the source directory where all of the files & folders that you want flattened & then set the DSTF variable & SS variable (by default its 1 so that everything just goes to your DSTF folder, and if thats what you want keep SS the same).

(# THIS PASTEABLE SCRIPT - FLATTENS FOLDERS BY COPYING CONTENTS OF A FOLDER INTO ANOTHER BUT ONLY KEEPING SO MUCH OF THE FOLDER PATH (BY LOOKING AT THE SLASHES). FILES BEING COPIED COULD ALREADY EXIST WITH SAME NAME, SO THERE IS A RENAMER THAT APPENDS THE CURRENT DATETIME (UNIX EPOCH TIME). SOURCE FOLDER IS UNHARMED (AS LONG AS DESTINATION FOLDER IS NOT IN THE SAME DIR TREE AS THE SOURCE). DESTINATION FOLDER IS AUTOCREATED BY THE SCRIPT SO YOU DONT HAVE TO MAKE IT BEFORE HAND. JUST SET THE VARIABLES TO RUN THE APP:
# The SOURCE directory is current working directory. There is no SOURCE variable as its just the current working directory (So "cd" into the directory first)
# SS is the variable that sets how many folder levels to keep after flattening
# DSTF is where to copy flattened stuff (this should be in another location on the filesystem that is not within the SOURCE). Also the DSTF should not somewhere recursively within the SOURCE
# To run dry run (so no mkdirs or copys are done - its a readonly script at that point) put DRY=1, if you want the script to perform the flattening then leave it as DRY=0 (this will run the mkdirs and copys - and it this point its a script that does write to a filesystem)
# NOTE: DSTF should not be in the same directory tree as the source (current dir) - there is no check in the app, so do it yourself.
# NOTE: SRC should not be in DSTF as well for similar reasons
# NOTE: SS variable works like this:
# SS of 1 means you will have 0 layer of subfolders within the DSTF/destination folder - all files will be copied directly to DSTF (Expect alot of renames):
# /DSTF/files
# SS of 2 means you will have 1 layer of subfolders within the DSTF/destination folder:
# /DSTF/subfolder1/files
# SS of 3 means you will have 2 layers of subfolders within the DSTF/destination folder:
# /DSTF/subfolder1/subfolder2/files
# -- variables we set -- #
SS=1; 
DSTF="/somewhere/";
DRY=0;
# -- the program -- #
OIFS=$IFS; IFS=$'\n'; N=0; RCNT=0; # setting vars
for i in `find -type f`; do
N=$((N+1)) # file counter
SRC1=`echo $i` # source file
# new filename after flattening
DST1=`echo $SRC1 | awk -F/ -v s="$SS" -v df="$DSTF" '{gsub("%","%%",$0);while(substr(df, length(df)) == "/"){df=substr(df, 0, length(df))};l=NF-s+1; printf df; for(i=l;i<=NF;i++){p=i;if(p>1){printf "/" $p;}}; printf "\n";}'`
echo "* $N $SRC1 --> $DST1"
# check if dir exists and make it if needed
DDIR=`dirname $DST1`
if [[ ! -d $DDIR ]]; then
 if [[ $DRY -eq 0 ]]; then
   echo "   - making $DDIR"
   mkdir -p "$DDIR"
 fi
fi
# check if file exists, and if it does append date before extension (if no extension just append at end)
if [[ $DRY -eq 0 ]]; then
 while [[ -f "$DST1" ]]; do
    RCNT=$((RCNT+1))
     echo -n "   - file exists - renaming #${RCNT}: "
     BASENAME=`basename $DST1`
     C=`date +%s`
     if [[ $BASENAME == *"."* ]]; then
     EXTENSION="${DST1##*.}"
      FILENAME="${DST1%.*}"
      NEWFILENAME="$FILENAME-$C.$EXTENSION"
      echo " $DST1 -> $NEWFILENAME"
     else
      NEWFILENAME="$DST1-$C"
     echo " $DST1 -> $NEWFILENAME"
     fi
     DST1="$NEWFILENAME"
 done
 # at this point should have picked a filename that doesnt exist - the cmd below will copy
 (cp -p $SRC1 $DST1 2>&1 && echo "success" || echo "fail") | awk '{print "   " $0}'
fi
done
IFS=$OIFS
)

The end.

Note you can make your own edits to make this, executable as shell script, instead of a copy paste (I just use copy pastes alot in my day 2 day linuxing)

Note (requirements) this is tested with Bash4, but im sure it works with Bash3. also you need commands like basename (to get the filename), dirname (to get the dirname) and awk (which processes the new file name).

 

Leave a Reply

Your email address will not be published. Required fields are marked *