Page 1 of 2
runprogram Command in Mac OS
Posted: Thu Dec 14, 2023 9:00 am
by JohnF
I've been successful using runprogram to call Exiftool (a widely distributed command line program that accesses image files and their associated meta data) in Windows
I have two cases where I call exiftool. Below are the code snippets for the Windows implementation, which work as expected.
The first case (creating a comprehensive list of keywords) includes sorting, reducing the list to unique entries, and outputting to a file. Because this is piped it required that I execute it by first calling "cmd /c". Here's the working Windows code.
Code: Select all
CodeSelect
Workingdirectory$=""
Commandline$ = " /c exiftool -r " + Chr(34) + Input_Directory$ + Chr(34) + " -Keywords -b -sep " + Chr(34) + "\n" + Chr(34) + " -sep " + Chr(34) + "\n" + Chr(34) + " | sort /unique > " + Chr(34) + Output_Directory$ + "Keyword Listing.txt" + Chr(34)
Result = RunProgram("cmd",Commandline$,Workingdirectory$,#PB_Program_Hide|#PB_Program_Wait)
The second case (selectively copying images to an output folder) was achieved by invoking exiftool directly. Here's the working Windows code:
Code: Select all
Exiftool$ = "exiftool"
Workingdirectory$=""
Commandline$ = " -r " + Chr(34) + Input_Directory$ + Chr(34) + " -if " + Chr(34) + "$rating >="+Rating$ + Keyword_Filter$ + Chr(34) + " -o " + Chr(34) + Output_Directory$ + Output_String$ + Chr(34)
Result=RunProgram(Exiftool$,Commandline$,Workingdirectory$,#PB_Program_Hide | #PB_Program_Wait)
Both work just as they should in Windows.
So I'm 100% new to the mac world. I've scoured the internet, looking also for python equivalent examples, etc. and seen some posts on this site too. I've believe I've accounted for special characters and have substituted with Char(39) to use a single quote where needed (though not reflected in the original working Windows posted above). It seems that my difficulties are more fundamental and that exiftool is never really even called. I've experimented with using either "open", "sh" or "bash" in the same fashion as "cmd /c" but as of yet, no luck. As mentioned in my previous post, when directly entering a good command string in to a Terminal window, things are good.
On the Mac, how do I call exiftool with all the associated syntax for these two cases - one to mimic using "cmd /c" as the principle program called with exiftool and its syntax passed as the argument and the other to call exiftool and its argument directly? As you can see my calling program (PureBasic) is flexible - it allows me to specify the command, command line string, and working directory. In addition, I can stipulate if I wish to have it execute hidden, to hold execution, etc..
An answer to either would be great, both would be fantastic. Do you have a tutorial on this?
Re: runprogram Command in Mac OS
Posted: Thu Dec 14, 2023 12:11 pm
by mk-soft
Not testet ...
Code: Select all
Exiftool$ = "exiftool"
Workingdirectory$=""
Commandline$ = " -r " + Chr(34) + Input_Directory$ + Chr(34) + " -if " + Chr(34) + "$rating >="+Rating$ + Keyword_Filter$ + Chr(34) + " -o " + Chr(34) + Output_Directory$ + Output_String$ + Chr(34)
Result=RunProgram("open", "-a exiftool" + Commandline$,Workingdirectory$,#PB_Program_Hide | #PB_Program_Wait)
Debug Result
Re: runprogram Command in Mac OS
Posted: Fri Dec 22, 2023 9:53 am
by Piero
Code: Select all
Procedure simpleShell(ShellCommand$)
Protected shell
shell = RunProgram("/bin/zsh","","", #PB_Program_Open|#PB_Program_Write)
If shell
WriteProgramStringN(shell,ShellCommand$)
WriteProgramData(shell,#PB_Program_Eof,0)
While ProgramRunning(shell) : Delay(10) : Wend ; Wait until program quits (optional)
CloseProgram(shell)
EndIf
EndProcedure
Structure daResults
Out.s
Err.s
ExC.w ; exit code
EndStructure
Global Sh.daResults
Procedure Shell(ShellCommand$, AddReturns = #True)
Protected Err$, tmper$, Output$, shell, Exc.w = -1 ; -1 on failed launch
shell = RunProgram("/bin/zsh","","",
#PB_Program_Open|#PB_Program_Write|#PB_Program_Read|#PB_Program_Error )
If shell
WriteProgramStringN(shell,ShellCommand$)
WriteProgramData(shell,#PB_Program_Eof,0)
While ProgramRunning(shell)
If AvailableProgramOutput(shell)
Output$ + ReadProgramString(shell)
If AddReturns : Output$ + Chr(13) : EndIf
EndIf
tmper$ = ReadProgramError(shell)
If tmper$ : Err$ + tmper$ + Chr(13) : EndIf
Wend
Exc = ProgramExitCode(shell) : CloseProgram(shell)
EndIf
Sh\Out = Output$ : Sh\Err = Err$ : Sh\ExC = Exc
EndProcedure
; Print Single-Quoted Hello in Terminal: echo \'Hello\'
rprg = RunProgram("echo", ~"\\'Hello\\'", "", #PB_Program_Open | #PB_Program_Read)
If rprg
While ProgramRunning(rprg)
If AvailableProgramOutput(rprg)
Output$ + ReadProgramString(rprg)
EndIf
Wend
CloseProgram(rprg)
EndIf
Debug Output$ ; Ooops!!!!!!!
Debug ~"\r------\r\r"
Shell(~"echo \\'Hello\\'") ; uses WriteProgramString to zsh
Debug Sh\Out
Shell(~"echo '\"Hello\"'") ; double-quoted (in Terminal: echo '"Hello"')
Debug Sh\Out
Edit: added simpleShell
Re: runprogram Command in Mac OS
Posted: Sat Dec 23, 2023 4:12 pm
by Piero
Also,
Code: Select all
Procedure.s QuoteString(str$) ; useful to quote FILE/FOLDER PATHS
ProcedureReturn "'" + ReplaceString(str$, "'", "'\''") + "'"
EndProcedure
; if you want it to behave EXACTLY like Terminal,
; e.g., recognize commands added to $PATH:
Macro Terminal(SHELLCOMMAND)
Shell("zsh -lc " + QuoteString(SHELLCOMMAND)) ; (Shell from post above)
EndMacro ; note for Gurus: this doesn't work for stuff (functions, aliases…) in ~/.zshrc
; use URLEncoder() for URLs
Edit: changed QuotePath to QuoteString and added some tips
Re: runprogram Command in Mac OS
Posted: Thu Jan 25, 2024 9:59 am
by Piero
Dedicated to mk (if this code can be useful)
Code: Select all
; Mostly sh this time (Terminal uses zsh…)
Structure daResults : Out.s : Err.s : ExC.w : EndStructure
Global Sh.daResults, daShell$="/bin/sh"
Procedure z_shShell(ShellCommand$, AddReturns = #True)
Protected Err$, tmper$, Output$, shell, Exc.w =-1 ; exit code -1 on failed launch
shell = RunProgram(daShell$,"","",
#PB_Program_Open|#PB_Program_Write|#PB_Program_Read|#PB_Program_Error )
If shell
WriteProgramStringN(shell,ShellCommand$)
WriteProgramData(shell,#PB_Program_Eof,0)
While ProgramRunning(shell)
If AvailableProgramOutput(shell)
Output$ + ReadProgramString(shell)
If AddReturns : Output$ + Chr(13) : EndIf
EndIf
tmper$ = ReadProgramError(shell)
If tmper$ : Err$ + tmper$ + Chr(13) : EndIf
Wend
Exc = ProgramExitCode(shell) : CloseProgram(shell)
EndIf
Sh\Out = Output$ : Sh\Err = Err$ : Sh\ExC = Exc
EndProcedure
Macro zsh(s,r=#True) : daShell$="/bin/zsh" : z_shShell(s,r) :EndMacro
Macro sh(s,r=#True) : daShell$="/bin/sh" : z_shShell(s,r) : EndMacro
Macro as(s,r=#False) : sh(~"osascript\n"+s,r) : EndMacro ; sh applescript (osascript)
Macro js(JS,r=#False) ; sh JAVASCRIPT (osascript)
sh( ~"osascript -l JavaScript\n(function(){" + JS + "})()" ,r)
EndMacro
; **************************************************************
zsh(~"a=1;b=1;if [ $a -eq $b ];then;echo \"Same\";else;echo \"Different\";fi")
Debug sh\Out ; zsh script
sh(~"a=1\nb=2\nif [ $a -eq $b ]\nthen\necho \"Same\"\nelse\necho \"Different\"\nfi")
Debug sh\Out ; sh script
sh(~"cd /NO/DIR/ERROR || echo 'WHOOOPS! Shell Error! --> '", #False) ; , #False: don't add returns
Debug sh\Out + sh\Err ; sh (error) || echo
js( ~"var a=1;var b=1;if(a!=b){return'Different';}else{return'Same';}" )
Debug sh\Out ; sh javascript (osascript)
as(~"tell app\"finder\"\nactivate\nbeep\nend\n\"Finder Just Beeped\"")
Debug sh\Out ; sh AppleScript (osascript) - "return" not needed in this case
as(~"tell app \"Finder\" to display dialog \"Copy AppleScript to Clipboard?\"with icon stop"):If sh\Err:End:EndIf
; Puts an AppleScript "like as if copied in Script Editor" on clipboard
as(~"set the clipboard to \"tell application \\\"Finder\\\" -- tell finder\\ractivate\\rbeep\\rend tell\\r\\\"Finder Just Beeped\\\"\"")
; Translates AppleScript code on clipboard to obtain a PB string like below
; Better compile your AppleScript code, before copying it…
Define MyAScr.s = ~"osascript\n"+
~"set c to the clipboard as string\n"+
~"property dacut : \"\\\\n\\\"+\\r~\\\"\"\n"+
~"set pbt to \"\"\n"+
~"set rc to false -- false = do not remove comments\n"+
~"try\n"+
~"display dialog \"Remove -- Comments?\\r\\rAVOID -- in strings!!!\" buttons {\"Keep\", \"Remove\"} cancel button 1 default button 2\n"+
~"set rc to true\n"+
~"end try\n"+
~"repeat with i in paragraphs of c\n"+
~"set i to i as string\n"+
~"set i to replace_chars(i, tab, \"\") -- editor MUST use TABS to indent...\n"+
~"if rc then -- remove comments\n"+
~"set o to offset of \"--\" in i\n"+
~"if o > 1 then\n"+
~"set i to (characters 1 thru (o - 2) of i) as string\n"+
~"else if o = 1 then\n"+
~"set i to \"\"\n"+
~"end if\n"+
~"end if\n"+
~"if i is not \"\" then\n"+
; \ in AppleScript strings: "\\"
~"set i to replace_chars(i, \"\\\\\", \"\\\\\\\\\") -- backslash to double backslash\n"+
~"set i to replace_chars(i, \"\\\"\", \"\\\\\\\"\") -- double-quote to backslash double-quote\n"+
~"set pbt to pbt & \"\\\\n\" & i\n"+
~"end if\n"+
~"end repeat\n"+
~"set pbt to \"Define MyAScr.s = ~\\\"osascript\" & pbt & \"\\\"\\r\\r\"\n"+
~"if (count pbt) > 80 then\n"+
~"try\n"+
~"display dialog \"CUT?\" buttons {\"NO\", \"Cut\"} cancel button 1 default button 2\n"+
~"copy replace_chars(pbt, \"\\\\n\", dacut) to pbt\n"+
~"end try\n"+
~"end if\n"+
~"set dla to display dialog \"Set the Clipboard to this?\\r\\rYou can edit here…\" default answer pbt buttons {\"Cancel\", \"Copy\"} cancel button 1 default button 2\n"+
~"set the clipboard to text returned of dla -- set the clipboard to pbt -- no editing\n"+
~"on replace_chars(this_text, search_string, replacement_string)\n"+
~"local asd\n"+
~"set asd to AppleScript's text item delimiters\n"+
~"set AppleScript's text item delimiters to the search_string\n"+
~"set the item_list to every text item of this_text\n"+
~"set AppleScript's text item delimiters to the replacement_string\n"+
~"set this_text to the item_list as string\n"+
~"set AppleScript's text item delimiters to asd\n"+
~"return this_text\n"+
~"end replace_chars"
sh(MyAScr)
Re: runprogram Command in Mac OS
Posted: Thu Jan 25, 2024 2:23 pm
by Piero
Now let's see some Ruby examples:
Code: Select all
; I'm kidding, but in case explore TextMate
Edit: Dear wise forum admins, why do I often get duplicates when editing a post? (even when changing only one letter... Safari)
Edit2: There are 2 possibilities:
1) You fixed it (I thought it could be "preview")
2) (more probable) This noob was clicking "quote" instead of "edit"
Edit3: BTW, if it wasn't a Miracle
Edit4: Added the Miracle possibility
Re: runprogram Command in Mac OS
Posted: Wed Apr 10, 2024 12:16 am
by JohnF
Thank you for the extended reply which, if I understand correctly allows for extended two-way communication between PureBasic and the executable that is being called.
My application is quite simple, and luckily I've made progress, where I am able to launch and send a commandline$ argument to the executable. The problem I am now running into is that if the arguments have spaces in them then using either single or double quotes are not recognized. Here's an example. It's all a bit confusing because I'm not having this difficulty in the Windows version.
Commandline$ = " -LensMake=Leica Filename.DNG"
Result = RunProgram("/usr/local/bin/exiftool",Commandline$,"")
Works fine.
When I set Commandline$ = " -LensMake=Leica AG" then all that is written is Leica and the " AG" is lost.
When I try Commandline$ = " -LensMake='Leica AG'" then what gets written is 'Leica meaning the single quote is just treated as text and has no significance.
I get the same result if I programmatically try to construct the Commandline$ like this - using double quotes - Chr(34) or single quotes Char(39)
Commandline$ = " -LensMake=" + Chr(39) + "Leica AG" + Chr(39)
On the other hand, if I type the same command directly into a terminal window
/usr/local/bin/exiftool -LensMake='Leica AG' Filename.DNG works just as it should and correctly passes the full Leica AG.
Would you be able to suggest what syntax to I need to add so that I can pass string information with blank spaces in MacOS?
Thanks a million if you can help with this.
Re: runprogram Command in Mac OS
Posted: Wed Apr 10, 2024 5:53 am
by Piero
JohnF wrote: Wed Apr 10, 2024 12:16 amif you can help with this.
Huh? If you use the shell procedure I posted above, you must get the same results you get in Terminal!
In case, you only have to properly escape \ and " in the PB ~"string"...
Re: runprogram Command in Mac OS
Posted: Wed Apr 10, 2024 5:56 am
by JohnF
Can you help me with-> you only have to properly escape \ and " in the PB ~"string"...
I'm afraid there's much I don't know, so giving me and example would be helpful.
Re: runprogram Command in Mac OS
Posted: Wed Apr 10, 2024 6:28 am
by Piero
Re: runprogram Command in Mac OS
Posted: Wed Apr 10, 2024 7:25 am
by JohnF
Hi Piero,
Thanks for your reply, but I'm afraid I don't quite know what to make of it.
Can you apply your fix to:
Commandline$ = " -LensMake=Leica AG" ?
Many thanks!
John
Re: runprogram Command in Mac OS
Posted: Wed Apr 10, 2024 7:37 am
by Piero
From my 1st post above:
Code: Select all
Shell("/usr/local/bin/exiftool -LensMake=Leica AG")
if it works on Terminal, otherwise try:
Code: Select all
Shell("/usr/local/bin/exiftool -LensMake='Leica AG'")
or
Code: Select all
Shell(~"/usr/local/bin/exiftool -LensMake=\"Leica AG\"")
Re: runprogram Command in Mac OS
Posted: Wed Apr 10, 2024 12:49 pm
by Piero
JohnF wrote: Wed Apr 10, 2024 7:25 amMany thanks!
John
BTW, you are very,
VERY welcome: I entered this forum for the same reason, screaming with indignation that runprogram wasn't working

Re: runprogram Command in Mac OS
Posted: Wed Apr 10, 2024 5:34 pm
by JohnF
Hello Piero,
I'm very pleased to know that others have gone completely mad trying to get RunProgram to function as desired. I've been completely successful when compiling and operating under WINDOWS. My trouble seems to be wholly centered around how strings are treated and passed in the MacOS environment.
I tried your suggestions below. Without success. One caveat, I am not using the full implementation that have provide, - just trying to executed the desired function directly in a single call. As mentioned, this had been completely successful for me in WINDOWS.
Here's what works in in MacOS when typing directly into a Terminal window. - I'm now giving the full command string
/usr/local/bin/exiftool -LensMake=Voigtlander -LensModel='Color Skopar' -preserve -overwrite_original_in_place -X -m /Users/Elizabeth/Desktop/John/Temp
In the Terminal window, the single (or double quotes) are recognized just as they should.
The problem is I can find no way to pass the values for parameters (ie -LensMake= or -LensModel=) that contain spaces. When I execute the following
Result = RunProgram("/usr/local/bin/exiftool",~" -LensMake=Voigtlander -LensModel=\"Color Skopar\" -preserve -overwrite_original_in_place -X -m /Users/Elizabeth/Desktop/John/Temp","",#PB_Program_Hide|#PB_Program_Wait)
the assignment to LensModel is "Color. It takes the double quote (or single quote if I try that) as part of the value. I have tried building d Commandline$ string and passing it. I get the same results. With the Commandline$ approach I've also tried using #DQUOTE$ and Chr(34) and Chr(39) around the arguments as well. In every case the quote that I'm inserting (using any mechanism) is simply treated as the first character of the value I'm assigning.
I've yet to find any syntax that allows me to pass the a value for -LensModel where the space is not parsed in the overall command line.
As mentioned, this is all working under WINDOWS. Why is this so difficult on MacOS?
Thanks for your willingness and expertise.
Best,
John
Re: runprogram Command in Mac OS
Posted: Wed Apr 10, 2024 6:12 pm
by Piero
It seems you don't need to get the output, so you can use a simplified shell procedure if you want:
Code: Select all
Procedure simpleShell(ShellCommand$)
Protected shell
shell = RunProgram("/bin/zsh","","", #PB_Program_Open|#PB_Program_Write)
If shell
WriteProgramStringN(shell,ShellCommand$)
WriteProgramData(shell,#PB_Program_Eof,0)
While ProgramRunning(shell) : Delay(10) : Wend ; Wait until program quits
CloseProgram(shell)
EndIf
EndProcedure
simpleShell("/usr/local/bin/exiftool -LensMake=Voigtlander -LensModel='Color Skopar' -preserve -overwrite_original_in_place -X -m /Users/Elizabeth/Desktop/John/Temp")
Edit/PS: if your mac os is very old, use /bin/sh instead of /bin/zsh
JohnF wrote: Wed Apr 10, 2024 5:34 pm|#PB_Program_Wait)
Edit 2: Added "Wait until program quits"