Simple parsing but I cannot get it

I am trying to write a script to delete all the snapshots that Big sur creates from Time Machine backups. In terminal I do “tmutil listlocalsnapshots /” to list them. The listlocalsnapshots command returns → “Snapshots for disk /:
com.apple.TimeMachine.2021-12-08-004944.local
com.apple.TimeMachine.2021-12-08-120549.local
com.apple.TimeMachine.2021-12-08-130503.local
com.apple.TimeMachine.2021-12-08-150112.local”
Now I have to delete them all 1 at a time with “deletelocalsnapshots 2021-12-08-004944”
I want to automate this in a script and leave only one the most recent.

So far I have got:


set m to (do shell script "tmutil listlocalsnapshots /")
set AppleScript's text item delimiters to ":"
set m to text items of m
set m to rest of m
-- this leaves me with --> "Snapshots for disk /: removed and all snapshots but the last entry

what I cannot do is to isolate each one of the time stamps and delete them one at a time with shell script deletelocalsnapshots
Can anybody help, please?

I found out that tmutil thinlocalsnapshots / 100000000000 1 will do the job. I still would like to see how it is done in Applescrept if anyone would care to show mw

I suspect that thinlocalsnapshots does not just delete them. It probably works like Time Machine, when it “deletes” expired backups - their actual content is merged with a more recent backup.

So deleting snapshots with AppleScript is quite probably unwise.

I think I solved it.


--set localSnapshots to (do shell script "tmutil listlocalsnapshots /") -- in real life this is what needs to be done
set localSnapshots to "Snapshots for disk /:
com.apple.TimeMachine.2021-12-08-170101.local
com.apple.TimeMachine.2021-12-08-170102.local
com.apple.TimeMachine.2021-12-08-170120.local
com.apple.TimeMachine.2021-12-08-170120.local
com.apple.TimeMachine.2021-12-08-170120.local
com.apple.TimeMachine.2021-12-08-170120.local
com.apple.TimeMachine.2021-12-08-170120.local
com.apple.TimeMachine.2021-12-08-170120.local
com.apple.TimeMachine.2021-12-08-170120.local
com.apple.TimeMachine.2021-12-08-170120.local
com.apple.TimeMachine.2021-12-08-170120.local
com.apple.TimeMachine.2021-12-08-170119.local
com.apple.TimeMachine.2021-12-08-170113.local
com.apple.TimeMachine.2021-12-08-100014.local" -- and this needs to go. only good for testing

set theQuestion to "I found the following snapshots." & return & localSnapshots & return & "Would you like to keep the most recent one?"
set theAnswer to button returned of (display dialog theQuestion buttons {"yes", "no"} default button "yes")

set oldDelims to AppleScript's text item delimiters -- save their current state
set AppleScript's text item delimiters to {".local"}
set croppedSnapshotsList to every text item of localSnapshots
set theNr to count croppedSnapshotsList

if theAnswer is "yes" then
	set theNr to theNr - 2
else
	set theNr to theNr - 1
end if

repeat with a from 1 to theNr
	set theCurrentListItem to text item a of croppedSnapshotsList
	-- Process the current list item
	set AppleScript's text item delimiters to {"."} -- declare new delimiters
	set thisSnapshot to text items of croppedSnapshotsList
	set timeStamp to text item 4 of theCurrentListItem
	set cmnd to "tmutil deletelocalsnapshots " & timeStamp
	
	display dialog cmnd & " is item " & a & " in the list." -- -- in real life this is not needed
	
	-- do shell script cmnd -- in real life this is what needs to be done
end repeat

Hi - so, here’s how I would handle it…


set localSnapshots to text returned of (do shell script "tmutil listlocalsnapshots /")

set SnapShotItems to every paragraph of localSnapshots
set SnapShotList to items 3 thru -1 of SnapShotItems
set listCount to count SnapShotList

--build a dialog to query the user
set q1 to "By default, this program will delete all but the most current TimeMachine snapshot; if this is what you'd like to accomplish, click \"Default\""
set q2 to "To select specific snapshots for deletion, click \"Select\""

set queryString to q1 & return & return & q2
set theMethod to button returned of (display dialog queryString with title "Select a method of deletion." buttons ["Cancel", "Select", "Default"] default button 3 cancel button 1)
if theMethod is "Default" then
	set deleteString to "" --build and itemized list tailored as an argument for the shell...
	set listCount to count SnapShotList
	repeat with i from 1 to listCount
		set ss to SnapShotList's item i
		if i = listCount then
			set deleteString to deleteString & ss
		else
			set deleteString to deleteString & ss & space
		end if
	end repeat
else if theMethod is "Select" then
	set deleteString to ""
	set selectList to (choose from list SnapShotList with multiple selections allowed)
	set selectCount to count selectList
	repeat with j from 1 to selectCount
		set xx to selectList's item j
		if j = selectCount then
			set deleteString to deleteString & xx
		else
			set deleteString to deleteString & xx & space
		end if
	end repeat
end if
 
--now just put the pieces of the shell script together
set shellCmnd to "tmutil delete" & space & deleteString
do shell script shellCmnd

One thing to note is that you’ll need to affix the path to the directory where the snapshots are stored to the front of every entry you wish to delete either before or while you’re building the deleteString variable. Probably a good way to do that is to create a variable which contains the path to that directory early on in the script. Then when the deleteString is being build in theMethod section of the script, you could simply concatinate it with each list entry as you go e.g.

set deleteString to deleteString & path_to_Directory & ss & space

Thank you all for your help

re:" I suspect that thinlocalsnapshots does not just delete them"
I found this post in stackoverflow that seems to confirm that both the snapshot and the storage are deleted if you set the purge_amount and urgency params
https://apple.stackexchange.com/questions/309143/how-to-thin-your-local-time-machine-snapshots-on-macos-high-sierra

I appreciate the alternative ways you show me of doing it.
My question is:
Are you saying that what I wrote faulty or just not the best code to achieve it?

Thanks again for your interest and help.

Sorry sineEyed I tested your code and found the following problems:

set localSnapshots to text returned of (do shell script “tmutil listlocalsnapshots /”) fails
I changed it to:
set localSnapshots to (do shell script “tmutil listlocalsnapshots /”) as text
and it worked

set shellCmnd to “tmutil delete” & space & deleteString fails with the following error:
tmutil: delete requires root privileges.
I changed it to:
“sudo tmutil delete” & space & deleteString
and it failed with the following error:
sudo: a terminal is required to read the password; either use the -S option to read from standard input or configure an askpass helper
Changing that to
“sudo tmutil delete” & space & deleteString asks for password but then it fails again
It also fails when there are no localsnapshots with error:
Can’t get items 3 thru -1 of {“Snapshots for disk /:”}this is because
set SnapShotList to items 3 thru -1 of SnapShotItems fails when there are no snapshots