How to run an AppleScript automatically once I insert a USB drive? - applescript

I need to rename and fill about 70 USB sticks. Having an Applescript run automatically when they are inserted would make it much simpler.

Any external drive you connect to an OS X machine is mounted into /Volumes. If you watch that folder for changes, you can pick up on added external drives and process these. Running this code:
property ignoredVolumes : {"Macintosh HD", "Time Machine Backups"} -- add as needed
tell application "System Events"
set rootVolume to disk item (POSIX file "/Volumes" as text)
set allVolumes to name of every disk item of rootVolume
repeat with aVolume in allVolumes
if aVolume is not in ignoredVolumes then
set name of disk item (path of rootVolume & aVolume) to newName
end if
end repeat
end tell
will rename drives that are not in your ignoredVolumes list (unplug all but those you want to ignore, run ls /Volumes in Terminal and add the names to the property) to newName. To have this triggered on every change, modify the codes to be a Stay-Open script application:
property pollIntervall : 60 -- in seconds
property ignoredVolumes : {…} -- from above
on run
my checkVolumes()
end run
on idle
my checkVolumes()
return pollInterval
end idle
on checkVolumes()
tell … end tell -- from above
end checkVolumes
and save it in AppleScript Editor (select “AppleScript application”, make sure you tick “Stay Open” when you do). Once launched, the script will keep running, executing the on idle handler every pollInterval seconds.
This will do fine if you are basically doing a once-in-a-while batch job. If you want a more permanent solution that does not rely on running a stay-open script application, you can either
attach a Folder Action Script to the /Volumes folder (hat tip to Philip Regan on Ask Different; details on how to configure the action in this Mac OS X Hints post) – the advantage being you strictly process additions to /Volumes, or
use launchd by creating a LaunchAgent with the StartOnMount key set to true – that will trigger the script / app the agent starts every time a filesystem is mounted (tip of the hat to Daniel Beck at Ask Different; see Apple’s Technical Note TN2083 for the gory details).

Related

how to identify a mounted external volume?

I have the following to identify if a volume is mounted:
on volumeMounted:aNotification
set volumeName to (NSWorkspaceVolumeLocalizedNameKey of aNotification's userInfo) as text
if volumeName is "myVolume" then
--do something
end if
end volumeMounted:
on volumeUnmounted:aNotification
set volumeName to (NSWorkspaceVolumeLocalizedNameKey of aNotification's userInfo) as text
if volumeName is "myVolume" then
--do something
end if
end volumeUnmounted:
I wanted a way to identify if an external volume is mounted even though it has the same name.
For example:
my internal volume is named "Macintosh HD" and I connect an external volume with the same name
it is only recognizing 1 volume, so I can't unmount both volumes with a Button created in the Interface.
Note: I know I could just right click and ask to eject but I wanted to do that in the UI.
There is another key NSWorkspaceVolumeURLKey in the userInfo dictionary of the volumeMounted notification .
The associated value is the URL of the mount point. You can cast the URL to alias. The default folder for mounted volumes is /Volumes, the path of the startup volume is always /.

cocoa-applescript: Determinate progress indicator

I am making an application using Cocoa-Applescript which identifies local IP addresses which are up and running, by pinging x.y.z.[1-255] and appends running IPs to a text file. I already have a GUI for choosing x, y and z and have already made the script to ping each address:
repeat 255 times
try
do shell script "ping -o -t 1 -c 1 " & ipstamp & num4
do shell script "echo " & ipstamp & num4 & " >>/Users/DJ/Desktop/GoodIPs.txt"
end try
set num4 to (num4 + 1)
end repeat
Where ipstamp is x.y.z and num4 is the [1-255]. But now I want a progress indicator to show where it is up to in the process. I know how to get an indeterminate indicator which simply starts and stops:
ProgressBar's startAnimation_(ProgressBar)
## code to ping IP address
ProgressBar's stopAnimation_(ProgressBar)
But that is only indeterminate and I can not find any info on setting determinate ones in Cocoa-Applescript, setting their maximum steps and setting their current step - much like in regular Applescript's:
set progress total steps to x
set progress completed steps to y
Except it is in the GUI, and so i need to use NSProgressIndicators to do it. So summed up, how do you make a determinate progress bar, how do you set its total steps and how do you update its current step?
EDIT
It can be changed to determinate in the menu builder's Attribute Inspector, as can max, min and current steps. However I do need to be able to change the maximum and current steps from within the script, so those still apply.
Well you can make a Determinate progress indercator but the problem is you have to repeat telling it to go to a certain lenght by using the
setDoubleValue_()
message, but anyway here is a simple script to tell it to go to go up to 100
property MyProgressBar : missing value -- Progress Bar IB Outlet
on applicationWillFinishLaunching_(aNotification)
set c to 0
repeat 100 times
set c to c + 1
delay 0.2
tell MyProgressBar to setDoubleValue_(c) -- Tells Progress bar the % to go to
if c > 99 then
exit repeat -- If current repeat is 100 or more then cancels adding more
end if
end repeat
end applicationWillFinishLaunching_
Hope this helped!
just do:
set progress total steps to -1
-- do something
set progress total steps to 0
Remember, that the visualization depends on how the script is executed.

Splitting files into folders

Would someone please get me started with Automator and/or AppleScript. I've gotten more than a little frustrated with it. I'd like to take a very large folder of files (thousands), in a predetermined order (perhaps by name or by date) and move them into subfolders, each of which is no more than a specified size (probably 4.7GB). I don't want ISOs or DMGs or anything just my files nicely split into disk size chunks. No reshuffling of the order either. If a disk only fits a single 10MB file because the next one would blow the limit then so be it. No file will be bigger than the limit either in case your wondering - they will be up to about 50MB tops.
So far I've got a Folder Action with Get Selected Finder Items followed by this AppleScript
on run {input, parameters}
return first item of input
end run
That gets me the first item. I can create a folder e.g. Disk 1 too. And I can move the file also. But how can I work out whether to move to this folder or if it's time for a new folder to be created?
I'd like to do this in Automator if possible, but suspect I'll need a little AppleScript. I'm sure this problem has been solved already, so please just link me if you can. Thanks.
Heres a working AppleScript that will do this. It's imperfect but gets the job done.
It can be very slow with lots of files, I'm sure it can be improved though. High performance is not one of Applescript's strengths.
tell application "Finder"
set files_folder to folder POSIX file "/Users/MonkeyMan/Desktop/MyMessOfFiles"
set destination_folder to folder POSIX file "/Users/MonkeyMan/Desktop/SortedFiles"
set target_size to 250.0 -- size limit your after in MB.
set file_groupings to {} -- list we will store our stuff in
set current_files to {} -- list of the current files we are adding size
set total_files to the count of files in files_folder
set current_size_count to 0
repeat with i from 1 to total_files
set file_size to (the size of file i of files_folder) / 1024.0 / 1024.0 -- convert to MB to help prevent overrunning what the variable can hold
if ((current_size_count + file_size) ≥ target_size) then
-- this will overrun our size limit so store the current_files and reset the counters
set the end of file_groupings to current_files
set current_files to {}
set current_size_count to 0
end if
set current_size_count to current_size_count + file_size
set the end of current_files to (a reference to (file i of files_folder) as alias)
if (i = total_files) then
-- its the last file so add the current files disreagarding current size
copy current_files to the end of file_groupings
end if
end repeat
-- Copy the files into sub folders
set total_groups to the count of file_groupings
repeat with i from 1 to total_groups
set current_group to item i of file_groupings
set dest_folder to my ensureFolderExists(("disk " & i), destination_folder)
move current_group to dest_folder
end repeat
end tell
say "finished"
on ensureFolderExists(fold_name, host_folder)
tell application "Finder"
if not (exists folder fold_name of host_folder) then
return make new folder at host_folder with properties {name:fold_name}
else
return folder fold_name of host_folder
end if
end tell
end ensureFolderExists

Applescript Press and hold ⌘ F2 for 5 seconds?

I'm basically trying to figure this out because I want to use my iMac as an external monitor for my macbook air. I also want to use the iMac keyboard for my macbook air however for some reason, Apple has decided that once you press and hold Command F2 to activate Target Display Mode (meaning it is now an external monitor) that the keyboard paired with the iMac cannot be unpaired with the iMac.
To work around this I thought I would just pair the keyboard with the macbook air initially (leaving the iMac without a keyboard) and create an Applescript macro that would simulate the keyboard pressing and holding the Command F2 for five seconds eliminating the need to go buy another Apple keyboard.
Here's what I have so far and it doesn't work. It's telling me F2 is not right. I'm pretty sure F2's key code is 120.
tell application "System Events"
key down Command
key down F2
delay 5
key up Command
key up F2
end tell
Does anyone know how I might accomplish this?
Observations as of OS X 10.9.1:
There's a problem with the way you're sending F2 (you need to use (key code 120) instead of just 120), but the larger problem is that key up/down only works as expected with modifier keys.
While NON-modifier keys can be sent (using (key code <n>) syntax), the up / down aspect is ignored, making both key down (key code <n>) and key up (key code <n>) statements effectively the same as key code <n> (i.e., a Key Down event immediately followed by a Key Up event is sent).
There's a suggested workaround here, based on repeatedly sending keystrokes in short sequence - it's worth a try, but from a technical perspective it's not the same as keeping a key [combination] held down, so I'm not sure it'll work.
Adapted to your situation (and replacing key down with key code), we get:
tell application "System Events"
set now to the seconds of the (current date)
set later to now + 5
if later > 60 then set later to later - 60
key down command
# Workaround: send F2 repeatedly.
repeat while the seconds of the (current date) is not later
key code 120
end repeat
key up command
end tell
As I said: this may not work; also note that the loop is "tight" meaning that it'll make your machine pretty busy (if sending keys repeatedly, but not necessarily as fast as possible is an option, you could insert a short delay).
Some optional background info:
The key up and key down commands, while also requiring the System Events context, are NOT exposed in the System Events.sdef, the app's dictionary (only key code and keystroke are listed) - this may indicate that Apple doesn't officially support them.
On OS X 10.9.1 (unlike on OS X 10.8 - don't know about earlier versions) there is a bizarre bug where an extra "a" keypress is sent whenever you use key down with a (keycode <n>) specifier.
Ways of determining key-code values (gleaned from various other SO answers, mostly here):
Key Codes, a free GUI app for interactive use - very handy.
The following header file on your system (list of codes in hex format):
/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Events.h
List of decimal codes (incomplete):
I've started a project to do something similar, namely monitor the iMac and automatically trigger target display mode and toggle off bluetooth when a Macbook is connected. You can download it from https://github.com/duanefields/VirtualKVM.

USB_MASS_STORAGE driver crashes after vb script modify USBSTOR key

I have this script written by myself.
Basically what I wanted was script that "unlock" the front usb only for specific device.
The situation is such that we have a workstation with registry key USBSTOR\Start set to 4(disable) so the front usb is not available without some additional work of our IT department - this way we control who can access the usb
But the employees must use a camera to take pictures for specific needs and to send them through email clients.So we want to automate the "lock/unlock" phase.
The usb is unlocked when the device of interest is inserted,it stays "unlocked" while the device is in usb and after the device is plugged out,the script "lock" the usb again.
I have decided to use .vbs.The script works as I expected,but after the "lock" phase,the USB_MASS_STORAGE driver get crashed.I must uninstall it and restart the Windows for the driver to be reloaded again and to work properly.After I have run the script several times,the registry value in USBSTOR\Start does not affect the usb,i.e the usb is unlocked even if there is 4.If I change the value from 4 to 3 the driver crashes.
I am looking for some advices.
Here is the code for usbstor.vbs script. I have used a lot of comments,some of them explain a pretty obvious things,but I have decide so.
' Script for access to Front Usb (a.k.a USB MASS STORAGE)
' The usb is locked by default(the value in Registry Key USBSTOR/Start is 4 - disable).It is enabled(the value in Registry Key USBSTOR/Start is 3 - enable) when the device of interest is put into front usb.
' The usb is in "enable" state ,while the device is into it. After it is removed,the Registry Key USBSTOR/Start value is set to 4(disable).
' The device is recognized by hardware id ,which is known in advance by searching USBSTOR,when the device is inserted. This script is for pc,where what we want is access to front usb only for spcecific device(a camera in our case).
' For everything else the usb should be disabled.The script is loaded in RAM and if the while loop condition isn't change to false,we must kill the process within TaskManager
' The CPU time is high > 98 while the script runs.I came to this solution for my problem,but any ideas for improvements or for different logic are highly welcomed.
Option Explicit On
Dim Shell,Start,Hwid,Enum_0,Enum_1,Count,Flag_0,Flag_1,Check_0,Check_1 'Dimension of varables we are going to use in the script.
Set Shell = CreateObject("WScript.Shell") 'Create an object to work with Windows Registry.
'Start = Shell.RegRead("HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\USBSTOR\Start") 'Save the value of the registry key into Start variable
Hwid = "USB\Vid_0930&Pid_6545\001D92A866B8B8B1934703FE" 'hardawre id of device of interest.We get it from the registry and the script scan for this id.It is constant string
Count = 1 'Initialize the Count variable with value of 1.We use it as a condition in endless while() loop.It makes script run in real-time,so it can scan uninterupted for changes in the registry
QueryEnum0 ' The subroutines QueryEnum0 and QueryEnum1.The id is either in USBSTOR\Enum\0 or in USBSTOR\Enum\1 .That is for sure.
QueryEnum1 ' Declaration before definition - not exactly explanation.
'The purpose of these two subroutines is: create an object everytime the sub is called ,thus read the value in Enum\0 or in Enum\1 constantly as "scanning"
'Probably not so elegant solution to somebody,but actually it works.
Sub QueryEnum0 ' Enter the sub
Dim Flag_Enum_0,Shell ' Declare local variables.They will be created each time the sub is invoked.
Set Shell = CreateObject("WScript.Shell") 'Create an object to work wirh registry any time the sub is called
On Error Resume Next 'Error handling
Flag_Enum_0 = Shell.RegRead("HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\USBSTOR\Enum\0") 'Try to read reg value into Flag_Enum_0. The purpose
On Error GoTo 0
Flag_0 = Flag_Enum_0 'Assign the value to variable Flag_0,outside of sub.The memory for Flag_0 is set once and lasts while the script runs.
End Sub
' Same as QueryEnum0
Sub QueryEnum1
Dim Flag_Enum_1,Shell
Set Shell = CreateObject("WScript.Shell")
On Error Resume Next
Flag_Enum_1 = Shell.RegRead("HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\USBSTOR\Enum\1")
On Error GoTo 0
Flag_1 = Flag_Enum_1
End Sub
Do While Count = 1 'Real-time loop,the code within while is running while count is equal to 1. The script is loaded in memory constanlty.
On Error Resume Next
Enum_0 = Shell.RegRead("HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\USBSTOR\Enum\0") ' Try to read hardware id if it is in Enum\0
On Error GoTo 0 '
On Error Resume Next
Enum_1 = Shell.RegRead("HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\USBSTOR\Enum\1") 'Try to read hardware id if it is in Enum\1
On Error GoTo 0
If StrComp(Hwid,Enum_0) <> 0 And StrComp(Hwid,Enum_1) <> 0 Then 'Check if both reg keys are empty
MsgBox "There is no device in the front usb.Please put the device or see the connection"
ElseIf StrComp(Hwid,Enum_0) = 0 Then 'If the hardware id is in Enum\0,thus assigned to Enum_0
Shell.RegWrite "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\USBSTOR\Start",3 'Enable the usb by "unlock" it
On Error Resume Next
QueryEnum0 'Invoke sub QueryEnum0.If the id we looking for is in Enum\0,we know that it is assigned to Flag_0 also
Check_0 = Flag_0 'Use another variable to copy value from Flag_0.
On Error GoTo 0
If StrComp(Hwid,Check_0) = 0 Then 'Compare the constant Hwid with the value in Check_0,test for id
Msgbox "Check_0 still holds the hardware id" 'Some messages to inform us whats happening
else
MsgBox "Check_0 does not contain the hardware id anymore"
Shell.RegWrite "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\USBSTOR\Start",4 'Disable the front usb
Count = 2 'End the while loop,count is 2,so the condition is false .The loop breaks.
End If
ElseIf StrComp(Hwid,Enum_1) = 0 Then 'If the hardware is in Enum\1....same as above mentioned
Shell.RegWrite "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\USBSTOR\Start",3
On Error Resume Next
QueryEnum1
Check_1 = Flag_1
On Error GoTo 0
If StrComp(Hwid,Check_1) = 0 Then
MsgBox "Check_1 still holds the hardware id"
MsgBox Check_1
else
MsgBox "Check_0 does not contain the hardware id anymore"
Shell.RegWrite "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\USBSTOR\Start",4
Count = 2
End If
End If
Loop
' Useful information for me
'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\USBSTOR -> value name -> Start ,value data = 3(enable) = 4(disable)
'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\USBSTOR\Enum -> value name -> 1 or 0 ,value data we look for is -> USB\Vid_04da&Pid_2372\5&2f621ee5&0&8
' USB\Vid_04da&Pid_2372\5&2f621ee5&0&8 - camera id in our case
' fantom value - USB\Vid_03f0&Pid_032a\000000000Q912WFBSI1c - name: 0 ,type: REG_SZ,in the key Enum.This is another hardware id,which is strange somehow,because I do not have any device
' inserted in my usb.However,I take this value into account,thus use both keys 0 and 1 within Enum to scan for the id I need.
According to the Microsoft documentation the data in the CurrentControlSet Registry tree is used during start up and driver initialization.
Also with Windows 7 and later any changes to HKEY_LOCAL_MACHINE must be made by a utility running as an Administrator. Otherwise the changes will be made to a user clone of HKEY_LOCAL_MACHINE and changes will affect only the user under whose account the utility was run.
See HKLM\SYSTEM\CurrentControlSet\Services Registry Tree which states
"A driver can store global driver-defined data under its key in the
Services tree. Information that is stored under this key is available
to the driver during its initialization."
Everything that I have found thus far concerning additional security for USB storage devices by setting this value indicates that it is done once as a preventive measure. So it would appear that the approach you outlined is not a feasible solution.
There may also be an initial state issue in that the until a USB mass storage device is plugged in, the device driver is not fully initialized hence this data may not have yet been accessed. Readings also seem to imply that it will depend on whether the device has been previously plugged in successfully, creating the necessary Registry data and driver initialization or not.
I think it is pretty safe to say that changing the Registry value on the fly in this way was not design intent for Windows USB drivers.
See also this page of Microsoft Knowledge Base article 103000, CurrentControlSet\Services Subkey Entries for details about the data in this Registry entry. This article says the following about the Start keyword values.
0x3 (Load on demand) Available, regardless of type, but will not be started until the user starts it (for example, by using the Devices icon in Control Panel).
0x4 (disabled) NOT TO BE STARTED UNDER ANY CONDITIONS.
See also the following stackoverflow posts.
C# Disable/Enable USB ports
Enable and Disable USB port
Win32 API function to programmatically enable/disable device

Resources