Applescript do shell to create file with text and manipulate - shell

I am trying to write a simple logger with Applescript. I have a couple of questions
Firstly I am testing if a file exists, if it does not I want to create a file with that name and set the first line to a string "Counter:0".
So far I have this but my syntax is wrong. Appreciate any help as the info on the web is a bit slender.
tell application "Finder"
set thePath to "/Data/GameLog/"
set theFile to thePath & (do shell script "date '+%Y.%m.%d'" & ".log")
if exists POSIX file thePath then
--display dialog "Found"
else
do shell script "Counter:0" > echo thePath
end if
end tell
secondly I would like to read the first line of the file (ie Counter:0) and increment the integer after the : by 1.
Help v.much appreciated

Based on vadian's answer:
set logFolder to (POSIX path of (path to home folder)) & "Desktop/" -- the trailing slash is crucial
set timeStamp to do shell script "date '+%Y.%m.%d'"
set logFile to logFolder & timeStamp & ".log"
if ((do shell script "test -d " & quoted form of logFolder & "&& echo true||echo false") as boolean) then
try
close access logFile
end try
try
set fileReference to open for access logFile with write permission
on error
display alert "File already open"
return -1
end try
set counter to (get eof of fileReference)
if counter is not 0 then
try
set counter to (read fileReference from 9) as integer
on error
close access logFile
display alert "Read error"
return -1
end try
set counter to counter + 1
set eof of fileReference to 0
end if
write ("Counter:" & counter) to fileReference as «class utf8»
close access logFile
else
display alert "Folder does not exist"
end if
The advantage is that this will not ignore errors. It will also be able to find your home directory without needing you to manually enter it in code. This is safe since we check for the logFolder first. The rest of the code can't fail if that folder exists. I am aware that AppleScript has a method for checking whether or not a file exists, but I have found that functionality to be fairly difficult to use when dealing with POSIX paths, so I'm using a do shell script instead, which naturally deals with POSIX paths.

Please try this, change the file path in the first line (consider the trailing slash)
set logFolder to "/Users/myUser/Desktop/" -- the trailing slash is crucial
set timeStamp to do shell script "date '+%Y.%m.%d'"
set logFile to logFolder & timeStamp & ".log"
try
set fileReference to open for access logFile with write permission
set counter to (get eof of fileReference)
if counter is not 0 then
set counter to read fileReference from 9
set counter to counter + 1
set eof of fileReference to 0
end if
write ("Counter:" & counter) to fileReference as «class utf8»
close access fileReference
on error
try
close access logFile
end try
end try

Related

Add path to text

I have a repeat which have
repeat with myrecordID in rtetr
set refpdf to field "File Attachments" of record myrecordID
if refpdf is "" then
set noref to noref + 1
else
set refpdf2 to POSIX path of (docs & "/PDF" & refpdf1)
set thePath to refpdf2
set thePath1 to my convertPathToAlias(thePath)
set thePath1 to thePath1 as text
end if
end repeat
variable is path to some files
I want to write this variable to a text file & add each path to a line.
but when I try
set myFile to open for access addtemp with write permission
write namesText to myFile
close access myFile
It only write the last variable, not all of them from repeat.
I also tried this
try
set fileDescriptor to open for access addtemp with write permission
write thePath1 & return to fileDescriptor
close access fileDescriptor
on error e number n
try
close access file addtemp
end try
display dialog "Error: " & e & " - number: " & n buttons {"Cancel"} default button "Cancel"
end try
but no chance
Ok it seems you are working with in a tell block for some application you haven't mentioned — set refpdf to field "File Attachments" of record myrecordID is not standard AppleScript, and won't compile on its own — but using a generic application name, I think you want something that looks like this (please remember to replace "Generic Name" with the name of the app in question):
try
set myFile to open for access addtemp with write permission
on error
close access addtemp
set myFile to open for access addtemp with write permission
end try
tell application "Generic Name"
repeat with myrecordID in rtetr
set refpdf to field "File Attachments" of record myrecordID
if refpdf is "" then
set noref to noref + 1
-- write out a notice that there was no data, with a line break after
write "No Reference" & return to myFile
else
set refpdf2 to POSIX path of (docs & "/PDF" & refpdf1)
set thePath to refpdf2
set thePath1 to my convertPathToAlias(thePath)
set thePath1 to thePath1 as text
-- write out the data, with a line break after
write thePath1 & return to myFile
end if
end repeat
end tell
close access myFile
In short, you open the write-out file at the start of the script, write the data sequentially inside the repeat loop, and then close the file at the end.
The try block at the beginning handles a frequent problem: if your script errors out before the close statement, the file handle is left open and can error if you try to open it again; this try block detects an error, and tries to close and reopen the file handle.
You may need to put the keyword my before the write statements if the app in question defines its own write command.

ApplescriptObjC - Reading contents of plain txt file from Finder and making that a list

I am attempting to read the contents of a txt file (list of IP addresses) and then make a list from them to use in a shell script loop. Seems to error on the "set Switches to paragraphs of (read POSIX file file path)" and I assume I need to reconstruct but I know Applescript and barely getting into ObjC so this one has me stumped....
I really want to change the first three lines to set filepath to choose file but I get the same error.
on theButtonChooseFile_(sender)
set x to (system info)
set theuser to short user name of x
set filepath to "/Users/" & theuser & "/Desktop/switches.txt" as string -- Define path to file
set listOfSwitches to {}
set Switches to paragraphs of (read POSIX file filepath)
repeat with nextLine in Switches
if length of nextLine is greater than 0 then
copy nextLine to the end of listOfSwitches
end if
end repeat
end theButtonChooseFile_
Error:
2016-08-25 08:13:37.655 Cisco Configurator[67884:4593159] *** -[AppDelegate theButtonChooseFile:]: Can’t make current application into type file. (error -1700)
The AppleScript read command accepts also POSIX paths and you can get the path to the desktop folder of the current user simply with
path to desktop
The entire handler can be written this way
on theButtonChooseFile_(sender)
set desktopFolder to POSIX path of (path to desktop)
set filepath to desktopFolder & "switches.txt"
set Switches to paragraphs of (read filepath)
set listOfSwitches to {}
repeat with nextLine in Switches
if length of nextLine is greater than 0 then
copy nextLine to the end of listOfSwitches
end if
end repeat
end theButtonChooseFile_
But consider than listOfSwitches is only accessible in the handler.

Play sound with AppleScript using afplay and relative file path

I'm trying to create an AppleScript that uses afplay (as suggested here) to play a random sound file that's located in a directory within the same directory as the script.
folder
-- applescript.scpt
-- sounds
----- sound-x.aiff
I found this comment regarding relative paths to be potentially useful:
(POSIX path of (path to me))
However, I keep receiving errors when I try mashing it up with this approach for randomness...
set theNumber to 3
set theFiles to {}
repeat
set file_path to quoted form of (POSIX path of (path to me))
tell application "Finder" to set aFile to (some file of file_path & "/sounds_dir") as text
if aFile is not in theFiles then
set end of theFiles to aFile
tell application "Finder" to open file aFile
do shell script ("afplay " & file_path & " > /dev/null 2>&1 &")
end if
if (count of theFiles) is equal to theNumber then exit repeat
end repeat
In a script, path to me returns the path to the script file but we need its parent path to add a sub path.
To compose the correct path, we can use the subroutine composeFilePath(fileName).
The main action is in the repeat loop. I also added a delay so it's easier to test. Save the script before using since path to me will return a wrong path when its unsaved.
set LOOPS to 3
set soundFolderName to "sounds_dir"
# ---------------------
# SETUP
# ---------------------
set soundFolderFullPath to my composeFilePath(soundFolderName)
tell application "Finder"
if folder soundFolderFullPath exists then
set soundFolder to (folder soundFolderFullPath)
else
set soundFolder to ""
# Customize action when folder does not exist
beep
log "*** Error: folder " & quoted form of soundFolderFullPath & " missing!"
return
end if
if (count files in soundFolder) is 0 then
# Customize action when folder has no items in it
beep
log "*** Error: No items in " & quoted form of soundFolderFullPath & " !"
return
end if
end tell
# -------------------------------------------
# We have "soundFolder" and it has items in it
# -------------------------------------------
repeat LOOPS times
# PLAY RANDOM FILE
# As ONE LINER:
## do shell script "/usr/bin/afplay " & quoted form of (POSIX path of ((some file of soundFolder) as text)) & " > /dev/null 2>&1 &"
# Step By Step
set aRandomFile to some file of soundFolder
set aRandomFile to POSIX path of (aRandomFile as text)
set shellScript to "/usr/bin/afplay " & quoted form of aRandomFile & " > /dev/null 2>&1 &"
do shell script shellScript
delay 1
end repeat
on composeFilePath(fileName)
if fileName is "" then return ""
set pathToMe to path to me -- this is the full path to this script
-- get the folder this script is in:
set thisScriptsFolder to ""
tell application "Finder"
try
set thisScriptsFolder to (get container of pathToMe) as text
end try
end tell
if thisScriptsFolder is "" then
return ""
end if
return thisScriptsFolder & fileName -- full path
end composeFilePath
You have your paths all messed up. Applescript doesn't use "posix paths" so you can't pass that type of path to applications such as the Finder. Shell scripts do use posix paths and you have to quote them.
In addition for applescript, whenever your path is in string format you must put the word "file" or "folder" in front of the path before you use it so that applescript can understand it's an object to be acted upon otherwise applescript just treats it like a string. Also string paths in applescript should be colon delimited (:) rather than slash delimited (/). Finally applescript paths start with the name of your hard drive such as "Macintosh HD".
You can see you must treat paths in applescript much differently than you do with the shell. Anyway, I didn't test this code but using those basic rules this should work. Good luck.
set theNumber to 3
set theFiles to {}
repeat
set file_path to (path to me) as text
set file_dir to file_path & ":sounds_dir:"
tell application "Finder" to set aFile to (some file of folder file_dir) as text
if aFile is not in theFiles then
set end of theFiles to aFile
tell application "Finder" to open file aFile
do shell script "afplay " & quoted form of POSIX path of aFile & " > /dev/null 2>&1 &"
end if
if (count of theFiles) is equal to theNumber then exit repeat
end repeat

OSX: How can check whether a file exists in current directory using applescript?

I want to make an automator app which creates an empty file in current directory.
I did some google search and found:
http://hints.macworld.com/article.php?story=20050219134457298 and http://hints.macworld.com/article.php?story=20100509134904820
However, I want to do something more powerful.
If the specified file already exists, I want to show a warning instead of overwriting the original file, which is what one of the above link does. (The other one creates a text file using textEdit. I do not want to create text file. I want an empty file like what linux/unix does)
I already figured out how to do most of the part, but
How can check whether a file exists in current directory using applescript??
How can I concatenate two variable in applescript?
Checking if a file exists (assuming thefullpath is already set as in the referenced question):
tell application "Finder"
if exists POSIX file thefullpath then
--do something here like
display alert "Warning: the file already exists"
end if
end tell
Not sure what you mean by the second part but if you want to concatenate strings stored in var1 and var2 you could simply do
var1 & var2
Something I have been using a lot of late for this sort of thing is the command /bin/test
The test test for the existence of in this case a file
if (do shell script "/bin/test -e " & quoted form of (POSIX path of theFile) & " ; echo $?") is "1" then
-- 1 is false
--do something
end if
The -e option:
-e file True if file exists (regardless of type).
The are tons of other test options shown in the /bin/test man page
The following code, adapted from your second link, is usually right, but it doesn't always work. The current directory is better specified as the directory of the document that is being opened which is most likely from the Finder's front window, but not necessarily. I like to write code that will work no matter what.
on run {input, parameters}
tell application "Finder"
set currentPath to insertion location as text
set x to POSIX path of currentPath
display dialog "currentPath: " & (x as text)
end tell
return x
end run
I wrote a whole "Run AppleScript" action to put things into context:
on run {input, parameters}
# count the number of files
set numFiles to 0
repeat with f in input
# warn the user that folders are not processed in this app
tell application "Finder"
if (kind of f is "Folder") then
display dialog "The item: " & (f as text) & " is a folder. Only files are allowed. Do you want to continue processing files or do you want to cancel?"
else
set numFiles to numFiles + 1
end if
end tell
end repeat
# require that at least one file is being opened
if numFiles < 1 then
display alert "Error: the application Test1.app cannot be run because it requires at least one file as input"
error number -128
end if
# get the current directory from the first file
set theFirstFile to (item 1 of input)
tell application "System Events" to set theFolder to (container of theFirstFile)
# ask the user for a file name
set thefilename to text returned of (display dialog "Create file named:" default answer "filename")
# create the file
tell application "System Events" to set thefullpath to (POSIX path of theFolder) & "/" & thefilename
set theCommand to "touch \"" & thefullpath & "\""
do shell script theCommand
# return the input as the output
return input
end run
The "touch" command is OK. If the file doesn't exist, it is created and if it does exist, only the modification date is changed (which isn't too bad) but it doesn't overwrite the file. If your file is being overwritten, it's not the touch command that is doing it.
I changed the default file name to remove the extension ".txt" This extension may default to being opened by TextEdit.app, but you can change this in the Finder by choosing "Get Info" for a file and changing the "Open With" property. You can change which application opens the file with that extension or you can change them all. For example, all of my ".txt" files are opened with BBEdit.app
Will you vote my answer up?
Another option that doesn't require Finder or System Events is to try to coerce a POSIX file or file object to an alias:
try
POSIX file "/tmp/test" as alias
true
on error
false
end try

Applescript copy files to mounted volume; test for file(s) already exists

I am writing a script to allow students to upload their files to a shared folder on teacher's computer in a computer lab (same network). I have a working script that, when executed, will duplicate all files in the folder UPLOAD on the student machine to the folder SUBMISSIONS on the teacher's machine. However, if a file already exists on the teacher machine, the script hangs.
I need to be able to test for the presence of individual files on the teacher machine and either (a) pop a message that says "this file already exists, rename it and upload again" or (b) append something to the file name to differentiate it...a random number or "copy 1" etc.
Ideally I want it to run as a folder action. When a file is added to the "UPLOAD" folder it will automatically be sent to the teacher. But I don't want files to copy over files of the same name...or for the script to hang.
Any thoughts or alterative approaches would be welcome.
Here's my code:
set home_path to path to home folder as string
set work_folder to alias (home_path & "Desktop:" & "Upload")
try
mount volume "afp://[LOGIN INFO].local/Submissions"
set this_folder to result as alias
tell application "Finder"
tell application "Finder"
duplicate every file of work_folder to this_folder
end tell
eject this_folder
end tell
end try
I think that it would help if your try-block had an on-error block to inform you about any errors.
try
# try stuff here
j # this will compile but will throw a runtime error and you can see the error
on error error_message number error_number
display alert "Error: " & error_message & return & return & (error_number as text)
end try
OK. I tried again to write some code. Here is my version.
It does a couple of things differently than the code posted by the original poster.
It copies the files and folders into a new folder on the server with a time-stamp to cope with the problems of whether some of the files already exist on the server.
I changed the wording of the duplicate statement from duplicating every "file" to duplicating every "item" so that folders are duplicated too.
I put in an on-error block in the try-statement to display any errors.
I activate Finder so that you can see the progress window.
I pop up a dialog at the end if there were no errors.
I had a problem that I had to fix:
On the server, I had to enable write permissions for the client or I got a -5000 error.
I think that the following code should work pretty well. It's almost 100% AppleScript. It only uses one call to the shell and that is to get the current date and format it for the time-stamp for new folders created on the server.
# set the path to the "work_folder" where the files are to be uploaded
set home_path to path to home folder as string
set work_folder to alias (home_path & "Desktop:" & "Upload")
# duplicate the files and folders in work_folder to the server
try
# TODO set the name of your server here
set the_volume to mount volume "afp://32-bit.local/Submissions"
set destination_path to the_volume as text
set folder_name to getTimeStamp()
tell application "Finder"
activate
set new_folder to make new folder at alias destination_path with properties {name:folder_name}
duplicate every item of work_folder to new_folder
eject the_volume
display alert "Successfully uploaded the files and folders"
end tell
on error error_message number error_number
if error_number is not equal to -128 then
display alert "Error: " & error_message & return & return & (error_number as text)
end if
end try
# This function returns the current date and time as a time-stamp of the form yyyy-mm-dd-hh-ss
# This function uses a shell script because it's just a lot easier to do than in AppleScript
on getTimeStamp()
set time_stamp to do shell script "date '+%Y-%m-%d-%H-%M-%S'"
return time_stamp
end getTimeStamp
Here's another idea for debugging. You can put in calls to "display dialog" to be able to know where your script is failing:
display dialog "Entering script"
set home_path to path to home folder as string
display dialog "home_path: " & home_path
set work_folder to alias (home_path & "Desktop:" & "Upload")
display dialog "work_folder: " & work_folder as string
try
mount volume "afp://32-bit.local/Submissions"
set this_folder to result as alias
display dialog "this_folder: " & this_folder as string
tell application "Finder"
display dialog "Inside of the first tell application Finder"
tell application "Finder"
display dialog "About to call duplicate"
duplicate every file of work_folder to this_folder
display dialog "Just returned from calling duplicate"
end tell
display dialog "About to call eject"
eject this_folder
display dialog "Just returned from call to eject"
end tell
on error error_message number error_number
display alert "Error:" & error_message & return & return & (error_number as text)
end try
display dialog "Exiting script"
Another debugging technique is to log output to a text file.
Another debugging technique is to purchase the AppleScript debugger:
http://www.latenightsw.com/sd4/index.html
I believe that this debugger costs $200.00 that's too pricey for me, but I've used other debuggers and debuggers are wonderful tools that let you "peek inside" of your script while it's running to see the value of variables and to trace which lines of code are being executed.
This script will copy each file over to the mounted volume. If a file exists with the same name at the destination it will add a number to the end of the copy file name and try that.
Example:if test.doc exists in the folder already then the script will try and copy it with the name test_1.doc and so on..
The original file is never renamed and the older files are never overwritten.
The script is fully commented to explain what it is doing.
** Update2 **
The copy to destination code is now in it's own handler.
The original files are labeled by the finder label index 6 (green) to indicate successful copied.
This will also stop the same original file from being copied twice by using the index colour as a check. If it is labeled index label 7 it will be ignored.
You could if you want move the successful copied files to another folder out of the way using the script. But I have not done this in this script.
set home_path to path to home folder as string
set work_folder to alias (home_path & "Desktop:" & "Upload")
set counter to ""
global counter
--try
set this_folder to mount volume "afp://myMac/UserName/"
this_folder as text
tell application "Finder" to set theFiles to every file of work_folder as alias list #GET ALL FILES OF LOCAL FOLDER AS ALIASES
tell application "Finder" to set theRemoteFiles to (every file of ((this_folder & "Submissions:" as string) as alias)) #GET ALL FILES OF REMOTE FOLDER -- ONLY NEEDED FOR COUNT CHECK LATER
repeat with i from 1 to number of items in theFiles #PROCESS EACH LOCAL FILE
set this_item to item i of theFiles #GET A LOCAL FILE
tell application "Finder" to set LabelIndex to label index of this_item
if LabelIndex is not 6 then
tell application "Finder" to set this_name to displayed name of this_item #GET ITS DISPLAYED NAME
tell application "Finder" to set this_extension to name extension of this_item #GET ITS EXTENSION NAME i.E "txt"
set realName to (do shell script "echo " & quoted form of this_name & "| awk -F '" & quoted form of this_extension & "' '{print $1}'") #GET ITS NAME WITHOUT EXTENSION NAME
set counter to 1 # SET A NUMBER TO ADD TO THE FILE NAME IF THE FILE NAME EXIST ALREADY IN THE REMOTE FOLDER
my checkName(this_name, realName, this_item, this_extension, theRemoteFiles, this_folder) # CALL TO HANDLER THAT WILL DO THE CHECKING AND COPYING
end if
end repeat
tell application "Finder" to eject this_folder
# THE CALL TO THE HANDLER INCLUDES VARIABLES THAT ARE NOT GLOBAL OR PROPERTIES BUT NEED TO BE PASSED ON TO IT I.E(this_name, realName, this_item, this_extension, theRemoteFiles, this_folder)
on checkName(this_name, realName, this_item, this_extension, theRemoteFiles, this_folder)
# (1) IF THE NUMBER OF theRemoteFiles IS GREATER THAN 0 THEN FILES EXIST IN THE REMOTE FOLDER AND MAY CONTAIN FILES WITH THE SAME NAMES AS THE LOCAL ONES. PROCESS..
# (2) IF THE NUMBER OF theRemoteFiles IS NOT GREATER THAN 0.THEN FILES DO NOT EXIST IN THE REMOTE FOLDER AND THE LOCAL ONES CAN JUST BE COPIED OVER.
if (count of theRemoteFiles) is greater than 0 then # (1)
try
my copyOver(this_item, this_folder, this_name)
on error errMssg #WE USE not overwritten ERROR TO TRIGGER THE RENAME THE DESTINATION FILE NAME TO INCLUDE A NEW NUMBER.
--tell application "Finder" to set label index of this_item to 6
if errMssg contains "not overwritten" then
set this_name to (realName & counter & "." & this_extension)
set counter to counter + 1 #WE SETUP THE FILE NAME NUMBER FOR THE POSSIBLE NEXT RUN
# RUN THE HANDLER AGAIN WITH THE CHANED DETAILS
my checkName(this_name, realName, this_item, this_extension, theRemoteFiles, this_folder)
end if
end try
else # (2)
my copyOver(this_item, this_folder, this_name)
end if
end checkName
on copyOver(this_item, this_folder, this_name)
# THE -n OPTION IN THE SHELL COMMAND TELLS CP NOT TO OVERWRITE EXISTING FILES. AN ERROR OCCURE IF THE FILE EXISTS.
# THE -p OPTION IN THE SHELL COMMAND TELLS CP TO PRESERVE THE FOLLOWING ATTRIBUTES OF EACH SOURCE FILE IN THE COPY:
# modification time, access time, file flags, file mode,
#user ID, and group ID, as allowed by permissions. Access Control
#Lists (ACLs) and Extended Attributes (EAs), including resource
#forks, will also be preserved.
# THE -v OPTION IN THE SHELL COMMAND TELLS CP TO USE VERBOS MODE. WHICH GIVES US A BETTER CLUE OF THE ERROR
set theResult to (do shell script "cp -npv " & quoted form of (POSIX path of this_item) & space & quoted form of (POSIX path of (this_folder & "Submissions:" as string) & this_name))
if theResult contains "->" then
tell application "Finder" to set label index of this_item to 6
else
tell application "Finder" to set label index of this_item to 7
end if
end copyOver

Resources