ExifTool from bash script

Started by Michael Meyer, February 28, 2012, 07:48:53 PM

Previous topic - Next topic

Michael Meyer

I have written a bash script that will iterate incoming arguments, build a set of keywords to update and execute ExifTool to update an image.

I am having problems with ExifTool interpreting the arguments passed to it.

I have extracted the gist of what I am doing into a simplified bash script.

Executing ExifTool from within the script fails, it seems to interpret the argument differently. 
Echoing to sh seems to work.
Copying the echo output and pasting into terminal also works.

If the Subject has no spaces it works fine.

I am getting errors:
Error: File not found - Village"
Error: File not found - Arnold"

I'm baffled, is this some shell esoterica or a problem with the ExifTool wrapper script.

setsubjects
=======
#!/usr/bin/env bash

filename=test123.jpg
allsubj="-xmp-dc:Subject+=\"Places|Europe|Germany|Small Village\" -xmp-dc:Subject+=\"People|Meyer|Georg Arnold\""

# This works
echo exiftool -overwrite_original ${allsubj} ${filename} | sh

# This does not work
exiftool -overwrite_original ${allsubj} ${filename}

# echo shows the right thing, copy output, paste into terminal works
echo exiftool -overwrite_original ${allsubj} ${filename}


Phil Harvey

Wow.  Tricky.

I haven't figured out the answer yet, but it is clear that the echo command suffers the same problem.  It appears the echo works, but it does not.  Change the space into more spaces to see what I mean:

allsubj="-xmp-dc:Subject+='Places|Europe|Germany|Small    Village'"

echo exiftool -overwrite_original ${allsubj} ${filename}


produces this:

exiftool -overwrite_original -xmp-dc:Subject+='Places|Europe|Germany|Small Village' a.jpg

I don't have time to think about this more now, but I will when I get a chance.

- Phil
...where DIR is the name of a directory/folder containing the images.  On Mac/Linux, use single quotes (') instead of double quotes (") around arguments containing a dollar sign ($).

Michael Meyer

I ran the script with bash -x and saw the differences in the echo statements below


#!/usr/bin/env bash -x

filename='test123.jpg'
allsubj="-xmp-dc:Subject+=\"Places|Europe|Germany|Small  Village\" -xmp-dc:Subject+=\"People|Meyer|Georg  Arnold\""

echo "${allsubj}"

echo ${allsubj}


The echo with the quotes executes as:  note has the 2 spaces preserved:

+ echo '-xmp-dc:Subject+="Places|Europe|Germany|Small  Village" -xmp-dc:Subject+="People|Meyer|Georg  Arnold"'


without quotes, bash interprets the var as:

+ echo '-xmp-dc:Subject+="Places|Europe|Germany|Small' 'Village"' '-xmp-dc:Subject+="People|Meyer|Georg' 'Arnold"'


Strange, I'll add quotes around my variable.

Thanks,

Michael Meyer

Hmmm, still not right.  I noticed that in the first one the string is passed in as one parameter, resulting in setting the xmp subject to include the command.



+ exiftool -overwrite_original '-xmp-dc:Subject+="Places|Europe|Germany|Small  Village" -xmp-dc:Subject+="People|Meyer|Georg  Arnold"' test123.jpg


Resulting subject:

exiftool -G -s -subject test123.jpg
[XMP]           Subject                         : "Places|Europe|Germany|Small  Village" -xmp-dc:Subject+="People|Meyer|Georg  Arnold"

Phil Harvey

OK, here is the solution:

#!/bin/sh
filename='test123.jpg'
allsubj="-xmp-dc:Subject+=Places|Europe|Germany|Small Village"
exiftool -overwrite_original "${allsubj}" "${filename}"


I've used /bin/sh here, but bash should work too (/bin/sh is smaller and lighter though, so the preferred shell for scripts).

The problem is that the shell strips one level of quotes when assigning "allsubj", but apparently doesn't strip quotes from within a shell variable after interpolating the value within a command line... which makes sense now that I think about it.  So you must add quotes around the variables in the command instead.

- Phil
...where DIR is the name of a directory/folder containing the images.  On Mac/Linux, use single quotes (') instead of double quotes (") around arguments containing a dollar sign ($).

Alan Clifford

Phil, that still doesn't work with

allsubj="-xmp-dc:Subject+=Places|Europe|Germany|Small Village -xmp-dc:Subject+=People|Meyer|Georg"


You get
Subject: Places|Europe|Germany|Small Village -xmp-dc:Subject+=People|Meyer|Georg

Michael Meyer

I rewrote the script to use an array instead of concatenated strings, seems to work better.


#!/usr/bin/env sh -x

filename='test123.jpg'

# collect subjects as an array
allsubj[0]="-xmp-dc:Subject+=\"Places|Europe|Germany|Small  Village\""
allsubj[1]="-xmp-dc:Subject+=\"People|Meyer|Georg  Arnold\""

# clear subject
exiftool -q -overwrite_original -xmp-dc:Subject= ${filename}

# This works with array
exiftool -q -overwrite_original "${allsubj[@]}" ${filename}

# Check if it worked
exiftool -G -s -xmp:subject ${filename}


The shell debug output shows that the array expands to one argument per array element, and retains the extra spaces in the subject values:

+ filename=test123.jpg
+ allsubj[0]='-xmp-dc:Subject+="Places|Europe|Germany|Small  Village"'
+ allsubj[1]='-xmp-dc:Subject+="People|Meyer|Georg  Arnold"'
+ exiftool -q -overwrite_original -xmp-dc:Subject= test123.jpg
+ exiftool -q -overwrite_original '-xmp-dc:Subject+="Places|Europe|Germany|Small  Village"' '-xmp-dc:Subject+="People|Meyer|Georg  Arnold"' test123.jpg
+ exiftool -G -s -xmp:subject test123.jpg


and the subject written into the xmp shows as 2 elements instead of the previous one concatenated string:

[XMP]           Subject                         : "Places|Europe|Germany|Small  Village", "People|Meyer|Georg  Arnold"



FYI, in my actual script I have this as a function:

#!/usr/bin/env sh

function setSubject {
filename=$1
shift

declare -a allsubj
index=0
while [ $# -ne 0 ]; do
allsubj[index]="-xmp-dc:Subject+=\"$1\""

((index++))
shift
done

if [ ${#allsubj[@]} -ne 0 ]; then
exiftool -q -overwrite_original "${allsubj[@]}" ${filename}
fi
}

# test it
setSubject test123.jpg "Places|Europe|Germany|Small  Village" "People|Meyer|Georg  Arnold"
exiftool -G -S -Subject -HierarchicalSubject test123.jpg


Thanks all,

Phil Harvey

Right.  Definitely trickier to have 2 command-line arguments in the same shell variable.

I've never used an interpolated array like this in shell scripting, so you've taught me something.  Smart.

- Phil
...where DIR is the name of a directory/folder containing the images.  On Mac/Linux, use single quotes (') instead of double quotes (") around arguments containing a dollar sign ($).

Michael Meyer

After some more ExitTool doc reading I could also have collected the just the keywords in a delimited string, with a few extra quotes and escaped quotes:


#!/usr/bin/env sh -x

filename='test123.jpg'

# collect subjects
subj1="Places|Europe|Germany|Small  Village"
subj2="People|Meyer|Georg  Arnold"

allsubj="${subj1}//${subj2}"

exiftool -q -overwrite_original -sep "//" -xmp-dc:Subject=\""${allsubj}"\" ${filename}


Enough fun, back to work.   :)


brambil

Hallo to all.

I know is necroposting, but I have the some trouble.

The script returns file not found, but if I execute the command as reported in error copying and pasting in the terminal line it works.

Is the problem still here?

echo "exiftool -p -Keywords=\"${keyword}\" -Title+=\"ERA: ${fname}\" $fname -overwrite_original" | bash
could be a good solution?

And to do this any idea or better solution?
(I try to set the date, if it works (=no Datetimeoriginal was set)go on, otherwise maybe I want to overwrite it.


"exiftool -wm cg -DateTimeOriginal=\"${anno}':01:01 00:00:00'\" -overwrite_original ${fname}"
if [ $? -ne 0 ] #non avevo già una data e l'ho messa
then
datafile=(echo "exiftool -s3 -createdate \"$fname\"") | bash
while true
do
if [ "$anno" -lt 2021 ] #la scrittura della data in EXIF la faccio solo se è un anno minore di 2021. Se ho messo 5060 per indicare anni 50-60 non lo metto
then
read -e -p "Sovrascrivere la data $datafile con $anno?[Yes/No/Vuoto]" -i "Y" yn </dev/tty
else
read -e -p "Sovrascrivere la data $datafile con $anno?[Yes/No/Vuoto]" -i "N" yn </dev/tty
fi
case $yn in
[Yy]* ) echo "exiftool -p -DateTimeOriginal=\"${anno}':01:01 00:00:00'\" -overwrite_original \"${fname}\"" | bash;break;;
[Nn]* ) break;; #Non fo una sega
[0123456789][0123456789][0123456789][0123456789]* ) $("exiftool -p -DateTimeOriginal=\"${yn}:01:01 00:00:00\" -overwrite_original \"${fname}\"");break;;
* ) echo "Inserire Y[es]/N[o] o l'anno";;
esac
done
fi


The problem is also with 12.16

Thanks.