delete files

I want an applescript which uses shell (because Finder seems unreliable when dealing with a large number of items sequentially) to do this:

Delete(move to trash) all “.avi" & ".srt” files in a folder if a “*.mkv” exists in the same folder
where * = filename is exactly same for all the three files.

Basically, I use mkvmerge which merges the subtitle file (srt) into an avi file and creates an mkv file. I don’t want to over-write/remove the avi file with mkvmerge because I want to check if the newly created file runs properly.

The tricky part is putting items in the (various) Trash folder(s) in a way that is similar to FInder.

For instance, if the name of the item to be trashed is already in the Trash folder, Finder will insert a string right before the file’s extension so that it does not overwrite the older trashed file. To me, it looks like this is a time-stamp of some sort. I am not sure what Finder does if there is a collision with the time-stamped name though.

Another issue is that there is more than one Trash folder. On the startup disk, each user has one in their home folder (~/.Trash). Then for each non-startup volume there can be one for each user under .Trashes in the root of the volume (/Volume/vol_name/.Trashes/uid). Actually, the startup volume also usually has a /.Trashes, but it is not usually used. These per-volume Trash folders are used when trashing files on those volumes to avoid having to copy the trashed items back to the startup drive just to trash them.

If you disregard both of these complications, then it is pretty easy to use find, xargs, sh, and mv to do the job. In the code below, I tackled the first Finder-Trash-emulation problem (using unique filenames in the Trash folder), but not the second (if you use it to “trash” items that are not on your boot volume, the trashed files will be moved to the Trash folder in your homedir instead of using the per-Volume one. If you need to use the per-Volume Trash folders, it would make good “exercise for the reader” since it is completely orthogonal to the rest of the code (do something to calculate trash_dir instead of using the hard-coded value).

-- Comment out the 'mv' line in the trasher_script to see what it would have done without actually doing anything.
set dirAlias to choose folder with prompt "Choose a folder to (recursively) delete <name>.avi and <name>.srt only if <name>.mkv exists:"

set trasher_script to "
set -eu
unused() {
	! test -f \"$1\" && ! test -d \"$1\"
}
find_unused_name_in_dir() {
	local filename name ext time_str n
	# Try the name itself.
	filename=\"$1\"
	if unused \"${2}${filename}\"; then
		printf '%s\\n' \"$filename\"
		return 0
	fi

	# Try the name with a time inserted before the extension.
	name=\"${filename%.*}\"
	ext=\"${filename##*.}\"
	test \"$name\" = \"$ext\" && ext=''
	time_str=\"$(date +%H-%M-%S)\" # current hour, minute and second
	filename=\"${name} ${time_str}.${ext}\"
	if unused \"${2}${filename}\"; then
		printf '%s\\n' \"$filename\"
		return 0
	fi

	# Try name with time and a sequence number inserted before the extension.
	n=2
	while test \"$n\" -le 100; do
		filename=\"${name} ${time_str} ${n}.${ext}\"
		if unused \"${2}${filename}\"; then
			printf '%s\\n' \"$filename\"
			return 0
		fi
		n=\"$((n+1))\"
	done
	printf 'failed to find unique name for %s in %s\\n' \"$1\" \"$2\" 1>&2
	return 1 # failed to find unique name in dir
}
move_to_trash() {
	local trash_dir filename
	# ideally, if the file is not on the startup disk, then we would use /Volumes/$vol/.Trashes/$UID/
	trash_dir=\"$HOME\"/.Trash/

	# Find a unused name in the trash_dir
	if ! filename=\"$(find_unused_name_in_dir \"${1##*/}\" \"$trash_dir\")\"; then
		printf 'Unable to find unique name for %s in %s\\n' \"$1\" \"$trash_dir\"
		return 0
	fi

	# Move it to the unused name in the trash dir
	mv \"$1\" \"${trash_dir}${filename}\"
	printf 'Moved %s to %s\\n' \"$1\" \"${trash_dir}${filename}\"
}

for mkv_name in \"$@\"; do
	name=\"${mkv_name%.mkv}\" # strip off .mkv
	test -f \"${name}.mkv\" || continue
	for input_file in \"$name\".avi \"$name\".srt; do
		if test -f \"$input_file\"; then 
			move_to_trash \"$input_file\"
		fi
	done
done
"

set find_and_trasher_script to ¬
	"find " & quoted form of POSIX path of dirAlias & ¬
	" -type f -name '*.mkv' -print0 | " & ¬
	"xargs -0 " & ¬
	"sh -c " & quoted form of trasher_script & " sh"

do shell script find_and_trasher_script

Many thanks chrys. I love to read your “comprehensive” posts in this forum.

OMG!! I had no idea that something so easily achieved with applescript can be so difficult with shell script. (Or atleast it looks difficult to read and understand.)
I had not thought about the trash folder problem.

I am not upto it. I really wasn’t expecting to get into something like this. I think I would rather use “rm” or just move the files to a folder “deleteme” on the same volume with mv command.

What do linux users do if they won’t to delete something but not use “rm” so that they can retrieve it later?

The difference is that Finder wraps up a bunch of details into its commands/interface. Down at the Darwin level (i.e. “UNIX”), you do not get any of those extras in your commands, but you do not have any of the extra baggage either. If you want to accomplish “trashing” at the shell level, you have to go through all the basic operations that Finder does “under the hood”. I used shell functions to encapsulate the “trash” code, but even discounting that code, the rest is fairly opaque unless you already know some of the shell tools (find, xargs, and the various bits of shell functionality).

Just using rm (in place of move_to_trash) would certainly simplify the script (all of the shell functions could be deleted since they are just there to try to replicate parts of how Finder does “trash”). The trasher_script would be reduced to the one loop at the end of its code.

Using a volume-specific “deleteme” folder would still need extra work to calculate its per-volume location (˜What volume is this file on?‘, ˜Where is the “deleteme” folder on this volume?’, ˜Is it relative to the file or to the volume itself?'). Since it would not purport to being “trash”-like though, you could trim out some of the code by not searching for an “ununsed name”. But really, the difference between a per-volume “deleteme” and using the normal per-volume “trash” folder is really very little (a call to id -u to get the numeric user ID and a conditional mkdir (to create the proper per-user, per-volume trash folder)).

I consider myself a “UNIX” user (Mac OS X), but I have not used my Linux systems since the last time I moved. I just use rm. After decades of working without a “trash can” (though some of it was DEL not rm!), I have learned to treat deletion commands with the respect they are due. I do not use rm lightly. When writing scripts and one-liners, I always use “dry run” methods (use something like echo to display what would be done instead of actually doing it). Backups, and version control tools also help. Sometimes I lose things and have to recreate them. Those times strengthen the lesson to keep in mind that “the safety is off” when it comes to rm. For interactive use, some people like to use a shell alias to change “rm” to “rm -i” so that it will prompt for confirmation before each deletion. I tried that when I was first starting with UNIX systems, but I found it annoying (plus it is not generally usable in scripts, since there may not be anyone there to answer the questions).