Execute a shell command on a file selected in the Finder - shell

I'm a very novice and infrequent applescript experimenter. I've tried for several hours now to learn the individual applescript commands for the following task, but I always run into errors. Perhaps someone much more adept at applescript will find this task easy and quick, and for that I would be very grateful. Here is the task:
I want to be able to manually select a document or file within the finder and then execute the following unix command on that file. I would then store the script under Finder's "Services" menu. The unix command is:
srm -rfv -m path/filename
In my attempts, I assumed that a script that would open Terminal and execute the command would be the way to go, but just couldn't get anything to work. Thank you in advance to any good programmers who can whip out such a script for me.

My tip: Create such services using Automator!
Create a new Service in Automator
Choose "File & Folder" as Input and "Finder"
Add "Run shell script"
Choose "as arguments" as input
Change echo "$f" to your command srm -rfv -m "$f"
Save it as "Safe delete"
From now on, if you select a file inside Finder you will find the option "Safe delete" in the context menu.
Enjoy, Michael / Hamburg

Craig's comment is pertinent, but I am just focus on the script itself, not on the shell command. the script bellow must be saved as Application and each time you drop 1 or more file on its icon, the shell script command will be executed for each file :
on open myFiles
repeat with aFile in myFiles -- repeat loop in case you drop more than 1 file on the icon script
try
do shell script "srm -rfv -m " & (quoted form of POSIX path of aFile)
end try
end repeat
end open
Still make sure that in your shell command 'srm -rfv', the 'v' is necessary because this script will not display any thing ! I don't think so. also I did not display error during remove. what do you want to do with error (like write protect, ...) ?

Update: I missed that the OP wants to create an OS X Service that integrates with Finder. ShooTerKo's answer shows how to do that (and his solution doesn't even require use of AppleScript).
The only potentially interesting thing about this answer is that it demonstrates AppleScript commands to open a file-selection dialog, get the chosen file's POSIX path and run a shell command with it, with some general background information about executing shell commands with do shell script.
Try the following:
# Let the user choose a file via an interactive dialog.
set fileChosen to choose file
# Get the file's POSIX path.
set filePath to POSIX path of fileChosen
# Use the path to synthesize a shell command and execute it.
do shell script "echo srm -rfv -m " & quoted form of filePath
Note:
There's no explicit error handling; if you don't want the script to just fail, you'll have to add error handling (try ... on error ... end try) to handle the case of the user canceling the file selection and the shell command failing (unlikely in this case).
The shell command has echo prepended to it in order to perform a dry run (see its output in Script Editor's Result pane); remove it to perform the actual deletion.
quoted form of is important for ensuring that a string value is included as-is in a shell command (no risk of expansion (interpretation) by the shell).
do shell script will NOT open a Terminal window - it will simply run the shell command hidden and return its stdout output, which is usually what you want. If the shell command signals failure via a non-zero exit code, an error is raised.

Related

Send multiple lines of quoted text to IPython in Terminal Window

I want to send a selected group of lines to my current ipython window from a texteditor (It's TextMate in this case, but that's largely irrelevant.) The script uses a bash call so it can accept the variable and then an Applescript call to push the code to the window.
This current script works, but it can only send a single non-nested line at a time. Is there a way to fix this so I can send multiple non-nested lines of code at once?
#!/bin/bash
QUOTED_TEXT=${TM_SELECTED_TEXT//\"/\\\"}
echo "$QUOTED_TEXT"
osascript <<- APPLESCRIPT
tell application "Terminal"
set currentTab to (selected tab of (get first window))
set tabProcs to processes of currentTab
set theProc to (end of tabProcs)
if theProc is not "Python" then
set currentTab to (do script "ipython")
end if
do script "$QUOTED_TEXT \n" in currentTab
end tell
APPLESCRIPT
I don't use either TM or ipython myself so can't provide an immediate answer to your exact problem, but here's some general thoughts on calling AppleScript from Terminal:
Never pass arguments to AS like that: it's a mis-quoting accident just waiting to happen. Wrap your AS code in an on run argv ... end run handler, then append your extra arguments to the osascript command when calling it in bash. osascript will then pass those arguments directly to AppleScript as a list of strings assigned to the argv variable. Safe and simple.
Rather than wrap your AS code in a bash script, just add #!/usr/bin/osascript at the top of your AS code, save it as a plain text file in an appropriate location (e.g. somewhere on your shell's $PATH, such as /usr/local/bin), then do chmod +x /path/to/script to make it executable. This will allow you to run it directly from Terminal.
If you want to access STDIN or environment variables directly within an AppleScript-based shell script, use the AppleScript-ObjC bridge to call NSFileHandle's fileHandleWithStandardInput()'s readDataToEndOfFile() and NSProcessInfo's processInfo()'s environment() respectively. To access ARGV, use an explicit run handler as described above.
By default, osascript automatically writes the value returned by the run handler to STDOUT; alternatively, you can write directly to STDOUT at any time via NSFileHandler (you can put a plain return statement at the end of run handler to ensure it returns nothing else). And osascript automatically writes the results of log commands to STDERR, and sets the return code to non-zero when your script throws an uncaught exception (e.g. use an error ERROR_STRING number ERROR_NUMBER statement to raise an exception directly in your AS code).
(BTW, I wrote a File library not long ago that includes a bunch of very nice handlers for writing AS-based shell scripts. I no longer develop or support it myself; however, various folks have already forked it, so if you do much AS+shell work you may find it a helpful source of AS code to cut-and-paste or even to use as-is.)

How to send a command using AppleScript to terminal one by one and save the output, which is not writable to file anywhere?

So, I have a problem. I have downloaded a program from the web. And it's a command line app. I have written a code, which generated some n-k commands to the app. I have written them into an output file. I can write an app in Python, but it freezes on some of the commands. I have tested them manually and seems like there are two issues:
Commands must be run one-by-one;
Some of the commands give an output like bla-bla-bla, this thing is not written into an output file. So, if I run a command ./app -p /file1 -o /file2 -s -a smth- > /fileOutput.txt The fileOutput.txt is empty, though in the terminal, there's is this bla-bla-bla message, stating, that something is wrong. If the command gives bla-bla-bla the app may freeze for a while.
Here is what I want to do:
CD into folder, the containing app;
For command in fileWithCommands perform command and start the next, only when the previous finishes;
If the command gives message, containing bla-bla-bla (cause it may look like file1 bla-bla-bla), write the command and this strange output into file badOutputs.txt.
Have never done applescript before. However, this's what I've done so far:
set theFile to "/Users/MeUser/Desktop/firstCommand"
set fileHandle to open for access theFile
set arrayCommand to paragraphs of (read fileHandle)
#I have found the previous code here: http://alvinalexander.com/mac-os-x/applescript-read-file-into-list-array-examples
close access fileHandle
tell application "Terminal"
activate
do script "cd /Users/MeUser/Desktop/anApp/"
repeat with command in arrayCommand
do script command
end repeat
end tell
Though there's a problem, if in one window the commands make up a huge queue. Without window 1 cd and the command are in different windows. And I am still unable to save the output.
UPDATE
Did with accordance to #Mark Setchell's recommendations. So now I have such code:
set theFile to "/Users/meUser/Desktop/firstCommand"
set fileHandle to open for access theFile
set arrayCommand to paragraphs of (read fileHandle)
close access fileHandle
repeat with command in arrayCommand
do shell script "cd /Users/meUser/Desktop/App/; " & command
end repeat
To the command I have added the following:
2>&1 /Users/meUser/Desktop/errorOut.txt
However, the apple script says that a mistake of the app is the mistake of the script. I.e.: file corrupted, app fails. I want it to write into error file where has it failed and move to the next command, while the script just fails.
Maybe not a complete solution, but more than a comment and easier to format this way...
First Issue
Your command-line app which writes on the Terminal may be writing to stderr rather than stdout. Try redirecting stderr to the same place as stdout by using
./app -p ... > /FileOutput.txt 2>&1
Second Issue
You cannot do:
do shell script cd somewhere
do shell script do_something
because each do shell script will execute in a separate, unrelated process. So your first process will start - in the default directory like all processes - and correctly change directory and then exit. Then your second process will start - in the default directory like all processes - and try to run your command. Rather than that, you can do this:
do shell script "cd somewhere; do_something"
which starts a single process which changes directory and then runs your command line program there.
Issue Three
Why do you want to send your commands to Terminal anyway? Does the user need to see something in Terminal - seems unlikely because you want to capture the output, don't you? Can't you just run your commands using do shell script?
Issue Four
If you want to keep your normal output separate from your error output, you can do:
./app ... params ... > OutputFile.txt 2> errors.txt
Suggestion 1
You can retain all the errors from all the scripts and accumulate them in a single file like this:
./app .. params .. >> results.txt 2>&1
That may enable you to deal with errors separately later.
Suggestion 2
You can capture the output of your shell script into an Applescript variable, say ScriptOutput, like this, then you can parse it:
set ScriptOutput to do shell script "..."
Suggestion 3
If errors caused by your script are stopping your loop, you can enclose them in a try block like this so they are handled and everything continues:
try
do shell script "..."
on error errMsg
display dialog "ERROR: " & errMsg
end try

Wait for PID to exit using AppleScript

I'm new to AppleScript but I have to recreate a batch file which I have written for Windows in OSX and AppleScript seems the best way to do this. Basically, the script will created by another program dynamically and then executed. The AppleScript simply needs to wait for a process, which I want to identify by its process ID, and display a message if the process is still running after a specific amount of time.
Is this possible, and if so, how?
Thanks in advance
I eventually found a solution to my given problem after searching online a bit more. Here is the AppleScript code I use to check if a process with a given id pid is running
tell application "System Events"
set runningApplications to (unix id of every process)
if (runningApplications contains (pid as integer)) is false then
-- process is not running
else
-- process still running
end if
end tell
This is just a snippet obviously. Personally I have the above the statement in a repeat loop, but this offers a solution to checking a process id (which is the unix id).
Hope this helps others
Here are some things to (hopefully) get you on your way:
shell scripts in terminal, and 'do shell script' command: don't know how well you know Unix, but you definitely want to go there, and
learn basics of bash. with some limitations, you can run shell
scripts via AS through the 'do shell script' command.
writing the script dynamically: osascript and osacompile will probably come in handy. see the man pages. osascript can execute
scripts or script text, and osacompile can (!) compile text into
script form (non-text form), among other things.
script waiting for/watching process: more shell script stuff, or using the Finder (what used to be called the Scriptable Finder!),
that is, the Finder's scripting capabilities (dictionary), like
tell application "Finder" to get name of processes. The shell version (which can be called via the 'do shell script' AS command)
might be "ps ax | grep Safari | grep -v grep | awk '{print $1}'" (taken from
stackoverflow post. I like it because it returns empty string if no
match). Depending on how your main script will run, learn how the
idle handler works in a script application, and how that differs from
using an xcode-built app (if that's the route you go), or just a
script.
displaying a message: 'display dialog' is the super simple method, complete with timeout ("gives up" after n seconds). (Sorry if this is
so basic I just insulted your intelligence :-) )
other: Check out (unless you're already wedded to a script editing environment) Smile. It's my primary script editor.

How do I include a command line utility in my applescript?

I wrote an applescript that makes use of an external command line script that helps to perform clicks.
Now, if I want to publish my script, what should be my approach to the end users? I mean, should I say to them: "first of all you have to download this CLI utiltity, and put in this folder... then download and run my script!"
This is a newbie question but: is this the only way to do this? or can I include in some way the CLI code in my script/package? If yes how?
If the script is short enough, you can include it in the AppleScript directly:
do shell script "if [ -f /tmp/foo ]; then rm /tmp/foo; fi"
If it's long and/or complex enough that that's unwieldy, you can embed it in the application bundle (I think in the Contents/MacOS subfolder would be the best place), and then execute it from AppleScript like this:
set objectFolder to (path to me) as string
do shell script ((quoted form of POSIX path of (objectFolder)) ¬
& "Contents/MacOS/scriptname")

View output or progress of shell script when run in AppleScript

I'm working on a simple AppleScript that runs the following...
do shell script "diskutil partitionDisk /dev/disk1 1 APM JHFS+ Test 1G"
It's part of a bigger script. The above works fine but is there a way of viewing the progress? Anything will do really, preferably the actual command within a terminal window would be good.
Thanks
I often have the need for seeing the progress of do shell script commands and other various stages of an AppleScript script while it is running. What I do for do shell script is log the output to a log file and then use a program called MKConsole to display it on the Desktop. For example:
do shell script "diskutil verifyPermissions // &> /output.log"
In the above command diskutil will start up and start running and log standard output to the log file I created called output.log located at /. It won't show you the 0%...10%...20% progress indicator that you see in the Terminal but it does show you all the other output.
Then I configure MKConsole's preferences to read this log file and then in real time it will display all new log messages from that file on my Desktop.
If you need progress of non-shell script commands and need to log AppleScript activity then you can use the logger command. For example:
do shell script "logger -f /output.log The current count is: " & some_variable
If you have an AppleScript which runs for a long time and uses repeat loops and you'd like to know what stage of completion the script is in while running then
the above logger command is a great method for getting some feedback on the progress. I usually throw in a logger command right at the bottom of the script above the end repeat so that it is the last thing it does in that iteration of the loop.
The logger command method works well with MKConsole as well so that you can see the output in real time on your Desktop.
Invoke a terminal and have the terminal run diskutil:
"xterm -e 'diskutil partitionDisk /dev/disk1 1 APM JHFS+ Test 1G'"
is the commandline to pass to the shell, and have xterm(1) display the diskutil output. Other terminals(than xterm) can be used, and the window size font, and colors can be controlled: see the manual page for the terminal.
Maybe another way to show progress would be to run the entire script(not just the diskutil part) in a window.

Resources