4

Context: zsh Catalina MacOS:

The executable script BatesStamp engages imagemagick to stamp a number to a jpg file:

# BatesStamp: OVERWRITES and stamps ONE file with COUNTER  (upper left corner)
# usage  ./BatesStamp  COUNTER  PATH_FILE   
# to be used with find & -exec: https://unix.stackexchange.com/a/96239/182280   


COUNTER=$1     # 1st argument = number to be stamped upon .jpg file
PATH_FILE=$2   # 2nd argument = /path_to_file/Filename.jpg

convert $PATH_FILE  -auto-orient -gravity northWest  -font "Arial-Bold-Italic" -pointsize 175 \
   -fill red  -annotate +30+30   "$COUNTER"  $PATH_FILE;
   ((COUNTER++))  #https://stackoverflow.com/a/21035146/4953146
   echo "watermarked i= $COUNTER  $PATH_FILE"

The goal is to stamp all .jpg files in directory tree with a unique number. I believe $COUNTER must be incremented every time it is called: this ensure each file is stamped with a unique number. The strategy is to traverse all subdirectories with find to identify .jpg files and BatesStamp each .jpg file with $COUNTER++.

This script goBatesStamp.sh traverses subdirectories to exec each .jpg files to be processed by BatesStamp

# goBastesStamp.sh
cd /Users/user/Desktop/AITH_USB_Hope_Submitted_MyCloud/PhotoGraphs_Work/A_Building_NoBulkhead. # navigate to top level directory
COUNTER=100   # initialize COUNTER
find /Users/user/Desktop/AITH_USB_Hope_Submitted_MyCloud/PhotoGraphs_Work/A_Building_NoBulkhead -iname "*.jpg" -exec ./BatesStamp $((COUNTER+=1)) {} \;  

The problem is in the line beginning with the find command. Specifically, the COUNTER does not increment.

Test show that .jpg files are stamped with the number 100 It seams that the $((COUNTER++)) is the problem. What is the proper syntax to increment the COUNTER by one and feed the incremented COUNTER into ./BatesStamp?

2
  • Question Deleted. BTW, I have discovered that comments may NOT precede #! /bin/zsh - however, that is whole new discussion, but may be helpful for readers. TL\DR: ensure #! /bin/zsh - is on line 1 with nothing before it.
    – gatorback
    Nov 20 at 15:39
  • Yes, those #! have to be the first two bytes of the file. That is the special signal to the system (not the shell) that the file is a script. As far as the shell is concerned, that line is just a comment as it starts with # Nov 20 at 15:57
7

In the

find ... -exec ./BatesStamp $((COUNTER+=1)) {} ';'

shell code, that $((COUNTER+=1)) is expanded by the shell that interprets that command line before invoking the find command, so find is invoked as:

find ... -exec ./BateStamp 101 {} ';'

And for each file it finds, it calls ./BateStamp 101 path/to/the/file.

Even if you changed it to:

export COUNTER=100
find ... -exec sh -c 'for f do ./BatesStamp "$((COUNTER+=1))"; done' sh {} +

for find to start a shell that increments its own $COUNTER variable and processes several files in a loop, that would still not work as find with -exec cmd {} + may still call cmd several times, each with a batch of files, and each invocation of that sh above would start at 100 again.

Here, you'd need one invocation of sh or some other shell or programming language that processes all the files in one invocation.

Since you're already using zsh, you should be able to do:

#! /bin/zsh -

top=/Users/user/Desktop/AITH_USB_Hope_Submitted_MyCloud/PhotoGraphs_Work/A_Building_NoBulkhead
n=100
for file in $top/**/*.jpg(NDn.); do
  mogrify -auto-orient \
          -gravity northWest \
          -font "Arial-Bold-Italic" \
          -pointsize 175 \
          -fill red \
          -annotate +30+30 $n \
          -- $file &&
    print -ru2 -- $file was watermarked with $n
  (( n++ ))
done

Here using zsh recursive globs (where **/ matches 0 or more levels of subdirectories) instead of find to find the jpg files (restricted to regular files with the . glob qualifier and sorting the input list numerically with n¹ instead of processing them in random order like find does) and using mogrify instead of convert as that's the ImageMagick command to edit an image in-place.

Note that for a script to be interpreted by an interpreter other than sh when executed directly, you need a she-bang line (#! /path/to/the/interpreter) to tell the system which interpreter to use to interpret the contents of the file. Or you can invoke the interpreter manually on the file: zsh /path/to/that/file, or have the interactive shell you're currently using (if zsh) interpret it directly with source /path/to/that/file or in a subshell so it doesn't alter the environment of your shell session with (source /path/to/that/file).


¹ without the n (for numericglobsort which is an option that can also be enabled globally) glob qualifier, the sorting is lexical, so image2.jpg for instance would come after image10.jpg because 2 comes after 1 in collation order.

² if you wonder about the extra - in the #! /bin/zsh - shebang, see Why the "-" in the "#! /bin/sh -" shebang?

6
  • Bon Jour! Thank you for posting a promising solution. I was excited to test the glob and posted results and questions in a new question. Your experience and insight are appreciated.
    – gatorback
    Nov 20 at 13:55
  • @gatorback, see edit, in particular the part about how to make sure the script is interpreted by zsh and not by sh. I also gave links to relevant parts of zsh's manual. Nov 20 at 14:39
  • Thank you for your feedback. In order to simplify the matter, I have posted a screenshot: I believe that it confirms that zSh and not BASH is engaged. I am struggling (not successful) in globbing jpg files as shown in the new posting and willing to learn / follow instructions.
    – gatorback
    Nov 20 at 14:41
  • @gatorback, no, it does confirm you executed the script directly and that it was interpreted by sh, likely because you didn't add a #! /bin/zsh - line at the top. Nov 20 at 14:43
  • Giving a sh extension to a zsh script is also quite misleading. In any case, the extension is not needed and not relevant nor used to determine what shell / programming language interpreter to use to interpret the script. Nov 20 at 14:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.