Stripping directory paths to get file names

What I am doing is converting mp3’s with LAME. I already have an old script I wrote that works but I want to add to it this ability–to no longer delete the file but instead save it in a new root folder with sub-directories that match the using the path that it is already in.

This is the code I got off the Internet that I am using in my script to get the path:

 c=$FILENAME
 xpath=${c%/*} 
 xbase=${c##*/}
 xfext=${xbase##*.}
 xpref=${xbase%.*}
 path=${xpath}
 pref=${xpref}
 ext=${xfext}

The xpath and path give me the directory structure /home/user/working-root-directory/band-folder/album-name/

Using that technique, how do I script this to get just the band-folder into a separate variable and the album-folder into a separate variable?

Then I can use them to create new folders keeping all the mp3s in band album order to put them into a different root folder; this would eliminate me from having to move them myself so the next time I run my script I will not re-sample them again because they will no longer be in the working directoy path/folders and still have a back up copy of my files just in case.

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

When referring to $c if by filename you mean full-path to file then this job is really very easy. There are a few ways to do it.

Using just POSIX shell globs and parameter expansion you can do:

c='/home/user/working-root-directory/band-folder/album-name/music-file.mp3'
file=${c##*/}
album=${c#*"${c%/*/"$file"}"/}
band=${c#*"${c%/*/"$album"}"/}
band=${band%%/*} album=${album%%/*}

Basically it works by trimming a string from the tail of the variable and then trimming the results from the head of the variable. In the last line any unwanted remains from the tail are stripped away. It is perhaps easier to understand with real-world data. If you were to wrap the above snippet in a shell with set -x enabled, you’d see something along these lines printed to stderr:

+ c=/home/user/working-root-directory/band-folder/album-name/music-file.mp3
+ file=music-file.mp3
+ album=album-name/music-file.mp3
+ band=band-folder/album-name/music-file.mp3
+ band=band-folder
+ album=album-name

Another way you might do this is to use the shell’s internal field separator to split the pathname on / boundaries.

set -f; IFS=/             #disable globbing so as not to gen more filenames
set -- $c                 #set the shell's arg array to split path
shift "$(($#-3))"         #remove all but relevant fields
band=$1 album=$2 file=$3  #assign values

Here is some more set -x output:

+ c=/home/user/working-root-directory/band-folder/album-name/music-file.mp3
+ set -f
+ IFS=/
+ set -- '' home user working-root-directory band-folder album-name music-file.mp3
+ shift 4
+ band=band-folder
+ album=album-name
+ file=music-file.mp3

Method 2

I would hire zsh for this job:

$ c='/home/user/working-root-directory/band-folder/album-name/filename.mp3'

$ echo $c:h
/home/user/working-root-directory/band-folder/album-name
$ echo $c:h:h
/home/user/working-root-directory/band-folder
$ echo $c:h:h:h
/home/user/working-root-directory

(...)

$ echo $c:t    
filename.mp3
$ echo $c:h:t
album-name
$ echo $c:h:h:t
band-folder

(...)

If you have to use bash life is harder, you need to use temporary variables:

$ c='/home/user/working-root-directory/band-folder/album-name/filename.mp3'

$ x1=${c%/*}
$ echo $x1
/home/user/working-root-directory/band-folder/album-name
$ x2=${x1%/*}
$ echo $x2
/home/user/working-root-directory/band-folder

 (...)

$ y1=${c##*/}
$ echo $y1
filename.mp3
$ y2=${x1##*/}
$ echo $y2
album-name
$ y3=${x2##*/}
$ echo $y3
band-folder

(...)

Method 3

The previous solutions seem overly complicated to me. The following seems to me to be the simpliest solution.

To get the directory name of a filename strings in bash

 dirname "$varcontainingfilename"

To get the ONLY the filename from a filename string in bash use

 basename "$varcontainingfilename"

From this base you should be able to easily do what you want.

Example executing this shell script as script /a/b/c would serve as an example

 #!/bin/sh
 set -u
 PATH='/bin:/usr/bin'
 export PATH
 filestring="$1"
 fn=`basename "$filestring"`
 dn=`dirname "$filestring"`
 echo Original File String "$filestring"
 echo Filename is "$fn"
 echo directory is "$dn"
 exit 0

Method 4

To make “playing” with variables a little shorter

c=/home/user/working-root-directory/band-folder/album-name/music-file.mp3
for var in file album band 
do
  eval "$var=${c##*/}"
  c=${c%/${!var}}
done

echo $file $album $band
music-file.mp3 album-name band-folder

Other way is to use read command

IFS=/ read -r band album file  <<< ${c#"${c%/*/*/*}"/}

Method 5

Zsh is a much more elegant solution, but if it is not your shell, you can useawk:

 printf "%sn" "$path"
/home/user/working-root-directory/band-folder/album-name/music-file.mp3

band=$(awk -F/ '{print $(NF-2)}' <(printf "%s" "$path"))
printf "%sn" "$band"
band-folder

album=$(awk -F/ '{print $(NF-1)}' <(printf "%s" "$path"))
printf "%sn" "$album"
album-name

file=$(awk -F/ '{print $NF}' <(printf "%s" "$path"))
printf "%sn" "$file"
music-file.mp3


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x