LinuxQuestions.org
Welcome to the most active Linux Forum on the web.
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 07-16-2010, 11:15 AM   #1
SilversleevesX
Member
 
Registered: May 2009
Posts: 181
Blog Entries: 9

Rep: Reputation: 15
(BASH) Works on the command line, hangs as a script -- what's wrong?


I'm writing a Bash script to take IPTC keywords from a text file and write them, via Exiv2, to several (first batch is 100) JPEG files in a single directory. The script has one while loop inside another while loop, both terminated, but I'm pretty sure that's not my problem. I think it's how I'm incrementing the "counter" variable, although it could also be the method of parsing the text lines from the file (using cut with delimiters that have worked fine in simpler scripts).

Here's the code as I've worked it up to this point.
Code:
#!/bin/bash
b=1
while read 'line';
do
	n=`echo $line`
	thefile=`echo $n | cut -d: -f1`
	taxt=`echo $n | cut -d: -f2`
	commaz=`echo -e $taxt | grep -o "," | wc -l | sed s/\ //g`
	while [[ $b -le $commaz ]]
	do
		gword=`echo $taxt | cut -d, -f$b`
		exiv2 -M"set Iptc.Application2.Keywords String $gword" $thefile
		echo "Writing keyword $b to $thefile."
		b=$(( $b + 1 ))
	done<testcomment.txt
	echo
done<keywords
And yes, "keywords" checks out in Crimson Editor, Emacs GUI and nano as an ASCII file with UNIX line endings. No issues on that score.

Feeding each line consecutively into a terminal (excepting the exiv2 command) works fine: each variable echoes with the part of the text line used as a variable value as it should, even when the b variable is incremented the quick&dirty way (up arrow three commands and hit enter).

Running the above script in eval mode (sh -x) stalls after setting the b variable to one and reading in the first line of text. I'd like to know why. I'd also like some advice on another reliable method of parsing the read-in lines.

Thanks in advance.

BZT

Last edited by SilversleevesX; 07-16-2010 at 11:19 AM. Reason: Mis-cited stage of stall in eval mode.
 
Old 07-16-2010, 11:37 AM   #2
David the H.
Bash Guru
 
Registered: Jun 2004
Location: Osaka, Japan
Distribution: Debian + kde 4 / 5
Posts: 6,849

Rep: Reputation: 2024Reputation: 2024Reputation: 2024Reputation: 2024Reputation: 2024Reputation: 2024Reputation: 2024Reputation: 2024Reputation: 2024Reputation: 2024Reputation: 2024
Can we see an example of the input files, so we know what it's working with? Also, exactly what output do you get?

There are multiple problems with this script, as well as some other things that I want to comment on.

1.
$(..) is recommended over `..`

2.
Code:
while read 'line';
Why is line enclosed in single-quotes? I don't think it hurts the program, but it doesn't help anything either.

3.
Code:
n=`echo $line`
This is completely unnecessary. First of all, you don't need echo here, as a simple n="$line" would do the trick. But second, there's no reason to use n at all, as $line itself will do just fine in the two commands that follow.

4.
Code:
b=$(( $b + 1 ))
This is ok, but bash will expand variables inside $((..)) automatically, so the extra $ is unneeded. b=$(( b + 1 )) will work.

Edit:

5.
Code:
commaz=`echo -e $taxt | grep -o "," | wc -l | sed s/\ //g`
grep's -o option means to output only the part that matches. So if there's a comma in the text string, you'll only get a comma in the output. Then wc -l counts the number of lines. But since the input is read one line at a time, sed will always only see either 0 or 1, depending on what grep finds, and so has nothing to do. What exactly do you want this line to do?

Edit2:

6.
Code:
while [[ $b -le $commaz ]]; do
....
done<testcomment.txt
The input file here does nothing. And due to problem 5 above, the test will only be true one time at most.

Edit 3

7.
Code:
b=1
Since b is only set to 1 outside the first loop, it will never be reset again after the first iteration. The number will just keep climbing, so the second loop will only run a single time then never again.

Last edited by David the H.; 07-16-2010 at 12:22 PM. Reason: See above edits
 
1 members found this post helpful.
Old 07-17-2010, 01:17 AM   #3
SilversleevesX
Member
 
Registered: May 2009
Posts: 181

Original Poster
Blog Entries: 9

Rep: Reputation: 15
1.Regarding item 1 in your reply:
For what it's worth, all the ticks in the script have now been changed to dollar sign-parentheses pairs. Moving on...

2.Regarding item 5, ditto:
This came from an earlier script that was written to count the commas in the line read by "while read" from the text file. It occurred to me that not every picture in the folders this script would be used with would have the same number of keywords, but that the common element in every set of keywords would be their delimiter. It's already common practice in GUI apps to use a comma, so the earlier script was written to count them and return their number with grep and sed. I confess I disregarded CW with that wc -- I found the line on a forum where the question was "How do you count specific elements in a string of text?"

In this script, I'd still like to be able to count the commas in the nested 'while' loop (or a 'for' or 'case/esac' loop if either of those are faster at the draw), since the fact remains that file gooberfoo.jpg and file RonaldMcDonald.jpg, just to use some off-the-cuff examples, are likely to have a different number of keywords. I thought I had adequately isolated the second part of the string read in from the text file by the time that loop-inside-the-loop (is the proper term here "nested"?) was run with the "taxt=`echo $n | cut -d: -f2` " command. If I keep a while loop here, where should I put the b variable so that it starts at 1 and increments with that loop and that loop quits when b reaches the same number as the number of commas in the part of the string evaluated within the variable taxt? (that's text with an "a", btw)

BZT

Just for the sake of argument, here's a text string of the type that appears in the file keywords:
Code:
guineabissaubeach.jpg:beach,daytime,mid-coast,summer,stay,vacation

Last edited by SilversleevesX; 07-17-2010 at 01:23 AM.
 
Old 07-17-2010, 02:05 AM   #4
grail
LQ Guru
 
Registered: Sep 2009
Location: Perth
Distribution: Manjaro
Posts: 9,832

Rep: Reputation: 3089Reputation: 3089Reputation: 3089Reputation: 3089Reputation: 3089Reputation: 3089Reputation: 3089Reputation: 3089Reputation: 3089Reputation: 3089Reputation: 3089
So my general philosophy here would be KISS (keep it simple silly). I don't know a lot about exiv2 but the below worked for me based on your input:
Code:
#!/bin/bash

for i in $(sed 's/[:,]/ /g' keywords)
do
	if [[ $i =~ .jpg$ ]]
	then
		thefile=$i
	else
		echo exiv2 -M\"set Iptc.Application2.Keywords String $i\" $thefile
	fi
done
Just take out the echo and and the escapes before " and it should be good to go.

Last edited by grail; 07-17-2010 at 02:49 AM.
 
1 members found this post helpful.
Old 07-17-2010, 06:03 PM   #5
SilversleevesX
Member
 
Registered: May 2009
Posts: 181

Original Poster
Blog Entries: 9

Rep: Reputation: 15
Thumbs up Simple is best -- many thanks.

grail,

Thanks a bunch. With one minor tweak ("add" instead of "set" in the exiv2 command), it worked.

"set" in exiv2 is for IPTC, EXIF, and XMP fields and elements that are one-liners (like Date & Time, Caption Writer or Location) "add" is for lists like keywords and supplemental categories, contacts, revision dates, and the like. Hey, like you said, you weren't familiar. No big cheese.

Again, thanks. Lucky me -- two issues posted in as many days and solved likewise. 8)

BZT
 
Old 07-17-2010, 10:54 PM   #6
grail
LQ Guru
 
Registered: Sep 2009
Location: Perth
Distribution: Manjaro
Posts: 9,832

Rep: Reputation: 3089Reputation: 3089Reputation: 3089Reputation: 3089Reputation: 3089Reputation: 3089Reputation: 3089Reputation: 3089Reputation: 3089Reputation: 3089Reputation: 3089
No probs ... glad we could help. Please remember to mark as SOLVED
 
1 members found this post helpful.
Old 07-18-2010, 07:23 PM   #7
SilversleevesX
Member
 
Registered: May 2009
Posts: 181

Original Poster
Blog Entries: 9

Rep: Reputation: 15
Just one more niggling little thing. I could almost answer this question myself, but I want to be sure of my ground before I do.

My own practice with IPTC supplemental categories has been to use a list of words that, by comparison with the one for keywords, is only a few shades different. Both are the lists I've generated in, and carried over several version upgrades for use with, iView Media Pro (now MS Expression Media, the first release of version 2 of which I now have and use with these lists). I presume that to write a script to introduce the "supp cats" into these JPEGs, the only changes necessary would be to change the name of the file read in by the sed command and the field written to by the exiv2 -M"add..." one; everything else should work as is.

BZT
 
Old 07-19-2010, 12:28 AM   #8
grail
LQ Guru
 
Registered: Sep 2009
Location: Perth
Distribution: Manjaro
Posts: 9,832

Rep: Reputation: 3089Reputation: 3089Reputation: 3089Reputation: 3089Reputation: 3089Reputation: 3089Reputation: 3089Reputation: 3089Reputation: 3089Reputation: 3089Reputation: 3089
Sounds plausible, but like all good programming, you should test on a copy first to make sure you are on the right path

Good luck.
 
1 members found this post helpful.
Old 07-20-2010, 01:44 PM   #9
SilversleevesX
Member
 
Registered: May 2009
Posts: 181

Original Poster
Blog Entries: 9

Rep: Reputation: 15
Getting ambitious

Of course, of course.

I hope it doesn't break any rules or conventions to continue this thread along mainly the same lines as the OP and subsequent posts.

This morning I got ambitious and wrote up a single-line text file that had both Keywords and Supplemental Categories on the same line, following the filename (with path) as I gave in my example (post from the 17th), and delimited by a semicolon instead of a colon from the preceding array of keywords. I introduced another for/do/done loop thusly:
Code:
for j in $(sed 's/[:,]/ /g' listannots)
do
	if [[ $j =~ .jpg$ ]]
	then
		thefile=$j
	else
		exiv2 -M"add Iptc.Application2.SuppCategory String $j" $thefile
	fi
done
..and ran it on the file specified in the text file listannots. When I viewed both keywords and supp cats in a GUI app, the latter had, as its first entry, the full pathname of the file, followed by the other delimiter (the colon); the next five lines were the keywords, and the supplemental cats didn't start until the seventh line. I'm curious as to how I might re-format that for command and its sed to ignore everything until the delimiter that marks off the beginning of the categories list. Or am I better off with a whole separate script with which to enter the supplemental categories?

BZT

Last edited by SilversleevesX; 07-20-2010 at 01:45 PM.
 
Old 07-20-2010, 10:29 PM   #10
grail
LQ Guru
 
Registered: Sep 2009
Location: Perth
Distribution: Manjaro
Posts: 9,832

Rep: Reputation: 3089Reputation: 3089Reputation: 3089Reputation: 3089Reputation: 3089Reputation: 3089Reputation: 3089Reputation: 3089Reputation: 3089Reputation: 3089Reputation: 3089
Sorry ... you will have to dumb it down for me a little.
Try showing what is in listannots so I can follow your logic.
Maybe also show what the following line should look like in your mind based on the input from the file:
Code:
exiv2 -M"add Iptc.Application2.SuppCategory String $j" $thefile
 
1 members found this post helpful.
Old 07-21-2010, 05:23 PM   #11
SilversleevesX
Member
 
Registered: May 2009
Posts: 181

Original Poster
Blog Entries: 9

Rep: Reputation: 15
Quote:
Originally Posted by grail View Post
Sorry ... you will have to dumb it down for me a little.
Try showing what is in listannots so I can follow your logic.
Maybe also show what the following line should look like in your mind based on the input from the file:
Code:
exiv2 -M"add Iptc.Application2.SuppCategory String $j" $thefile
I thought, to save a little time, that I might put both keywords and categories in the same text file as the FwP (filename with path), one-two-three, for each file in a folder. I assumed that in order to distinguish between one set of items and another, the sets should be set off by a different delimiter. I also figured that the for-sed combination from the script that worked for writing keywords, would work for supplemental categories if they were set off by, for instance, a colon instead of the semicolon. What I didn't expect, but naturally what I got, was sed reading in both sets, and prefixing the first item with the FwP in the Supplemental Categories 'tab' of my GUI app.

I imagined that
Code:
exiv2 -M"add Iptc.Application2.SuppCategory String $j" $thefile
would write everything in the second set of words, delimited like the first between one another by commas, and ignore the first set. That the for-do-sed-done loop would "jump" the keywords but write just the categories to the file at the appropriate place. I got, as I described above, both more and less than I bargained for.

Here's the kind of line that appears in listannots:
Code:
guineabissaubeach.jpg:beach,daytime,mid-coast,summer,stay,vacation;beach,day,midcoast,midday,summer,Sunday,vacation,warm
Now that you see the second set appended like that, maybe you can help me tweak the for-sed combo to give sed a "pole" with which to "vault over" the keys and read and pass the supp cats to the exiv2 -M"add Iptc.Application2.SuppCategory..." command.

BZT
 
Old 07-21-2010, 07:40 PM   #12
grail
LQ Guru
 
Registered: Sep 2009
Location: Perth
Distribution: Manjaro
Posts: 9,832

Rep: Reputation: 3089Reputation: 3089Reputation: 3089Reputation: 3089Reputation: 3089Reputation: 3089Reputation: 3089Reputation: 3089Reputation: 3089Reputation: 3089Reputation: 3089
So the issue you have is that sed will make its changes to the whole line and so the for loop looks at the complete line now broken into pieces.
As you have now a need split the initial line and then act on the two separate halves, I came up with the following:
Code:
#!/bin/bash

tmp_arr=($(sed 's/;/ /' listannots))

for i in $(echo ${tmp_arr[0]} | sed 's/[:,]/ /g')
do
	if [[ $i =~ .jpg$ ]]
	then
		thefile=$i
	else
		exiv2 -M"set Iptc.Application2.Keywords String $i" $thefile
	fi
done

for j in $(echo ${tmp_arr[1]} | sed 's/,/ /g')
do
    exiv2 -M"add Iptc.Application2.SuppCategory String $j" $thefile
done
There may be cleaner ways but I will leave you to play
 
Old 08-01-2010, 02:41 AM   #13
SilversleevesX
Member
 
Registered: May 2009
Posts: 181

Original Poster
Blog Entries: 9

Rep: Reputation: 15
Now the first script won't work.

I'm referring to grail's post from the 7th -- or rather the script I made from it which worked when it was new:
Code:
#!/bin/bash

for i in $(sed 's/[:,]/ /g' keywords)
do
	if [[ $i =~ .jpg$ ]]
	then
		thefile=$i
	else
		exiv2 -M"set Iptc.Application2.Keywords String $i\" $thefile
	fi
done
Call me a dunce when it comes to getting my mind around sed, but I can't puzzle out why there's an "else" (do) in that if/fi conditional loop that actually has the command one wants to execute on the files read in from the text list by sed. Unless it's there on the very off-chance that one of the lines in said file does not refer to a JPEG in that directory or anywhere else?



BZT
Edit1: It's writing again -- as you see above, I had a very old save with "set" instead of "add" in the Exiv2 command and was using that (egg on face). But I also just now noticed the script isn't strictly delimiting by colons and commas -- in one file in a small set I just ran it on, the single keyword entry "cute ass" got split into "cute" (comma)"ass" and put on two lines: split at the space, I'm presuming. Maybe customizing the $IFS would help with that?

Edit2: Customizing IFS didn't do any good -- neither did setting off each keyword for that file in double-quotes. After writing to that one file, having cleared the original keywords twice, between the sed and Exiv2 "cute ass" became "cute,ass", and it was the one and only keyword (one word or two) surrounded by quotes. I think I need a serious for-ID10Ts explanation of sed. NE1 know where I can get one for cheap?

Last edited by SilversleevesX; 08-01-2010 at 03:35 AM. Reason: ::Egg::
 
Old 08-01-2010, 03:54 AM   #14
SilversleevesX
Member
 
Registered: May 2009
Posts: 181

Original Poster
Blog Entries: 9

Rep: Reputation: 15
Getting back to concept

Putting it in both broad and narrow terms, and including the rules-of-thumb for formatting I apply to my directory lists, this is what a script like this would have to do with a text file list of files in one directory:

1. Read in the filename. The filename is set off from the keywords by a colon.
Code:
/cygdrive/c/blu/newest/five/amanor-1005-005.jpg:
2. Count the number of keywords in each line. The keywords are one- and two-word substrings delimited by commas.
Code:
adult,cute,cute ass,indoor,naked,redhead
3. Assign a variable to each keyword, one after the other, and pass each to an exiv2 -M"add Iptc.Application2.Keywords" command for the file in the folder whose name is the string as read in from the text file (that part of the whole line that appears before the colon) in succession until the end of the list read in from the text file is reached.
Code:
for x in $(ls $(key) | wc -l); do;done
4. Terminate itself (gently).

Can this be done in BASH? I'm beginning to doubt it.

BZT

Last edited by SilversleevesX; 08-01-2010 at 03:56 AM.
 
Old 08-01-2010, 06:32 AM   #15
SilversleevesX
Member
 
Registered: May 2009
Posts: 181

Original Poster
Blog Entries: 9

Rep: Reputation: 15
Lightbulb

I think I've got it:
Code:
#!/bin/bash
while read 'line';
do
	souse=$(echo $line)
	drunk=${souse%:*}
	barfly=$(basename "$drunk")
	verydrunk=$(echo $souse | cut -d":" -f2)
	booze=${verydrunk//[!,]/}
	bartab=$(echo ${#booze}) 
	w=1
	while [ $w -le $bartab ]
	do
		key=$(echo $verydrunk | cut -d, -f$w)
		exiv2 -M"add Iptc.Application2.Keywords String $key" $drunk
		w=$[w+1] ;
	done
	echo -e "Keywords added to file $barfly."
done<fivefootone
fivefootone only had a single file referenced and eleven keywords, but it worked. Earlier I tried the list item where the sed script split "cute" and "ass" into two; this one (or rather its one-by-one command line precursors) kept them together. So much for sed and offsetting with established delimiters.

I almost went with a while loop for the whole thing, to keep track of the total number of files worked on (file w of X), and echo that back to the user along with the "Finished this one" kind I did add, but all-of-a-sudden I blanked on how to replace a "while read" command/expression.

BZT
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

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 Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
mkdir won't work in a script but works on command line ifeatu Linux - Newbie 3 02-04-2010 02:26 PM
Bash script works from command line, fails from cron cmfarley19 Linux - General 4 08-14-2009 12:24 PM
works on command line but not in bash script tara Linux - General 7 02-09-2009 03:57 AM
Perl Script works from command line, but not when evoked by Nagios gfem Linux - General 10 06-05-2008 03:59 PM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 05:20 AM.

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