Rename subfolder: add leading word from parent folder - shell

I have a large music library organized in Artist folders with Album subfolders:
Artist 1
- Album A
-- File 1
-- File 2
- Album B
-- File 1
-- File 2
Artist 2
- Album C
-- File 1
-- File 2
- Album D
-- File 1
-- File 2
Now I want to rename all Album folders by adding the Artist name as prefix AND move all Album folders in to the root directory, like this:
Artist 1 - Album A
- File 1
- File 2
Artist 1 - Album B
- File 1
- File 2
Artist 2 - Album C
- File 1
- File 2
Artist 2 - Album D
- File 1
- File 2
What is the best way to do that? I tried to do it with Total Commander, but I don't get it. Or maybe shell, mp3tag? I use Windows 10.
Thanks!

This could be done in several ways, actually. I'll show how in bash, but that's only if you have that thing of Bash on Ubuntu on Windows. Again:
BASH
Assuming that your music library is ~/Music/, that all the artist folders are inside, that you accept to have everything moved to a new folder, assuming that it doesn't yet exist (because if you had an artist folder named 'CoolSinger' and just accidentally you also have another artist folder named 'CoolSinger - CoolAlbum', by a mistake, there would be problems...), and that there are not any other files involved than what you state, the shell script would look like this:
#!/bin/bash
mkdir ~/NewMusic/
for artistdir in ~/Music/*; do
artistname="$(basename "${artistdir}")"
for albumdir in "${artistdir}"/*; do
albumname="$(basename "${albumdir}")"
mv "${albumdir}" ~/NewMusic/"${artistname} - ${albumname}" 2>/dev/null
done
done
And let me explain it in human:
Create the destination folder. Then, for every artist (assuming all are folders); iterate through every album sub-folder, moving it to the new folder, and renaming it, by the way, using the artist name and folder name from the actual folder names. If everything went OK, this would result in:
A folder ~/Music/ with the original sub-folder names, but they're now empty.
A folder ~/NewMusic with the desired structure.
If an artist folder is empty, the shell would try to throw an error because it expands to the literal string (explained later), but don't worry, because if it is empty, then anything is to do with it, so you can ignore it, appending 2>/dev/null to the command. However, if anything else goes wrong with this, it will also be silenced.
If you wanted to, let's say, stay with the original files, and have a copy of all that with the desired structure, you should change the mv command for a cp -R one.
And all that is for a script, in a file. If you wanted to execute it right from the command line, which would make somewhat tricky to change the folder name for the music library, it would look like this:
mkdir ~/NewMusic/; for artistdir in ~/Music/*; do artistname="$(basename "${artistdir}")"; for albumdir in "${artistdir}"/*; do albumname="$(basename "${albumdir}")"; mv "${albumdir}" ~/NewMusic/"${artistname} - ${albumname}"; done; done
And an explanation of everything there:
mkdir creates the directory.
for A in B ; do C ; done is a loop. It loops through every string separated by one of the IFS characters in B, and assign it as a value for the variable A, while it executes C.
Note that the text in the script has an asterisk. The shell expands a string with an asterisk to what it finds to match the pattern. For instance, if you had a bunch of Word documents, typing echo *.docx would expand to every name matching that ending, in the current folder (in another case, echo /path/*.docx).
So, for every folder inside the specified directory, assign (once for every artist folder) the variable artistname with the base name of the file specified (in this case, the folder) as its value. So, basename /path/to/anything returns anything.
With the help of another for loop, do for every element inside the artist directory, rename it as specified, and if the new name is in another directory, it will move it there.
Again, the 2>/dev/null redirection is there to redirect all output in stderr to the void. Output in stdout would be still printed, though, if there was any.
SOME IMPORTANT NOTES
I don't have any idea whether the shell from Bash on Ubuntu on Windows detects /dev/null as the special character file that a standard UNIX machine has or not. Likely it does, or it wouldn't be compatible with so many things.
This is only if you have Bash on Ubuntu on Windows in your Windows 10. If I recall correctly, the chance of using it was implemented with the Anniversary Update. If you have the update, but not bash, I recommend you to install it. There are several guides over that on the Internet.
About the folder structure. I suspect the same about /dev/null as for the POSIX folder structure: that it follows it, as it's not just Bash on Windows, but Bash on Ubuntu on Windows.

I found an answer by myself. The best way to do it is to use mp3tag tool. You can create new folders and copy your files in it.
Select Converter > Tag - Filename
Type in:
E:\%artist% - %album%\$num(%track%,2) - %title%
You'll receive a directory with Artist - Albumname\01 - File.mp3

Related

How to create a list of sequentially numbered folders using an existing folder name as the base name

I've done a small amount of bash scripting. Mostly modifying a script to my needs.
On this one I am stumped.
I need a script that will read a sub-folder name inside a folder and make a numbered list of folders based on that sub-folder name.
Example:
I make a folder named “Pictures”.
Then inside I make a sub-folder named “picture-set”
I want a script to see the existing sub-folder name (picture-set) and make 10 more folders with sequential numbers appended to the end of the folder names.
ex:
folder is: Pictures
sub-folder is: picture-set
want to create:
“picture-set-01”
“picture-set-02”
“picture-set-03”
and so forth up to 10. Or a number specified in the script.
The folder structure would look like this:
/home/Pictures/picture-set
/home/Pictures/picture-set-01
/home/Pictures/picture-set-02
/home/Pictures/picture-set-03
... and so on
I am unable to tell the script how to find the base folder name to make additional folders.
ie: “picture-set”
or a better option:
Would be to create a folder and then create a set of numbered sub-folders based on the parent folder name.
ex:
/home/Songs - would become:
/home/Songs/Songs-001
/home/Songs/Songs-002
/home/Songs/Songs-003
and so on.
Please pardon my bad formatting... this is my first time asking a question on a forum such as this. Any links or pointers as to proper formatting is welcome.
Thanks for the help.
Bash has a parameter expansion you can use to generate folder names as arguments to the mkdir command:
#!/usr/bin/env bash
# Creates all directories up to 10
mkdir -p -- /home/Songs/Songs-{001..010}
This method is not very flexible if you need to dinamically change the range of numbers to generate using variables.
So you may use a Bash for loop and print format the names with desired number of digits and create each directory in the loop:
#!/usr/bin/env bash
start_index=1
end_index=10
for ((i=start_index; i<=end_index; i++)); do
# format a dirpath with the 3-digits index
printf -v dirpath '/home/Songs/Songs-%03d' $i
mkdir -p -- "$dirpath"
done
# Prerequisite:
mkdir Pictures
cd Pictures
# Your script:
min=1
max=12
name="$(basename "$(realpath .)")"
for num in $(seq -w $min $max); do mkdir "$name-$num"; done
# Result
ls
Pictures-01 Pictures-03 Pictures-05 Pictures-07 Pictures-09 Pictures-11
Pictures-02 Pictures-04 Pictures-06 Pictures-08 Pictures-10 Pictures-12

Automator Move Preview/Thumb image to Folder with similar FileName as the image (Applescript)

I GET 2 ERRORS.
OBJECTIVE: In my downloads folder I have:
FOLDER: fileName 001
IMAGE: fileName 001 preview.jpg
FOLDER: fileName 002
IMAGE: fileName 002 preview.jpg
FOLDER: fileName 003
IMAGE: fileName 003 preview.jpg
I want Automator to move all previews inside their respective folders.
So, I followed this similar tutorial and I got this so far:
1st)
on run {input, parameters}
return input
end run
2nd) Filter Finder Items: Kind is Folder
3rd) Set Value of Variable: FilePath ( path where image has to go )
4th) IN THIS STEP I GET AN ERROR: Check the actions properties ( I'm new so don't know ).
on run {input, parameters}
tell application "Finder"
set FileName to name of file input
end tell
end run
5th) Set Value of Variable: FilePath ( path where image has to go )
6th) Filter Finder Items: Kind is Image AND Name contains {FilePath} ( same path as folder name ).
The problem occurs with the {FilePath}, Automator doesn't accept the newly created variable: FilePath, in the contains field, the Newly created variable called FilePath.
7th) Get Value of Variable: FileName
8th) Move Finder Items to: FilePath
Here is the workFlow file.
The link to your workflow file didn't work, but it's ok—thanks for trying to share it anyway, but, in this instance, I can work without it, as I'm actually going to do it from scratch:
Depending how your workflow will be setup to receive the input, you can remove the Get Specified Finder Items action and allow the workflow to run as a Service, for example, whose input will be the folder that contains images to be processed. As you said it was your Downloads folder that contains the images, I felt it was unlikely that you'd be setting up the workflow to work as a Folder Action.
For testing purposes, you can keep the Get Specified Finder Items action and replace the Example folder I've put in there with your Downloads folder.
Here's the code from the Run AppleScript action for you to copy/paste:
on run {input, parameters}
# input will be the folder containing the images, e.g. the Downloads folder
set [here] to the input
set suffix to "preview.jpg"
set my text item delimiters to {pi, space & suffix}
tell application "Finder"
set jpgs to the name of every file in here whose name ends with the suffix
repeat with jpg in jpgs
set this to the text items of jpg as text
set there to (a reference to the folder named this in here)
if not (there exists) then set there ¬
to make new folder in here ¬
with properties {name:this}
move the file named jpg in here to there
end repeat
end tell
end run
It matters not whether the destination folders exist: if they do, the image will be moved into the appropriate one; for those that don't, the folder is created before the image is moved.
Update In Response To Comments
① Sometimes I have a string between "preview" and extension .jpg or .png
② I'd like to also move the completed folders to a new folder /Users/sebba/BLENDER/BLENDS
Given the variability of your filenames that you've now disclosed, I think it's easier to use a shell script instead of an AppleScript to process the files.
Delete the Run AppleScript action and insert a Run Shell Script action, with the following options: Shell: bin/bash, Pass input: as arguments.
Delete any sample code that appears in the editing field and enter this code:
cd "$1"
folder=()
while read -r file
do
folder+=("$(egrep -io '^.+ \d{3}' <<<"$file")")
[[ -d "${folder[#]: -1}" ]] || mkdir "${folder[#]: -1}"
mv -v "$file" "${folder[#]: -1}"
done <<<"$( ls | egrep -i '^.+ \d{3} preview.*\.(jpg|png)' )"
mv -v "${folder[#]}" "/Users/sebba/BLENDER/BLENDS"
This uses a regular expression match to pick out filenames that:
Begin with absolutely anything (but not nothing); then
Follow with a space, precisely three digits, a space, then the word "preview"; then
Follow with absolutey anything (including nothing); then
End with ".jpg" or ".png".
Therefore, it will move the following files to the given folder (the folder is created if it doesn't exist, and files with the same name are overwritten):
My image_file 001 preview.foo bar.jpg → My image_file 001/
004 2 001 preview.png → 004 2 001/
But the following files won't be affected:
001 preview.png
My image_file 01 preview.jpg

Naming a file with a variable in a shell script

I'm writing a unix shell script that sorts data in ten subdirectories (labelled 1-10) of the home directory. In each subdirectory, the script needs to rename the files hehd.output and fort.hehd.time, as well as copy the file hehd.data to a .data file with a new name.
What I'd like it to do is rename each of these files in the following format:
AA.BB.CC
Where
AA = a variable in the hehd.data file within the subdirectory containing the file
BB = the name of the subdirectory containing the file (1-10)
CC = the original file name
Each subdirectory contains an hehd.data file, and each hehd.data file contains the string ij0=AA, where AA represents the variable I want to use to rename the files in the same subdirectory.
For example: When run, the script should search /home/4/hehd.data for the string ij0=2, then move /home/4/hehd.output to /home/4/2.4.hehd.output.
I'm currently using the grep command to have the script search for the string ij0=* and copy it to a new text file within the subdirectory. Next, the string ij0= is deleted from the text file, and then its contents are used to rename all target files in the same subdirectory. The last line of the shell script deletes the text file.
I'm looking for a better way to accomplish this, preferably such that all ten subdirectories can be sorted at once by the same script. My script seems incredibly inefficient, and doesn't do everything that I want it to by itself.
How can I improve this?
Any advice or suggestions would be appreciated; I'm trying to become a better computer user and that means learning better ways of doing things.
Try this:
fromdir=/home
for i in {1..10};do
AA=$(sed 's/ij0=\([0-9]*\)/\1/' "$fromdir/$i/hehd.data")
BB="$i"
for f in "$fromdir/$i/"*;do
CC="${f##*/}"
if [[ "$CC" = "hehd.data" ]]; then
echo cp "$f" "$fromdir/$i/$AA.$BB.$CC"
else
echo mv "$f" "$fromdir/$i/$AA.$BB.$CC"
fi
done
done
It loops over directories using Bash sequence {1..10].
In each directory, with the sed command the ij0 value is assigned to AA variable, the directory name is assigned to BB.
In the file loop, if the file is hehd.data it's copied, else it's renamed with the new name.
You can remove the echo before cp and mv commands if the output meets your needs.

looping files with bash

I'm not very good in shell scripting and would like to ask you some question about looping of files big dataset: in my example I have alot of files with the common .pdb extension in the work dir. I need to loop all of them and i) to print name (w.o pdb extension) of each looped file and make some operation after this. E.g I need to make new dir for EACH file outside of the workdir with the name of each file and copy this file to that dir. Below you can see example of my code which are not worked- it's didn't show me the name of the file and didn't create folder for each of them. Please correct it and show me where I was wrong
#!/bin/bash
# set the work dir
receptors=./Receptors
for pdb in $receptors
do
filename=$(basename "$pdb")
echo "Processing of $filename file"
cd ..
mkdir ./docking_$filename
done
Many thanks for help,
Gleb
If all your files are contained within the .Repectors folder, you can loop each of them like so:
#!/bin/bash
for pdb in ./Receptors/*.pdb ; do
filename=$(basename "$pdb")
filenamenoextention=${filename/.pdb/}
mkdir "../docking_${filenamenoextention}"
done
Btw:
filenamenoextention=${filename/.pdb/}
Does a search replace in the variable $pdb. The syntax is ${myvariable/FOO/BAR}, and replaces all "FOO" substrings in $myvariable with "BAR". In your case it replaces ".pdb" with nothing, effectively removing it.
Alternatively, and safer (in case $filename contains multiple ".pdb"-substrings) is to remove the last four characters, like so: filenamenoextention=${filename:0:-4}
The syntax here is ${myvariable:s:e} where s and e correspond to numbers for the start and end index (not inclusive). It also let's you use negative numbers, which are offsets from the end. In other words: ${filename:0:-4} says: extract the substring from $filename starting from index 0, until you reach fourth-to-the-last character.
A few problems you have had with your script:
for pdb in ./Receptors loops only "./Receptors", and not each of the files within the folder.
When you change to parent directory (cd ..), you do so for the current shell session. This means that you keep going to the parent directory each time. Instead, you can specify the parent directory in the mkdir call. E.g mkdir ../thedir
You're looping over a one-item list, I think what you wanted to get is the list of the content of ./Receptors:
...
for pdb in $receptors/*
...
to list only file with .pdb extension use $receptors/*.pdb
So instead of just giving the path in for loop, give this:
for pdb in $receptors/*.pdb
To remove the extension :
set the variable ext to the extension you want to remove and using shell expansion operator "%" remove the extension from your filename eg:
ext=.pdb
filename=${filename%${ext}}
You can create the new directory without changing your current directory:
So to create a directory outside your current directory use the following command
mkdir ../docking_$filename
And to copy the file in the new directory use cp command
After correction
Your script should look like:
receptors=./Receptors
ext=.pdb
for pdb in $receptors/*.pdb
do
filename=$(basename "$pdb")
filename=${filename%${ext}}
echo "Processing of $filename file"
mkdir ../docking_$filename
cp $pdb ../docking_$filename
done

Terminals - Creating Multiple Identical Folders within Subdirectories and Moving Files

I have a bunch of files I'm trying to organize quickly, and I had two questions about how to do that. I really appreciate any help! I tried searching but couldn't find anything on these specific commands for OSX.
First, I have about 100 folders in a directory - I'd like to place an folder in each one of those folders.
For example, I have
Cars/Mercedes/<br>
Cars/BMW/<br>
Cars/Audi/<br>
Cars/Jeep/<br>
Cars/Tesla/
Is there a way I can create a folder inside each of those named "Pricing" in one command, i.e. ->
Cars/Mercedes/Pricing <br>
Cars/BMW/Pricing<br>
Cars/Audi/Pricing<br>
Cars/Jeep/Pricing<br>
Cars/Tesla/Pricing
My second question is a little tougher to explain. In each of these folders, I'd like move certain files into these newly created folders (above) in the subdirectory.
Each file has a slightly different filename but contains the same string of letters - for example, in each of the above folders, I might have
Cars/Mercedes/payment123.html
Cars/BMW/payment432.html
Cars/Audi/payment999.html
Cars/Jeep/payment283.html
Is there a way to search each subdirectory for a file containing the string "payment" and move that file into a subfolder in that subdirecotry - i.e. into the hypothetical "Pricing" folders we just created above with one command for all the subdirectories in Cars?
Thanks so much~! help with either of these would be invaluable.
I will assume you are using bash, since it is the default shell in OS X. One way to do this uses a for loop over each directory to create the subdirectory and move the file. Wildcards are used to find all of the directories and the file.
for DIR in Cars/*/ ; do
mkdir "${DIR}Pricing"
mv "${DIR}payment*.html" "${DIR}Pricing/"
done
The first line finds every directory in Cars, and then runs the loop once for each, replacing ${DIR} with the current directory. The second line creates the subdirectory using the substitution. Note the double quotes, which are necessary only if the path could contain spaces. The third line moves any file in the directory whose name starts with "payment" and ends with ".html" to the subdirectory. If you have multiple files which match this, they will all be moved. The fourth line simply marks the end of the loop.
If you are typing this directly into the command line, you can combine it into a single line:
for DIR in Cars/*/ ; do mkdir "${DIR}Pricing"; mv "${DIR}payment*.html" "${DIR}Pricing/"; done

Resources