bash n flac

Hi All
I'm trying to take a .flac file and use metaflac to write
the tag info into a file, Then read the title portion of the tag and rename the original .flac file with this info.

lets call this script renameflac

now on the command line if I type:

metaflac myfile.flac --export-tags-to=/tmp/tmp.txt

This puts a file called tmp.txt in the /tmp directory :slight_smile:
and in this file it looks like this:
ALBUM=Self Esteem
ARTIST=The Offspring
COMPOSER=The Offspring
DESCRIPTION=
DISCID=0912a612
GENRE=Just Plain Wierd
MUSICBRAINZ_DISCID=o33.enTD6BY0zF2.Wn..dIpgsFI-
TITLE=Self Esteem
TRACKNUMBER=1
TRACKTOTAL=18

I figure I could grab the TITLE= info to rename the file but I get stuck here:

#!/bin/bash
for file in *.flac ; do
metaflac $file --export-tags-to=/tmp/tmp.txt
done

renameflac myfile.flac

This just hangs
Is this because the metaflac commands uses its flags after the file name?
And is this even possible with bash?
Thanks for your time.

I don't write bash-scripts, so i can't give you a complete solution, but a few suggestions:

1) If you use a wildcard expansion like "*.flac" and then rename the files matched by that wildcard the results may or may not get obfuscated. Rename the .flac-files to something intermediate like ".flac.processed" and in a last step (after the for .... *.flac... do.....done-loop has finished) rename them again.

2) Maybe this is not posing a problem here, but generally it is better not to use the "for file in * ; do ....done", but a pipeline instead:

find . -prune -name "*" -type f -print | while read file ; do
   ....
done

The reason is: wildcards are expanded by the shell. The shell tries to execute the line "for file in * ; do" and replaces the asterisk with a list of filenames matching it. Only then the command will be executed. If the wildcard now matches a very big list of files chances are you hit the limitation of the input line length (in POSIX 4k characters) and get an error. This will not be the case with the find command and the pipeline.

3) You write a file to /tmp without checking if this is possible. Usually it is, but it is a risk you are taking if you do not check that first. Two problems could arise: /tmp is full and you (your user account) is not allowed to write to there.

Either check first if you can indeed write or work in a pipeline instead of an intermediate file:

newname="$(metaflac myfile.flac --export-tags-to=- | sed -n 's/^TITLE=//p;q')"

Hope this helps.
bakunin

Thank you!
I belive I understand.
The problem using the find command is that the flac files are
named like this:
01_-_Track_1.flac
and the output file should be
Self_Esteem.flac

Actualy, The more I write and test this the less I understand :slight_smile:

Thanks again for your time bakunin

while this works:

for file in *.flac ; do
          metaflac $file --export-tags-to=tmp/tmp.txt
           part1=$(awk '{FS="="} /ARTIST/{print $2}' tmp/tmp.txt |sed 's/ /_/g')
           part2=$(awk '{FS="="} /TITLE/{print $2}' tmp/tmp.txt |sed 's/ /_/g')
           mv $file ${part1}_$part2.flac
 mv tmp/tmp.txt tmp/${part1}_$part2.txt
done

This doesn't:

find . -prune -name "*" -type f -print | while read file ; do
          metaflac $file --export-tags-to=tmp/tmp.txt
           part1=$(awk '{FS="="} /ARTIST/{print $2}' tmp/tmp.txt |sed 's/ /_/g')
           part2=$(awk '{FS="="} /TITLE/{print $2}' tmp/tmp.txt |sed 's/ /_/g')
           mv $file ${part1}_$part2.flac
 mv tmp/tmp.txt tmp/${part1}_$part2.txt
done

Any Idea?
Thanks again

You're pruning everything. You need to let find at least not prune the current directory. Try this one:

find . ! -name . -prune -name \*.flac -type f -print

and by the way, make sure you have read this alert.

Yep, I figured that out earlier, Its amazing what a -v after bash will do :). Thanks!!!
Also Thank you for the alert link Pererabo.

Perderabo has always told you how to solve the find problem - sorry for my sloppiness. Still, the above lines give me headaches: why use two different tools for a purpose where one would suffice?

part1="$(sed -n '/ARTIST/ {;s/^.*=//;s/ /_/g;p;}' tmp/tmp.txt)"
part2="$(sed -n '/TITLE/ {;s/^.*=//;s/ /_/g;p;}' tmp/tmp.txt)"

does the same.

bakunin

I'm the one who is sorry.. Sorry at coding :slight_smile: That line was a big help!

Just my misunderstanding of what you said earlier.
I thought you meant that any time that a .* was used,
it would be safer to use a pipe. This Awk/Sed example by Trumpen was
the only thing I could find that did what I wanted.
After re-reading your post I now understand that it is meant
only for input.

I'm in the middle of adding the error checks mentioned earlier, After I
get it done, I will put it up here for other's to peruse and/or use as needed.

Thanks again all!!

You can do it as a single command with awk, too:

part1=$(awk -F= '/ARTIST/{gsub(" ","_");print $2}' tmp/tmp.txt)
part2=$(awk -F= '/TITLE/{gsub(" ","_");print $2}' tmp/tmp.txt)

This concept is a bit hard to grasp, because an asterisk is seemingly the same as an asterisk. No, that isn't the case.

There are regular expressions which are interpreted by regexp-using programs: sed, awk, grep, ed, vi, ... In this set of regular expressions a "." stands for "any character" and the asterisk stands for "0 or more of the last expression". ".*" means therefore "any number (including 0) of any character" and will therefore match any string whatsoever. Its the "real big joker", so to say.

But all these regexps deal with *string* manipulation. You feed a string to the program and this regexp will decide if the string matches or not. In this regard awk, sed and the others work basically the same way: if the string matches (of if it doesn't match) then a certain action is performed on this string, otherwise it isn't. (If i got you interested: Dale Dougherty wrote an excellent book, "sed & awk".)

Something entirely different are *shell* wildcards. They are some sort of regular expressions too, but their meaning is another. In this set of regexps the asterisk means "any number of any characters", similar to ".*" above.

The reason, why shell expansion poses a risk is the following: suppose a commandline like

ls -l *txt

In OSes like DOS the options ("-l", "*txt") would be passed to the program, in this case the executable "ls", and ls would have to figure out if a filename matches the schema and should be displayed or not. Not so in Unix: the shell (the program you type the command into) itself will expand the wildcard and replace what you have typed by what is matched by what you typed.

Suppose there are two files "a.txt" and "b.txt" in the directory and you issue the above command. First the shell will notice the wildcard, look into the directory and find 2 files which match it. Then it will replace your command with:

ls -l a.txt b.txt

and this is, what ls will finally see. If you had entered the command this way yourself ls would not be able to tell the difference.

Now, suppose you would enter the same command in a directory where several hundreds of files matching the wildcard reside. The commandline would become pretty long, yes? Unfortunately the commandline has a limit (4k characters) and ls has also a limit. More than a certain number of arguments will not be digested. "Too many arguments" or "Argument list too long" reads the error which will be displayed then.

I hope this clarifies it.

bakunin