UNIX executables

Out of pure frustration with not being able to have a key to conveniently make script files into executables, I built this crazy AppleScript. It will ask a user to select some file and then make a new copy of that file in executable format. I think it goes a little beyond just the chmod +x because the new executable will have the nice looking UNIX executable icon attached to it.

If the script doesn’t output an executable file right away, try opening that file with TextEdit, saving it, and then opening an information window from Finder (+i). It works most of the time but, since it’s horribly hackish, it does miss converting a file every so often.

The script should be added as an automator service and put on a hotkey to make it easily accessible! --this is kind of the whole reason for the script

set theExoticCharacters to {" ", "'", "\"", ";", ":", "*"} --expand this list if needed
set theNormalCharacters to {"\\ ", "\\'", "\\\"", "\\;", "\\:", "\\*"} --corrects characters for Terminal

tell application "Finder"
	set the_file to choose file
	set file_name to (name of the_file)
	set file_name to my remove_extension(file_name)
	set parent_fol to folder of the_file
	set new_file to make new file at parent_fol with properties {name:file_name, kind:"Unix Executable File"}
end tell

set the_text to read the_file
set new_file to new_file as text

set the open_newFile to open for access new_file with write permission
write the_text to open_newFile
close access the open_newFile
set new_fileName to my renameFile(new_file)
set newest_fileName to my POXIFY_location(new_fileName)

do shell script "chmod u+x " & newest_fileName & ""

tell application "Finder"
	open file new_file
	tell application "System Events"
		keystroke "s" using {command down}
		keystroke "w" using {command down}
	end tell
	select file new_file
	tell application "System Events"
		keystroke "i" using {command down}
		keystroke "w" using {command down}
		tell process "Finder"
			set frontmost to true
			keystroke "w" using {command down}
		end tell
	end tell
end tell



------------------------------
on remove_extension(this_name)
	if this_name contains "." then
		set this_name to ¬
			(the reverse of every character of this_name) as string
		set x to the offset of "." in this_name
		set this_name to (text (x + 1) thru -1 of this_name)
		set this_name to (the reverse of every character of this_name) as string
	end if
	return this_name
end remove_extension

on POXIFY_location(this_name)
	if this_name contains ":" then
		set this_name to (every character of this_name) as string
		set x to the offset of ":" in this_name
		set this_name to (text (x + 1) thru -1 of this_name)
		set this_name to (every character of this_name) as string
	end if
	set oldTID to AppleScript's text item delimiters
	repeat with i from 1 to count of ":"
		set AppleScript's text item delimiters to item i of ":"
		set theTextBits to text items of this_name
		set AppleScript's text item delimiters to item i of "/"
		set this_name to theTextBits as text
	end repeat
	set AppleScript's text item delimiters to oldTID
	return this_name
end POXIFY_location

on renameFile(theName)
	global theExoticCharacters, theNormalCharacters
	set oldTID to AppleScript's text item delimiters
	repeat with i from 1 to count of theExoticCharacters
		set AppleScript's text item delimiters to item i of theExoticCharacters
		set theTextBits to text items of theName
		set AppleScript's text item delimiters to item i of theNormalCharacters
		set theName to theTextBits as text
	end repeat
	set AppleScript's text item delimiters to oldTID
	return theName
end renameFile

Hello.

This is a different take, for other purposes. Not as versatile as yours.

This works from within BBEdit (but just change it to “TextWrangler”), and I go to a long extent of not having to enter a localized file kind, god knows what the kind is in Portuguese for instance! :wink:

I learned the trick for doing this by reading some articles about file creator codes at Daringfireball.net.
This Daring Fireball: Scripting File and Creator Types and Daring Fireball: Setting Empty File and Creator Types.



-- http://macscripter.net/edit.php?id=154742
-- © McUsr 2012 you may not post this elsewhere, nor put it in a repository.
tell application "BBEdit"
	activate -- maybe invoked from quicksilver
	set the_file to file of window 1
	
	set dialog_result to display dialog ¬
		¬
			"Make Unix Executable:" buttons {"Cancel", "Chmod"} default button 2 ¬
		with icon note
	
	if button returned of dialog_result = "Chmod" then
		tell text document 1 of text window 1 to close
		set px to quoted form of POSIX path of the_file as text
	else
		error number -128 -- user canceled
	end if
end tell
do shell script "chmod u+x " & px
(*
Apparently file creator codes are still good for something at least in SL
as they obstruct the type identifier setting.
The string of four zeros are necessary as this is what inside an "empty"
File creator and File Type code.
*)
tell application "Finder"
	set file type of file the_file to "0000"
	set creator type of file the_file to "0000"
end tell

tell application "BBEdit"
	activate
	open the_file
end tell

I’m confused as to why you’d need to reverse the string - twice. Can’t you get the offset then copy from S(1) to S(n) where S(n) is 1 character before the offset?

What am I missing?

Example:

display dialog remove_extension("filename.txt")

on remove_extension(this_name)
	if this_name contains "." then
		set x to the offset of "." in this_name
		set this_name to (text (1) thru (x - 1)) of this_name
	end if
	return this_name
end remove_extension

A file names filename.txt.zip ? The file name is filename.txt and the extension is zip. Unlike bash, I mention this because of your name, AppleScript only has a one direction offset :frowning:

EDIT: an example of what I meant:

removeExtension("filename.txt.zip")

on removeExtension(fileName)
	do shell script "a=" & quoted form of fileName & ";echo ${a%.*}"
end removeExtension

EDIT2:
Don’t use POXIFY_location(this_name). It’s wrong to do that yourself, let the command POSIX path handle this:


set macintoshPath to "Macintosh HD:Users:you:Desktop:testfile.txt"
set posixPath to POSIX path of macintoshPath

Maybe someone is interested in this simple script:

(* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* DISCUSSION
 * This script is designed as an addition to TextEdit.app to write shell script.
 * It takes the string value from the fornt most document from TextEdit.app
 * The string is stored in a file named shellRunner, located in the temporary folder
 * The file's executable flag (with chmod) is set 
 * The new script file is executed and it's stdout and stderr are redirected to shellrunner_log
 * shellrunner_log is located in the temporary folder next to the script
 * The log file is also opened in Console.app after it's finished
 *
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * USAGE: A small tutorial
 * 1) Open/launch from your applications folder the application TextEdit.app
 * 2) Write in the (front most) document 'echo "Hello World!"' (text between the single quotes) 
 * 3) Launch this script 
 *
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * Revision history:
 * 0.4: bash scripts containing more than the maximum allowed characters on the command line can be executed now
 * 0.3.1: open the the log file now with console as identifier
 * 0.3: Shows errors in the console as well and don't show up as an error in this script
 * 0.2: Show result in the console
 * 0.1: First working solution:
 *
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * Plans in future releases
 * 1) Make it idiot proof: check if there is document open etc...
 * 2) Create a property/flag where the results should be opened (script editor, console, terminal)
 * 3) Create a dialog wheter you want to run or save the script
 * 4) Check for the user privileges of the openend document
 * 5) In addition to point 4: Ability to run the script as another user
 * 6) preprocessing
 *)
property creator : "DJ Bazzie Wazzie"
property version : 0.4

tell application "TextEdit"
	set theSource to text of document 1
end tell

set execFile to (path to temporary items as text) & "shellRunner"
set execLogFile to (path to temporary items as text) & "shellrunner_log"

--0.4 do shell script "/bin/echo -n " & quoted form of theSource " & > quoted form of posix path of execfile
try
	set descriptor to open for access file execFile with write permission
	set eof of descriptor to 0
	write theSource to descriptor as «class utf8»
	close access descriptor
on error m
	close access file execFile
	return m
end try

do shell script "chmod u+x " & quoted form of POSIX path of execFile
--0.3: " > " has been replaced with " &> " to pass messages to stderr to console as well
do shell script quoted form of POSIX path of execFile & " &> " & quoted form of POSIX path of execLogFile
do shell script "open -a console " & quoted form of POSIX path of execLogFile

Didn’t know about that. That seems like a strange omission of [any] programming language. Makes for more clumsy code than is necessary. The OP’s solution makes more sense, now. I’ve only tinkered with Applescript. Does it have a strings library, or does everyone just roll their own? I’d definitely code a right_offset() and stick it in.

Jon Pugh Has made a smart strings library, Jon Pugh’s Home Page.

To your second part, if if it was that practical to code and use a right_offset() then everyone would have done it.

You’ll end up with something like this, which eats cycles, we usually do string handling by text item delimiters, which considers content more than position. :slight_smile:



set m to "myfilename is long.txt.zip"

set rofs to right_offset for m by "."

set n to text 1 thru (rofs - 1) of m

-->"myfilename is long.txt"

to right_offset for sToSearch by sString
”  McUsr made this handler  and put into Public Domain without copyright!
” http://macscripter.net/edit.php?id=154775
	local l1, l2, sofs, tofs, rofs
	
	set l1 to length of sToSearch
	if l1 = 0 then return 0
	set l2 to length of sString
	if l2 = 0 then return 0
	set sofs to offset of (reverse of characters in sString as text) in (reverse of characters of sToSearch as text)
	if sofs = 0 then return 0
	set tofs to sofs + l2 - 1
	set rofs to l1 - tofs + 1
	return rofs
end right_offset

While I don’t disagree, it helps to understand that AppleScript was never designed as a general-purpose programming language, but rather as a simple user-friendly language to use to drive applications. Of course it’s used for tasks way beyond its initial remit, and you can argue that the original idea was somewhat short-sighted. (And of course you can argue that there’s been plenty of time to remedy things.) But it’s easier to understand what’s there if you also understand the intent.

Here is another idea, with respect to DJ Bazzie Wazzie`s.

This is really work in progress, this one filters the front text document through a shell command in BBEdit, and puts the result in an Output window. I know, you can just undo to bring the previous text back, but sometimes it is ok, to compare before and after, until you have the command right.

I found this at the address inside it, and modified it slightly, I am going to modify it further to work on a selection, when the moment is right.


-- https://groups.google.com/group/bbedit/tree/browse_frm/month/2009-07/688d7289fc320fb3?rnum=21&_done=%2Fgroup%2Fbbedit%2Fbrowse_frm%2Fmonth%2F2009-07%3F

property pPipeCommand : "sort | uniq"

on translate_line_breaks(str)
	set AppleScript's text item delimiters to {ASCII character 13}
	set _lines to every text item of str
	set AppleScript's text item delimiters to {ASCII character 10}
	set str to _lines as text
	set AppleScript's text item delimiters to {}
	return str
end translate_line_breaks

on run
	tell application "BBEdit"
		if not (exists text window 1) then
			display alert "This script requires an open text window."
			return
		end if
		if length of selection of text window 1 is 0 then
			select text 1 of text window 1
		end if
		set _selectedText to selection of text window 1 as Unicode text
	end tell
	
	set _selectedText to translate_line_breaks(_selectedText)
	
	set _tempFilePath to "/tmp/BBEditPipeTemp"
	set _tempFile to open for access (POSIX file _tempFilePath) with write permission
	set eof _tempFile to 0
	set a to write _selectedText to _tempFile as «class utf8» starting at 1
	close access _tempFile
	
	display dialog "Pipe command?" default answer pPipeCommand buttons {"Cancel", "Run"} default button 2
	set pPipeCommand to text returned of result
	
	set _command to "cat " & _tempFilePath & " | " & pPipeCommand
	set _output to do shell script _command
	
	tell application "BBEdit"
		make new text window at front
		set selection of text window 1 to _output
	end tell
end run

Hello.

This is yet another idea, the two scripts are for creating and executing shell scripts with TextEdit, using their own extension “shellscript”, so that it is easy to double click on the script files in the finder and either edit or run them, after you have had a look at them. (I like to see them before I run them, if the script is made for a special task, and isn’t a subroutine.) The output from a shell script will be redirected to a new open TextEdit document.

You’ll need maverick, and The library posted under the title ConsoleIO here in Code Exchange.

The script below executes a shell script that is open in TextEdit.

Edit

You need to have Developer tools installed to use this, since I use the command SetFile.

use AppleScript version "2.3"
use scripting additions
use Output : script "Output"

tell application id "ttxt"
	set docPath to path of its front document
	try
		set probe to docPath
	on error
		display alert "Save AND make executable first..."
		error number -128
	end try
end tell
set shFile to POSIX path of docPath
try
	set theResult to do shell script quoted form of shFile
on error e number n
	display dialog e
end try

if theResult is not "" then Output's initWithText("Output From: " & shFile & linefeed, 600, 800, theResult)
Output's leaveAtFront(0.5)

The script below creates a shell script of the front document in TextEdit and will make it so that it will always open in TextEdit, provided you give the script a “.shellscript” extension.

tell application id "ttxt"
	set docPath to path of its front document
	try
		set probe to docPath
	on error
		display alert "Save first..."
		error number -128
	end try
end tell
set shFile to POSIX path of docPath
set sfxOfs to offset of ".txt" in shFile
try
	if sfxOfs is not 0 then
		set newSFile to text 1 thru (sfxOfs - 1) of shFile
		
		do shell script "mv " & quoted form of shFile & space & quoted form of newSFile & "; chmod u+x " & quoted form of newSFile & "; SetFile -c \"ttxt\" -t \"ttxt\" " & quoted form of newSFile
		set docPath to POSIX file newSFile as alias as text
	else
		do shell script "chmod u+x " & quoted form of shFile & "; SetFile -c \"ttxt\" -t \"ttxt\" " & quoted form of shFile
	end if
on error e number n
	display dialog e
end try


tell application id "sevs" to tell disk item docPath
	set default application of it to "com.apple.textedit"
end tell