Merging and moving folders in loop - bash

I have a large library of music files stored as ./"Artist Name"/"Album Name"/"audio files".
I would like to re-organize to ./"Artist name --- Album name"/"audio files"
And be able to put it back to how it was.

This is your first loop
mkdir "ArtistName-Albumname"
cd "ArtistName/Albumname"
for filename in *; do
mv "$filename" "$ArtistName-Albumname/" ;;
done

I think something like this will get you started. It doesn't do anything but just parses your structure and works out what needs doing:
#!/bin/bash
find . -depth 2 -type d | while IFS= read p
do
p=${p:2} # Trim ./ from start
album=${p##*/} # album is everything after /
artist=${p%/*} # artist is everything before /
newloc="${artist} - ${album}"
echo Would move $artist/$album to ${newloc}
done
Sample output:
Would move artist1/album1 to artist1 - album1
Would move artist1/album2 to artist1 - album2
Would move artist1/album3 to artist1 - album3
Would move artist1/album4 to artist1 - album4
Would move artist2/album1 to artist2 - album1
Would move artist2/album2 to artist2 - album2
Would move artist2/album3 to artist2 - album3
Would move artist3/album1 to artist3 - album1
Would move artist3/album2 to artist3 - album2
Would move artist3/album3 to artist3 - album3
Would move artist4/album1 to artist4 - album1
Would move artist4/album2 to artist4 - album2
Would move artist4/album3 to artist4 - album3
Would move artist4/album4 to artist4 - album4
Would move artist4/album5 to artist4 - album5
The reverse operation is tricky as there may be a hyphen naturally occurring in an album name, so it will be hard to differentiate that from the hyphen introduced by the code below.

Here is the script to merge folder from "Artist Name"/"Album Name" to "Artist name - Album name"
#! /usr/bin/env bash
cd /PATH
find . -type f |while read -r line
do
file=${line##*/}
folder=${line%/*}
album=${folder##*/}
folder=${folder%/*}
artist=${folder##*/}
newfolder="$artist - $album"
mkdir -p "$newfolder"
echo mv "$line" "$newfolder"
done
If you understand above script, you should be fine to write a reverse one.

Took some time for an amateur. But here's my final sollution. Thank you so much for your input #mark #BMW.
function flatten() {
echo flattening...
ls -ld --format=single-column */* | while IFS= read albumpath
do
echo Flattening "$albumpath"
artist=${albumpath%/*} # artist is everything before /
echo Artist: "$artist"
echo Album: "$albumpath"
album=${albumpath##*/} # album is everything after /
newfolder="$artist --- $album"
echo Moving "$albumpath" to "$newfolder"
mv "$albumpath" "$newfolder"
done
find . -depth -type d -empty -delete #delete all empty (artist)folders
}
function unflatten() {
ls -ld --format=single-column */ | while IFS= read pathname
do
echo REVERSING "$pathname" ;
artist=${pathname% ---*} # artist is everything before " ---"
echo Artist: "$artist"
if [ ! -d "$artist" ];
then
echo Creating "$artist" folder
mkdir "$artist"
fi
album=${pathname##*--- } # album is everything after "--- "
album=${album%/*} # strip trailing /
echo Album: "$album"
echo Moving "$pathname" "$artist"/"$album"
mv "$pathname" "$artist"/"$album"
done
}

Related

bash - not finding value present in an array

I have an array of empty subdirectories and when I loop through all of the subdirectories I want to do something different depending on if it is in the array of empty subdirectories or not.
My issue is that I don't think my script is picking up on if a value is in the empty directories array. When I loop through the arrays and echo their values I can see that some values are in both arrays so I don't know why they aren't being picked up.
Code:
readarray empty_dirs < <(find /local/documents/guests/* -maxdepth 0 -empty)
for f in "${empty_dirs[#]}"
do
echo $f
echo "------------------------"
done
guest_path=/local/documents/guests/guest*
guest_arr=( $guest_path )
for dir in "${guest_arr[#]}"
do
echo "$dir"
if [[ " ${empty_dirs[#]} " =~ " ${dir} " ]];then
echo "--- $dir found ---"
fi
done
Output:
/local/documents/guests/guest10
------------------------
/local/documents/guests/guest12
------------------------
/local/documents/guests/guest15
------------------------
/local/documents/guests/guest18
------------------------
/local/documents/guests/guest20
------------------------
/local/documents/guests/guest1
/local/documents/guests/guest10
/local/documents/guests/guest11
/local/documents/guests/guest12
/local/documents/guests/guest13
/local/documents/guests/guest14
/local/documents/guests/guest15
/local/documents/guests/guest16
/local/documents/guests/guest17
/local/documents/guests/guest18
/local/documents/guests/guest19
/local/documents/guests/guest2
/local/documents/guests/guest20
/local/documents/guests/guest21
/local/documents/guests/guest22
/local/documents/guests/guest23
/local/documents/guests/guest24
/local/documents/guests/guest25
/local/documents/guests/guest3
/local/documents/guests/guest4
/local/documents/guests/guest5
/local/documents/guests/guest6
/local/documents/guests/guest7
/local/documents/guests/guest8
/local/documents/guests/guest9
The problem is ${empty_dirs[#]} contains the names including trailing newlines.
You can stop storing them using the -t option of readarray, or remove them before running the final loop:
empty_dirs=${empty_dirs[#]%$'\n'}

Bash/sh: Move Folder + subfolder(s) reclusively rename files if they exist [duplicate]

This question already has answers here:
Extract filename and extension in Bash
(38 answers)
Closed 1 year ago.
I'm trying to create a bash script that will move all files recursively from a source folder to a target folder, and simply rename files if they already exist. Similar to the way M$ Windows does, when a file exists it auto-renames it with "<filemame> (X).<ext>", etc. except for ALL files.
I've create the below, which works fine for almost all scenarios except when a folder has a (.) period in its name and a file within that folder has no extension (no period in its name).
eg a folder-path-file such as: "./oldfolder/this.folder/filenamewithoutextension"
I get (incorrectly):
"./newfolder/this (1).folder/filenamewithoutextension"
if "./newfolder/this.folder/filenamewithoutextension" already exist in the target location (./newfolder),
instead of correctly naming the new file: "./oldfolder/this.folder/filenamewithoutextension (1)"
#!/bin/bash
source=$1 ; target=$2 ;
if [ "$source" != "" ] && [ "$target" != "" ] ; then
#recursive file search
find "$source" -type f -exec bash -c '
#setup variables
oldfile="$1" ; osource='"${source}"' ; otarget='"${target}"' ;
#set new target filename with target path
newfile="${oldfile/${osource}/${otarget}}" ;
#check if file already exists at target
[ -f "${newfile}" ] && {
#get the filename and fileextension for numbering - ISSUE HERE?
filename="${newfile%/}" ; newfileext="${newfile##*.}" ;
#compare filename and file extension for missing extension
if [ "$filename" == "$newfileext" ] ; then
#filename has no ext - perhaps fix the folder with a period issue here?
newfileext="" ;
else
newfileext=".$newfileext" ;
fi
#existing files counter
cnt=1 ; while [ -f "${newfile%.*} (${cnt})${newfileext}" ] ; do ((cnt+=1)); done
#set new filename with counter - New Name created here *** Needs re-work, as folder with a period = fail
newfile="${newfile%.*} (${cnt})${newfileext}";
}
#show mv command
echo "mv \"$oldfile\" \"${newfile}\""
' _ {} \;
else
echo "Requires source and target folders";
fi
I suspect the issue is, how to properly identify the filename and extension, found in this line:
filename="${newfile%/}" ; newfileext="${newfile##*.}" which doesn't identify a filename properly (files are always after the last /).
Any suggestion on how to make it work properly?
UPDATED: Just some completion notes - Issues fixes with:
Initially Splitting each full path filename: path - filename - (optional ext)
Reconstructing the full path filename: path - filename - counter - (optional ext)
fixed the file move to ensure directory structure exists with mkdir -p (mv does not create new folders if they do not exist in the target location).
Maybe you could try this instead?
filename="${newfile##*/}" ; newfileext="${filename#*.}"
The first pattern means: remove the longest prefix (in a greedy way) up to the last /.
The second one: remove the prefix up to the first dot (the greedy mode seems unnecessary here) − and as you already noted, in case the filename contains no dot, you will get newfileext == filename…
Example session:
newfile='./oldfolder/this.folder/filenamewithoutextension'
filename="${newfile##*/}"; newfileext="${filename#*.}"
printf "%s\n" "$filename"
#→ filenamewithoutextension
printf "%s\n" "$newfileext"
#→ filenamewithoutextension
newfile='./oldfolder/this.folder/file.tar.gz'
filename="${newfile##*/}"; newfileext="${filename#*.}"
printf "%s\n" "$filename"
#→ file.tar.gz
printf "%s\n" "$newfileext"
#→ tar.gz

I want to name a file with the name of a directory in bash

I'm trying to create files with the name of it's antepenultimate directory:
Example:
Directory: a/b/c/d/e/f/g/h/i/j
The name of folder h is different for each case.
So I created an array
array=(/ a / b / c / d / e / f / g / * / * / *)
len=${#array[#]}
for (( q=0; q<$len; q++ ));
do
cd ${array[$q]}
sleep 1
mri_convert 0001*.dcm RAW.nii.gz #--->this line is just converting the format of file 0001*.dcm in to file RAW.nii.gz
done
This code is working but I want the file RAW.nii.gz to be named h_RAW.nii.gz
I tried doing this:
s1="${array%/*/*}"
$ echo "${s1##*/}"
and then:
mri_convert 0001*.dcm ${s1##*/}_RAW.nii.gz
but it's not working.
How about
cd /a/b/c/d/e/f/g
for dir in *; do
[[ -d $dir ]] || continue
for subdir in "$dir"/*/*/; do (
# doing this in a subshell so we don't need to "undo" this cd
cd "$subdir"
mri_convert 0001*.dcm "${dir}_RAW.nii.gz"
); done
done
Let's see if I can help. I'm not exactly sure of the details of what you're trying to do (mainly because the code you posted:
for (( q=0; q do cd ${array[$q]} sleep 1 mri_convert 0001*.dcm RAW.nii.gz
is not syntactically correct. So, that can't be what you're actually doing.
Just a hint of how i would approach a problem like this:
for path6 in /a/b/c/*/*/*
do
path5="${path6##*/}"
path4="${path5##*/}"
name4="${path4%/*}"
echo "Processing ${path4}"
mriconvert "${path6}"/0001*.dcm "${path6}/${name4}_RAW.nii.gz"
done

How to write every Nth file to new folder

I have this code which scans folders and moves all files in each folder to a new one.
How do I make it so only every Nth file is moved?
#!/bin/bash
# Save this file in the directory containing the folders (bb in this case)
# Then to run it, type:
# ./rencp.sh
# The first output frame number
let "frame=1"
# this is where files will go. A new directory will be created if it doesn't exist
outFolder="collected"
# print info every so many files.
feedbackFreq=250
# prefix for new files
namePrefix="ben_timelapse"
#new extension (uppercase is so ugly)
ext="jpg"
# this will make sure we only get files from camera directories
srcPattern="ND850"
mkdir -p $outFolder
for f in *${srcPattern}/*
do
mv $f `printf "$outFolder/$namePrefix.%05d.$ext" $frame`
if ! ((frame % $feedbackFreq)); then
echo "moved and renamed $frame files to $outFolder"
fi
let "frame++"
done
Pretty sure I need to edit the line for f in *${srcPattern}/* but not sure of the correct syntax
If files in the ND850 folders are sequential when listed (i.e. padded frame numbers), and the folders themselves are in order, then the following code should work.
#!/bin/bash
# Maintain a counter, and the output frame number
let "frame=1"
let "outframe=1"
outFolder="collected"
# frequency
gap=5
namePrefix="ben_timelapse"
#new extension (uppercase is so ugly)
ext="jpg"
srcPattern="ND850"
echo "Copying and renaming 1 in every $gap files"
mkdir -p "$outFolder"
for f in *${srcPattern}/*
do
if ! ((frame % $gap)); then
outfile=`printf "$outFolder/$namePrefix.%05d.$ext" $outframe`
cp $f "$outfile"
echo "copied $f to $outfile"
let "outframe++"
fi
let "frame++"
done
Try this instead of your mv command after do:
if ! ((frame % 5)); then
a=$((frame / 5));
mv $f `printf "$outFolder/$namePrefix.%05d.$ext" $a`
fi
It will move frame=5,10, and so on, to $outFolder/$namePrefix.00001.$ext,$outFolder/$namePrefix.00002.$ext, and so on

script to zip a file and change it's owenrships

I have a script I am working on that reads off of a text file and will use the information stored in the text file to put each line entered in as an array. This array is a reference to files that are imported to a directory in another script. The problem is i built a function to zip the contents of the directory and change it's ownerships, but when I run the script it was zipping and attempting to change ownerships of the pwd. Here is my code below:
file=~/exporttool/zipFiles.txt
index=0
declare -a studyinstanceuids
while read line ; do
studyinstanceuids[$index]="$line"
index=$((index+1))
echo $line
done < $file
for i in "${studyinstanceuids[#]}"
do
echo "$i" | ./cmd2;
done
echo "Exams are in!";
##Function with argument that will take prompt to change ownerships
echo "What is the name of the owner: "
read $owner
zipForOwner(){
arg1=$1
for i in "${studyinstanceuids[#]}"; do
zip -r ~/export/"${studyinstanceuids[#]}"/20140620_"${studyinstanceuids[#]}".zip .
sudo chown $1:$1 ~/export/"${studyinstanceuids[#]}"/"${studyinstanceuids[#]}".zip
sudo mv ~/export/"${studyinstanceuids[#]}"/"${studyinstanceuids[#]}".zip ~/home/"$1"
done
}
zipForOwner $owner
exit;
Does anyone have any suggestions
EDIT: Heere are my results running in xterm
+ file=/home/support/exporttool/zipFiles.txt
+ index=0
+ declare -a studyinstanceuids
+ read line
+ studyinstanceuids[$index]=1.3.46.670589.16.11.8.34254330145.20140603.134057.0
+ index=1
+ echo 1.3.46.670589.16.11.8.34254330145.20140603.134057.0
1.3.46.670589.16.11.8.34254330145.20140603.134057.0
+ read line
+ for i in '"${studyinstanceuids[#]}"'
+ echo 1.3.46.670589.16.11.8.34254330145.20140603.134057.0
+ ./cmd2
Please enter StudyInstanceUID:
+ echo 'Exams are in!'
Exams are in!
+ echo 'What is the name of the owner: '
What is the name of the owner:
+ read
sftpuser
+ zipForOwner
+ arg1=
+ for i in '"${studyinstanceuids[#]}"'
+ zip -r /home/support/export/1.3.46.670589.16.11.8.34254330145.20140603.134057.0/20140620_1.3.46.670589.16.11.8.34254330145.20140603.134057.0.zip .
adding: .studiesToExportSend.txt.swp^C
zip error: Interrupted (aborting)
+ sudo chown : /home/support/export/1.3.46.670589.16.11.8.34254330145.20140603.134057.0/1.3.46.670589.16.11.8.34254330145.20140603.134057.0.zip
[sudo] password for support:
This line is your problem:
zip -r ~/export/"${studyinstanceuids[#]}"/20140620_"${studyinstanceuids[#]}".zip .
You're attempting to create a zip file called ~/export/"${studyinstanceuids[#]}"/20140620_"${studyinstanceuids[#]}".zip (good) with all of the contents in the current directory . (bad).
You need to change the . to be the folder you want zipped.

Resources