LinuxQuestions.org
Welcome to the most active Linux Forum on the web.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Linux Forums > Linux - General
User Name
Password
Linux - General This Linux forum is for general Linux questions and discussion.
If it is Linux Related and doesn't seem to fit in any other forum then this is the place.

Notices


Reply
  Search this Thread
Old 01-30-2007, 04:03 AM   #1
maxvonseibold
Member
 
Registered: Mar 2006
Location: Oxford, UK.
Distribution: Debian, Ubuntu, RHEL5, OSX
Posts: 41

Rep: Reputation: 15
Unhappy Bash File Name Matching - Binary file .ogg matches !!!


Hello,


I am trying to loop through a directory of songs to rename them from a text file. This block is inside another loop which controls which entry from the file is currently being searched for.


for i in $(ls *.ogg); do


#if i contains the number 'N_' which matches '$i' then we make the swap.


if grep "$NEWNAME" "$i"; then

echo "MATCH Found"
#Make rename here.


fi



#End of the Loop.

done


The problem is that $i is being treated as a binary file (obvious enough) so the if statment always returns true.


Thanks.


p.s. No I do NOT want to do this in Perl!!!
How can I just check it name against the name of the variable $i and not the actual file itself...
 
Old 01-30-2007, 04:27 AM   #2
colucix
LQ Guru
 
Registered: Sep 2003
Location: Bologna
Distribution: CentOS 6.5 OpenSuSE 12.3
Posts: 10,509

Rep: Reputation: 1983Reputation: 1983Reputation: 1983Reputation: 1983Reputation: 1983Reputation: 1983Reputation: 1983Reputation: 1983Reputation: 1983Reputation: 1983Reputation: 1983
Code:
if [ `echo $i | grep $NEWNAME | wc -l` -ne 0 ] ; then
should do the trick! Or the same by:
Code:
if [ `echo $i | grep -c $NEWNAME` -ne 0 ] ; then

Last edited by colucix; 01-30-2007 at 04:35 AM.
 
Old 01-30-2007, 04:40 AM   #3
jschiwal
LQ Guru
 
Registered: Aug 2001
Location: Fargo, ND
Distribution: SuSE AMD64
Posts: 15,733

Rep: Reputation: 682Reputation: 682Reputation: 682Reputation: 682Reputation: 682Reputation: 682
Giving a sample entry from the file might help as well as a sample filename.

Your grep command looks backwords.

Code:
for i in $(ls *.ogg); do

You could use globbing here instead:
for song in *.ogg; do
Code:
if grep "$NEWNAME" "$i"; then

Perhaps something like:

newname=$(grep "$song" $NEWNAMELIST) | cut d=' ' f=2)
I am guessing what the format of the file might be, but I don't know what the format of the original vs final names might be.

If the original names are all like "1001.ogg" and the entries are like "1001_New_Name.ogg", then you could use something like "${song#[[:digit:]]*_}" to get just the "New_Name.ogg" part and "${song%_*.ogg}" to return 1001.ogg.
So the rename command could be:
mv "${song#[[:digit:]]*_}" "${song%_*.ogg}.ogg"
 
Old 01-30-2007, 04:43 AM   #4
matthewg42
Senior Member
 
Registered: Oct 2003
Location: UK
Distribution: Kubuntu 12.10 (using awesome wm though)
Posts: 3,530

Rep: Reputation: 65
Please post code in [code] tags to improve readability.

You don't need to execute ls to get a list of files - just use the pattern. This saves the invocation of an external program - this is something you should always strive to do. i.e.
Code:
# Do this:
for i in *.ogg; do 
  ...
done

# Rather than this:
for i in $(ls *.ogg); do
  ...
done
When you use grep, it looks inside the named file(s). I think you're trying to check if the file name matches some pattern rather than that it contains some pattern. grep knows how to handle text files. It's purpose is to output the lines inside a file which match some pattern. ogg files are not text files, hence the warning.

If you want to test if the file name matches some pattern (e.g. starts with some digits followed by an underscore), consider using the case statement:
Code:
#!/bin/bash

shopt -s extglob

for i in *.ogg; do
    case "$i" in
    +([0-9])_*)
        echo "MATCH:    '$i'"
        ;;
    *)
        echo "no match  '$i'"
        ;;
    esac
done
Note the enabling of the shell option extglob. This enables the extended glo patterns, which includes +(pattern), which matches pattern one or more times. We exploit this to match characters in the range [0-9] (digits). Note this option is probablt bash specific - if you're using another shell, like ksh, it will not work.
 
Old 01-30-2007, 05:58 AM   #5
maxvonseibold
Member
 
Registered: Mar 2006
Location: Oxford, UK.
Distribution: Debian, Ubuntu, RHEL5, OSX
Posts: 41

Original Poster
Rep: Reputation: 15
Lots to look through here!


Ok, your right, I am basically trying to right a small script that will rename a directory of nameless ogg files with names provided from a text file. This is for when I have encoded stuff and do not have an internet connection and could not be bothered to type in all the names ...


matthewg42, I am trying to go with your suggestion first of all. However I need to match $i against a value in another array (${anarray[$a]) as opposed to +([0-9])_*) .

I tried this:

Code:
for i in *.ogg; do

  case "$i" in
    +(${anarray[$a]})
    echo "MATCH:    '$i'"
    ;;
  *)
    echo "no match  '$i'"
    ;;
  esac


done
But it is giving me a syntax error ... Can you advise me further?


Thankyou to everyone for your help though.



Max.
 
Old 01-30-2007, 07:42 AM   #6
matthewg42
Senior Member
 
Registered: Oct 2003
Location: UK
Distribution: Kubuntu 12.10 (using awesome wm though)
Posts: 3,530

Rep: Reputation: 65
Using a string as an index to an array is an associative array (a map in C++ STL language, or a hash in Perl terminology). Sadly, bash doesn't really support associative arrays at the moment. I'm sure someone out there has a hackish way to do it, but for a nice clean and readable program, I'd recommend using a different shell, or even moving up a level of sophistication to a language like Perl or Python or awk. Awk is probably the smallest dependency, and it's installed by default on most unix-like OSes).

So here's some alternatives for associative arrays:

1. Use zsh. This is broadly compatible with bash scripts, so if you have a lot of script already written, it's probably the easiest option. Bare in mind it's not likely to be installed by default on most systems. Note that you must declare an associative array using th typedef -A statement first:
Code:
#!/usr/bin/zsh

typeset -A ar

ar["bananas"]="yellow"
ar["apples"]="green"

echo "apples are ${ar["apples"]}"
echo "bananas are ${ar["bananas"]}"
2. Use awk. This is probably the most portable because some implementation of awk is likely to be installed on pretty much any unix-like system.
Code:
#!/usr/bin/awk -f

BEGIN {
        ar["bananas"]="yellow";
        ar["apples"]="green";

        print "apples are " ar["apples"];
        print "bananas are " ar["bananas"];
}
3. Use Perl, because it's the best
Code:
#!/usr/bin/perl

$ar{"bananas"} = "yellow";
$ar{"apples"} = "green";

print "bananas are " . $ar{"bananas"} . "\n";
print "apples are " . $ar{"apples"} . "\n";

Last edited by matthewg42; 01-30-2007 at 07:43 AM.
 
Old 01-30-2007, 12:25 PM   #7
maxvonseibold
Member
 
Registered: Mar 2006
Location: Oxford, UK.
Distribution: Debian, Ubuntu, RHEL5, OSX
Posts: 41

Original Poster
Rep: Reputation: 15
I don't deny your statement about Perl being the best. I just wanted to try and learn more bash ...


Perhaps I should chill and just be happy with the existence of Perl ...


However, just to say, with ${anarray[$a]}, the $a was only a number and I was not really treating it as associative.

Are you quite certain I could not have any variable in the case statement?


How about:


Code:
  case "$i" in
    +$AVALUE_NAME
    echo "MATCH:    '$i'"
    ;; 
  *)
    echo "no match  '$i'"
    ;;
  esac

Once again, thankyou.
 
Old 01-30-2007, 12:50 PM   #8
matthewg42
Senior Member
 
Registered: Oct 2003
Location: UK
Distribution: Kubuntu 12.10 (using awesome wm though)
Posts: 3,530

Rep: Reputation: 65
Oh I see, regular arrays with numeric indexes should be fine.

You can use a case statement with a variable, but you must use the correct syntax - you missed a ")" after your variable.

Also, how are you setting the array? If you try to set an array item using a shell glob pattern, the pattern will be pre-expanded to match the pattern, so if you do this:

Code:
ar=(*.ogg)
...and you have some files in the working directory which match *.ogg, the array will contain a list of those files, not the pattern itself.
 
Old 01-30-2007, 06:31 PM   #9
maxvonseibold
Member
 
Registered: Mar 2006
Location: Oxford, UK.
Distribution: Debian, Ubuntu, RHEL5, OSX
Posts: 41

Original Poster
Rep: Reputation: 15
Done!

Ok, at last. Well it seems to work.

I tested it with an album and it worked a treat.

Thanks again for your help! I can now rename loads of untitled music files.






Max.


Code:
#!/bin/bash


#!/bin/bash

#Shell Script to Rename Music Files with names from a text file. 
#This is intended to be used when you have 
#encoded a cd but the computer did not have an internet 
#connection so the files are all unnamed. 

#Read in names from text file (provided from the command 
#line) and assign them to an array. 

#e.g. To encode Iron Maiden's Killers:

#./song_rename Iron_Maiden_Killers.txt 

#The data file MUST take the format 

#NUMBER_SONGNAME.ogg

#You could change .ogg to whatever you want 
#but why would you want to ... ;-) 


#Echo back the name of the text file to take the names from. 
filename="$1"

echo CDROM Data File is:
echo -----
echo $filename
echo -----



filename="$1"

declare -a anarray #declare anarray to be... an array


#Ok, next I want to cycle through the text file and load each entry 
#(one per line) into the array #named anarray. 


exec 3<$filename 

# opens $filename for reading giving it the identifyer "&3"
let counter=0

while read LINE <&3; do

    anarray[$counter]=$LINE
    ((counter++))

	#echo $LINE

done

exec 3>&- 

#close file


#Display the array contents.

echo "-------------------------"
echo "Output The Array Contents"
echo "-------------------------"

for (( i = 0; i < $counter; i++)); do

	echo ${anarray[$i]}

done

#Ok, We know from the prior loop how many entries (songs) 
#we have. So we need another loop which will 
#iterate the same number of times. For each iteration we want to 
#get an entry from anarray. We then want #to loop through the 
#directory with the songs and match that anarray entry against 
#the song number in the #directory. 
#When we make a match we replace the directory 
#UNTITLED name with the name from the file. 


#So 1_Untitled.ogg becomes 1_The_Ides_Of_March.ogg


shopt -s extglob


#The first loop will make a number of iterations 
#equal to the number of entries in the text file. 

for (( a = 0; a < $counter; a++)); do 


	#For each line, we need to grap the number 
	#of the song which must be the first bit of data
	#e.g. 1_The_Ides_Of_March.ogg
	#So we just get '1_' and assign this value to NEWNAME
	#On the next cycle NEWNAME will get 2_ 
	#And So On. 

	#We use this number to match the actual untitled file names 
	#in the directory against those in the text file. 
	
	#I do hope all this makes sense ... 
	

	echo "Searching for" ${anarray[$a]}   

	NEWNAME=$(echo ${anarray[$a]} | grep -o '[0-9]\+_')


	echo NEWNAME IS $NEWNAME


		#Ok, loop through every .ogg in the directory. 
	
		for i in *.ogg; do

		    case "$i" in
				$NEWNAME*)
	        	echo "MATCH:    '$i'"

				#Be safe and remove any spaces. Annoying things ... 	        	

	        	SONG_NEWNAME=$(echo ${anarray[$a]} | sed -e s/" "/_/g)
	        	      	
	        	SONG_NEWNAME=$SONG_NEWNAME".ogg"
	        	
	        	echo $SONG_NEWNAME
	        	   	
	        	#Ok, now we need to rename the file. 	        	
	        	
	        	mv $i $SONG_NEWNAME
	        	
    	    	;;
		    *)
        		echo "no match  '$i'"
	        	;;
    		esac


		#End of the Loop.

		done


done
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
bash: ./runcbl: cannot execute binary file message Earla Harding Linux - Enterprise 0 07-27-2005 02:39 PM
Including binary file into bash script Barca Programming 2 07-01-2005 12:45 PM
bash: routine outputting both matches and non-matches separately??? Bebo Programming 8 07-19-2004 06:52 AM
Mandrake9.2, bash: /cannot execute binary file premier_la Linux - Software 10 01-23-2004 02:53 AM
Mendrake9.2, bash: cannot execute binary file premier_la Mandriva 4 01-22-2004 05:16 PM

LinuxQuestions.org > Forums > Linux Forums > Linux - General

All times are GMT -5. The time now is 03:09 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration