Linux - NewbieThis Linux forum is for members that are new to Linux.
Just starting out and have a question?
If it is not in the man pages or the how-to's this is the place!
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
Ok here i am again with some question considering a script for managing my music collection.
Is it possible to see if
if [ -e "$file" ] but instead of checking file exist to check whether this file starts with [0-9]-* (i.e 00-wiz_khalifa-black_&_yellow-(remix)-(82_bpm).mp3) then...
And how do i remove this first part?
I now use
Code:
numbers=*-*
$(echo ${mp3#$numbers}
this has worked so far but this is not ideal...
The input may differ from 00- to 201_ so this is quite hard for me ;-)
(my current script sometimes screws this up -> 00_wiz_khalifa-black_&_yellow-(remix)-(82_bpm).mp3 becomes black_&_yellow-(remix)-(82_bpm).mp3 (which is not right)
But works with 00-
Thanks in regards !
Mainstream
Last edited by mainstream; 03-28-2011 at 02:29 PM.
Click here to see the post LQ members have rated as the most helpful post in this thread.
Ok here i am again with some question considering a script for managing my music collection.
Is it possible to see if
if [ -e "$file" ] but instead of checking file exist to check whether this file starts with [0-9]-* (i.e 00-wiz_khalifa-black_&_yellow-(remix)-(82_bpm).mp3) then...
And how do i remove this first part?
I now use
Code:
numbers=*-*
$(echo ${mp3#$numbers}
this has worked so far but this is not ideal...
The input may differ from 00- to 201_ so this is quite hard for me ;-)
(my current script sometimes screws this up -> 00_wiz_khalifa-black_&_yellow-(remix)-(82_bpm).mp3 becomes black_&_yellow-(remix)-(82_bpm).mp3 (which is not right)
But works with 00-
Thanks in regards !
Mainstream
You can try a loop and a regex like:
for i in `ls /somefolder/ | grep -E [0-9][0-9]-*`
do
mv $i $(cat $i | sed -e 's/[0-9][0-9]-//')
done
Well one would say you still need to test that the file exists prior to testing it starts with numbers.
Assuming you are using a recent version of bash you could do:
Well one would say you still need to test that the file exists prior to testing it starts with numbers.
Assuming you are using a recent version of bash you could do:
Code:
if [[ -e "$file" && $file =~ ^[0-9] ]]
With its current if logic I agree he would need that and it's a neet if condition. In a loop you would not need to test it, if it doesn't exist it will not show up. Like i'm doing a `ls`, if the file doesn't exist, it will not display. You save some pico seconds on the CPU and a few line of code. :P
You might want to test if you have write permission on the file but the worse is that the mv will return an error anyway and unless you need to automate a response to that error, it would be useless.
Is there a particular reason while you wanted to use a if instead of a loop? Most beginner in programmtion tend to use a if to test a condition which is not exactly a IF condition but more likely a loop condition. It is a bad logic to do a list of item and to test them individually for a condition. It is better to list only the object you want and to cycle an action on each of them. (My way to say my mind is not very clear because it's late. But if someone find a better way to explain it, you are more than welcome).
##!/bin/sh
for file in *-*\(*bpm\).mp3; do
if [ -e "$file" ]
then
echo \.---------------------------------------------------------------------------------\.
echo \| $file has BPM tags \-\> removing \|
echo \'---------------------------------------------------------------------------------\'
mv -i $file $(echo ${file/-([0-9]*_bpm)/} | sed 's/ /_/g' | sed 's/-(/_(/g' | sed 's/_-_/-/g' | tr '[A-Z]' '[a-z]')
else echo "No BPM tags found -> skipping"
fi
done
for file in `ls | grep -E [0-9]-* | egrep '\.mp3$'`; do
if [[ -e "$file" && $file =~ ^[0-9] ]]
then
echo \.---------------------------------------------------------------------------------\.
echo \| $file has numeric tags \-\> removing
echo \'---------------------------------------------------------------------------------\'
mv -i $file $(echo $file | sed -e 's/[0-9][0-9]-//')
else echo "No numeric tags found -> skipping"
fi
done
i know have this:
-except this doesn't work on 00_wiz_khalifa-sakdlaskd.mp3
if i change [0-9]_* it will detect
00_wiz_khalifa-sakdlaskd.mp3
10-wiz_khalifa-sakdlaskd.mp3
?
Last edited by mainstream; 03-29-2011 at 04:59 AM.
@toordog - the question was how to test if a file exists and starts with a number and as the original question was an if, this is what I supplied.
Should you wish to get technical, I am happy to oblige with the issues with your code.
1. Any files that have spaces in them will fail due to word splitting in your for loop.
2. As your ls, generally a bad choice as outlined here, does not segregate files and directories,
any directory that starts with a digit will also be moved
3. The OP specifically asked what if there is an underscore instead of a dash and later pointed out there could be more than 2 digits at the front which your grep and sed will fail.
4. Your sed only allows for 2 digits followed by a dash so again the underscore will not be evaluated
@OP - The reason 00_wiz_khalifa-sakdlaskd.mp3 is not working is due to the following:
Code:
#first loop
for file in *-*\(*bpm\).mp3; do # This says the file MUST contain a dash
# second loop
mv -i $file $(echo $file | sed -e 's/[0-9][0-9]-//') # sed only considers files with a dash in them
See how you go solving with this information. If you get stuck just holler back
@toordog - the question was how to test if a file exists and starts with a number and as the original question was an if, this is what I supplied.
Should you wish to get technical, I am happy to oblige with the issues with your code.
1. Any files that have spaces in them will fail due to word splitting in your for loop.
2. As your ls, generally a bad choice as outlined here, does not segregate files and directories,
any directory that starts with a digit will also be moved
3. The OP specifically asked what if there is an underscore instead of a dash and later pointed out there could be more than 2 digits at the front which your grep and sed will fail.
4. Your sed only allows for 2 digits followed by a dash so again the underscore will not be evaluated
@OP - The reason 00_wiz_khalifa-sakdlaskd.mp3 is not working is due to the following:
Code:
#first loop
for file in *-*\(*bpm\).mp3; do # This says the file MUST contain a dash
# second loop
mv -i $file $(echo $file | sed -e 's/[0-9][0-9]-//') # sed only considers files with a dash in them
See how you go solving with this information. If you get stuck just holler back
GRAIL thank you very much for pointing this out. It really gaves me some lead to read about the ls issue. I've been stuck in another context with the ls output to work under Linux and AIX and didn't find a solution at that time. I just found it with your reference. I should probably read much more about "find" as well.
Most files do not contain spaces as i replace them (sed 's/ /_/g') on the first run.
Well I do not see this as necessary, but that is of course up to you.
Quote:
It'll properly be solved when the first underscore in [0-9]_ will be replaced with sed i guess?
Or create another loop (but that's to much?)
Personally I would probably do all your removals in a single while loop fed by find (as a bonus this will not be affected by word splitting).
Also you could easily do most of what you want without the use of sed, grep, tr and so on and just use bash ... assuming your version is recent enough.
For example:
Code:
FILE="This IS a FiLe WIth MIXeD case.mp3"
echo ${FILE,,}
echo ${FILE// /_}
#also
FILE='00_wiz_khalifa-sakdlaskd.mp3'
[[ $FILE =~ ^[0-9] ]] && echo ${FILE#*[-_]}
Yes it is better to use 'for i in *' than ls as the star will glob entire name and ls values returned to the for loop will be subject to word splitting.
Maybe you would like to provide what the output you would like to see is for these 3 files once your script has run?
Also I noted that none of these start with digits??
Well I do not see this as necessary, but that is of course up to you.
Personally I would probably do all your removals in a single while loop fed by find (as a bonus this will not be affected by word splitting).
Also you could easily do most of what you want without the use of sed, grep, tr and so on and just use bash ... assuming your version is recent enough.
For example:
Code:
FILE="This IS a FiLe WIth MIXeD case.mp3"
echo ${FILE,,}
echo ${FILE// /_}
#also
FILE='00_wiz_khalifa-sakdlaskd.mp3'
[[ $FILE =~ ^[0-9] ]] && echo ${FILE#*[-_]}
Just to give you some ideas
No problem Happy to help
I would never come up with something like this, since:
1. I don't quite understand that (looks quite complex to me)
2. Can hardly find anything in literature (specific)
I used sed / tr because I'm quite new to coding, so still learning ;-)
So thanks for all the feedback and pointers you're giving -> really helpful.
I wanted to try this in 1 loop, but tried it so many times i eventually gave up hope.
Then a few weeks later i heard about this forum, and i must say: I'm very happy i did .
There's some much more to learn from other people, then just browse the Internet.
Quote:
Originally Posted by grail
Looks like we were posting at the same time.
Yes it is better to use 'for i in *' than ls as the star will glob entire name and ls values returned to the for loop will be subject to word splitting.
Maybe you would like to provide what the output you would like to see is for these 3 files once your script has run?
Also I noted that none of these start with digits??
Input would be
10-Nitrogenetics-hit me-zzzz.mp3 -> nitrogenetics-hit_me.mp3
202_wiz_khalifa-black_&_yellow-(remix)-(82_bpm)-zzzz.mp3 -> wiz_khalifa-black_&_yellow-(remix).mp3
And all the parameters from sed/tr in the script above.
I used this script quite a while, but a couple days ago, i wanted to make a new script, which is more compact and more functional, and learn something new.
Last edited by mainstream; 03-30-2011 at 08:46 AM.
http://www.amazon.com/New-KornShell-.../dp/0131827006
This book is the best book out there to learn Korn Shell. It's been written by the creator of the language and is quite good to start and to use as a reference later.
New KornShell Command And Programming Language, The (2nd Edition)
Morris I. Bolsky (Author), David G. Korn (Author)
I used sed / tr because I'm quite new to coding, so still learning ;-)
Cool ... and doing a decent job too
Quote:
I would never come up with something like this, since:
1. I don't quite understand that (looks quite complex to me)
2. Can hardly find anything in literature (specific)
1. Not really complex once you understand the mechanism
2. Obviously the above comes from doing and reading so 2 pieces of literature I will direct you to are:
http://tldp.org/LDP/abs/html/ - I found this helpful for learning the basics (don't let the word 'Advanced' scare you off) http://mywiki.wooledge.org/TitleIndex - Read through every single link, at your leisure. this is basically a list of all the things that people over think or do outright wrong (some of which are even
from the previous link I gave)
Now I realise there might be more combinations you may need to test, but the following works just fine on the given examples:
Code:
#!/bin/bash
while read -r DIR FILE
do
TMP="${FILE,,}"
TMP="${TMP// /_}"
[[ $TMP =~ ^[0-9] ]] && TMP="${TMP#*[-_]}"
[[ $TMP =~ bpm ]] && TMP="${TMP%-(*bpm*}.mp3"
echo mv "$DIR/$FILE" "$DIR/$TMP"
done< <(find . -type f -name '*.mp3' -printf "%h %f\n")
Starting with the find which feeds the loop and then going from top to bottom:
1. Find all files in the current directory and below that are files (-type f), end in '.mp3' and once found return the path and the file name as separated items
2. Read path into DIR and filename into FILE
3. ,, - return the entire string, to variable TMP, represented by FILE in lowercase (Note: a single comma will only change the first letter)
4. // /_ - find all spaces in string and replace with an underscore (Note: Single / /_ will only replace the first space found)
5. If string starts with a digit, ^[0-9], then starting from the front of the string, #, remove the shortest match until first underscore or dash, [-_], is found (Note: ## would remove the longest match)
6. If string contains substring, bpm, then starting from the back, %, remove the shortest match to, -(*bpm*. As this will remove until the end of the string you need to replace the .mp3 (Note: %% would remove the longest match)
7. echo allows us to check that the mv command would move the previous file name to the correct new file name
8. Once happy and tested you can simply remove the echo and change will actually take place
Let me know if you have any questions? All the above can be found on the links I provided.
FILE="This IS a FiLe WIth MIXeD case.mp3"
echo ${FILE,,}
echo ${FILE// /_}
#also
FILE='00_wiz_khalifa-sakdlaskd.mp3'
[[ $FILE =~ ^[0-9] ]] && echo ${FILE#*[-_]}
I would never come up with something like this, since:
1. I don't quite understand that (looks quite complex to me)
2. Can hardly find anything in literature (specific)
These are all examples of parameter substitution, similar to the one you used in your opening post. ${var,,} is a form new to bash v.4 that converts the variable to lowercase, and ${var// /_} simply substitutes spaces for underscores, similar to sed's s///g command.
And as mentioned before, if the file name doesn't match the pattern then no change is made, so there's no need to test for the patterns first.
Quote:
Input would be
10-Nitrogenetics-hit me-zzzz.mp3 -> nitrogenetics-hit_me.mp3
202_wiz_khalifa-black_&_yellow-(remix)-(82_bpm)-zzzz.mp3 -> wiz_khalifa-black_&_yellow-(remix).mp3
I'd personally use bash's extended globbing feature to help with variable-length strings and simply do something like the following for files like the above:
Code:
IFS=$'\n' #changing IFS to newline means spaces
#are ignored during word-breaking
shopt -s extglob #enable extended globbing
for file in $@ ; do
#break input filename into path and name
#so that names with paths included are correctly handled
filepath=${file%/*}
newname=${file##*/}
#change spaces (and tabs) to underscores in filename
#extglob reduces multiple spaces to single underscore
newname=${newname//+([[:space:]])/_}
#lowercase filename
newname=${newname,,}
#remove all initial "digit+[-_]" combinations
#again extglob handles variable numbers of digits
newname=${newname#+([0-9])[_-]}
#remove any final "-zzzz" strings
#similar to the last operation
#assumes the file ends in ".mp3", however
newname=${newname/-+(z).mp3/.mp3}
#remove any "-(nnn_bpm)" strings
#remember, spaces have been changed to underscores already
newname=${newname/-(+([0-9])_bpm)}
#you can continue adding as many changes as you want here.
#call mv to change the filename.
#I used -iv to make it interactive and verbose, but that's up to you.
echo "File \"$file\" will be changed to: \"$filepath/$newname\""
mv -iv "$file" "$filepath/$newname"
done
Notice how it's all bash's built-in parameter expansion at work. There are no external commands involved except mv at the end. I also recommend changing the input from a simple * globbing pattern to $@, and feeding the filenames directly into the script as it's launched. In other words, run the script like this:
Code:
./cleanupscript.sh ./dir1/*.mp3 ./dir2/*.mp3
This is more flexible in the long run, allowing you to store the file somewhere like /usr/local/bin, and targeting any directory. It also allows you to feed it multiple files or directories at a time.
#Edit: noticed and corrected a minor coding error.
Last edited by David the H.; 03-30-2011 at 02:22 PM.
Reason: minor rewording for clarity/fixed coding error
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.