Created
September 22, 2016 17:44
-
-
Save diego898/d0346f92af63383b73c5895575254a02 to your computer and use it in GitHub Desktop.
trash osx command line application from: https://github.com/morgant/tools-osx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# | |
# trash - Move files to the appropriate .Trash file on Mac OS X. (Intended | |
# as an alternative to 'rm' which immediately deletes the file.) | |
# | |
# v0.1 2007-05-21 - Morgan Aldridge <[email protected]> | |
# Initial version. | |
# v0.2 2010-10-26 - Morgan Aldridge | |
# Use appropriate .Trashes folder when trashing files | |
# on other volumes. Create trash folder(s) if necessary. | |
# v0.2.1 2010-10-26 - Morgan Aldridge | |
# No longer using bash built-in regexp support in hopes | |
# of support Mac OS X 10.4 and earlier. | |
# v0.3 2010-12-07 - Morgan Aldridge | |
# Correctly handle full volume path which is root volume. | |
# Now increments filename if filename already exists in | |
# trash folder (à la Finder). | |
# v0.4 2011-06-02 - Morgan Aldridge | |
# Option to list trash contents w/disk usage total. Allows | |
# emptying of trash w/confirmation, incl. secure empty. | |
# v0.5 2011-07-04 - Morgan Aldridge | |
# Support for trashing/emptying using Finder via AppleScript, | |
# when available. | |
# v0.5.1 2012-03-29 - Matt Brictson & Morgan Aldridge | |
# Fixed bug where cwd would get trashed instead of specified | |
# file/path if it contained a relative path when trashing | |
# using Finder via AppleScript. | |
# v0.5.2 2012-11-30 - Morgan Aldridge | |
# Merged in fix for realpath() implementation to fix | |
# assumption that relative paths were in root directory. | |
# Also correctly warns of missing files/directories. | |
# v0.5.3 2013-08-23 - huyz | |
# Fixed determination of Finder's PID when Path Finder is running | |
# | |
# global variables | |
verbose=false | |
user=$(whoami) | |
uid=$(id -u "$user") | |
finder_pid=$(ps -u "$user" | grep CoreServices/Finder.app | grep -v grep | awk '{print $1}') | |
v='' | |
# print usage instructions (help) | |
function usage() { | |
printf "Usage: trash [options] file ...\n" | |
printf " -v verbose output\n" | |
printf " -h print these usage instructions\n" | |
printf " -l list trash contents\n" | |
printf " -e empty trash contents\n" | |
printf " -s secure empty trash contents\n" | |
} | |
# determine whether we can script the Finder or not | |
function have_scriptable_finder() { | |
# We must have a valid PID for Finder, plus we cannot be in `screen` (another thing that's broken) | |
if [[ ($finder_pid -gt 1) && ("$STY" == "") ]]; then | |
true | |
else | |
false | |
fi | |
} | |
## | |
## Convert a relative path to an absolute path. | |
## | |
## From http://github.com/morgant/realpath | |
## | |
## @param string the string to converted from a relative path to an absolute path | |
## @returns Outputs the absolute path to STDOUT, returns 0 if successful or 1 if an error (esp. path not found). | |
## | |
function realpath() | |
{ | |
local success=true | |
local path="$1" | |
# make sure the string isn't empty as that implies something in further logic | |
if [ -z "$path" ]; then | |
success=false | |
else | |
# start with the file name (sans the trailing slash) | |
path="${path%/}" | |
# if we stripped off the trailing slash and were left with nothing, that means we're in the root directory | |
if [ -z "$path" ]; then | |
path="/" | |
fi | |
# get the basename of the file (ignoring '.' & '..', because they're really part of the path) | |
local file_basename="${path##*/}" | |
if [[ ( "$file_basename" = "." ) || ( "$file_basename" = ".." ) ]]; then | |
file_basename="" | |
fi | |
# extracts the directory component of the full path, if it's empty then assume '.' (the current working directory) | |
local directory="${path%$file_basename}" | |
if [ -z "$directory" ]; then | |
directory='.' | |
fi | |
# attempt to change to the directory | |
if ! cd "$directory" &>/dev/null ; then | |
success=false | |
fi | |
if $success; then | |
# does the filename exist? | |
if [[ ( -n "$file_basename" ) && ( ! -e "$file_basename" ) ]]; then | |
success=false | |
fi | |
# get the absolute path of the current directory & change back to previous directory | |
local abs_path="$(pwd -P)" | |
cd "-" &>/dev/null | |
# Append base filename to absolute path | |
if [ "${abs_path}" = "/" ]; then | |
abs_path="${abs_path}${file_basename}" | |
else | |
abs_path="${abs_path}/${file_basename}" | |
fi | |
# output the absolute path | |
echo "$abs_path" | |
fi | |
fi | |
$success | |
} | |
# see if any arguments were passed in | |
if [ $# -gt 0 ]; then | |
# if so, step through them all and process them | |
while [ $# -gt 0 ]; do | |
# see if the user intended us to run in verbose mode | |
if [ "$1" = "-v" ]; then | |
shift | |
verbose=true | |
# see if the user requested help | |
elif [ "$1" = "-h" ]; then | |
shift | |
usage | |
exit | |
# see if the user requested a list of trash contents | |
elif [ "$1" = "-l" ]; then | |
shift | |
num_volumes=0 | |
total_blocks=0 | |
# list file contents & calculate size for user's .Trash folder | |
if find "/Users/${user}/.Trash" -depth 1 ! -depth 0; then | |
num_volumes=$(( $num_volumes + 1 )) | |
blocks=$(du -cs "/Users/${user}/.Trash" | tail -n 1 | cut -f 1) | |
total_blocks=$(( $total_blocks + $blocks )) | |
fi | |
# list file contents & calculate size for volume-specific .Trashes folders | |
for file in /Volumes/*; do | |
if [ -d "$file" ]; then | |
folder="${file}/.Trashes/${uid}" | |
if [ -d "${folder}" ]; then | |
if find "$folder" -depth 1 ! -depth 0; then | |
num_volumes=$(( $num_volumes + 1 )) | |
blocks=$(du -cs "$folder" | tail -n 1 | cut -f 1) | |
total_blocks=$(( $total_blocks + $blocks )) | |
fi | |
fi | |
fi | |
done | |
# convert blocks to human readable size | |
size=0 | |
if (( $total_blocks >= 2097152 )); then | |
size=$(bc <<< "scale=2; $total_blocks / 2097152") | |
size="${size}GB" | |
elif (( $total_blocks >= 2048 )); then | |
size=$(bc <<< "scale=2; $total_blocks / 2048") | |
size="${size}MB" | |
else | |
size=$(bc <<< "scale=2; $total_blocks / 2") | |
size="${size}K" | |
fi | |
printf "%s across %s volume(s).\n" "$size" $num_volumes | |
exit | |
# see if the user requested to empty the trash contents | |
elif [ "$1" = "-e" ]; then | |
shift | |
# determine if we can tell Finder to empty trash via AppleScript | |
if have_scriptable_finder; then | |
if $verbose; then printf "Telling Finder to empty trash... "; fi | |
if /usr/bin/osascript -e "tell application \"Finder\" to empty trash" ; then | |
if $verbose; then printf "Done.\n"; fi | |
exit | |
else | |
if $verbose; then printf "ERROR!\n"; fi | |
exit 1 | |
fi | |
# if Finder isn't scriptable, we'll manually empty the trash ourselves | |
else | |
if $verbose; then v="-v"; fi | |
# confirm that the user wants to empty the trash | |
printf "Are you sure you want to empty the trash (this cannot be undone)? " | |
read confirm | |
if [ "$confirm" = "y" ]; then | |
printf "Emptying trash...\n" | |
# delete the contents of user's .Trash folder | |
find "/Users/${user}/.Trash" -depth 1 ! -depth 0 -print0 | xargs -0 rm $v -r | |
# delete the contents of the volume-specific .Trashes folders | |
for file in /Volumes/*; do | |
if [ -d "$file" ]; then | |
folder="${file}/.Trashes/${uid}" | |
if [ -d "$folder" ]; then | |
find "$folder" -depth 1 ! -depth 0 -print0 | xargs -0 rm $v -r | |
fi | |
fi | |
done | |
printf "Done.\n" | |
fi | |
exit | |
fi | |
# see if the user requested to securely empty the trash contents | |
elif [ "$1" = "-s" ]; then | |
shift | |
# determine if we can tell Finder to securely empty trash via AppleScript | |
if have_scriptable_finder; then | |
if $verbose; then printf "Telling Finder to securely empty trash... "; fi | |
if /usr/bin/osascript -e "tell application \"Finder\" to empty trash with security" ; then | |
if $verbose; then printf "Done.\n"; fi | |
exit | |
else | |
if $verbose; then printf "ERROR!\n"; fi | |
exit 1 | |
fi | |
# if Finder isn't scriptable, we'll manually empty the trash ourselves | |
else | |
if $verbose; then v="-v"; fi | |
# confirm that the user wants to securely empty the trash | |
printf "Are you sure you want to securely empty the trash (this REALLY cannot be undone)? " | |
read confirm | |
if [ "$confirm" = "y" ]; then | |
printf "Securely emptying trash...\n" | |
# securely delete the contents of user's .Trash folder | |
find "/Users/${user}/.Trash" -depth 1 ! -depth 0 -print0 | xargs -0 srm $v -r | |
# securely delete the contents of the volume-specific .Trashes folders | |
for file in /Volumes/*; do | |
if [ -d "$file" ]; then | |
folder="${file}/.Trashes/${uid}" | |
if [ -d "$folder" ]; then | |
find "$folder" -depth 1 ! -depth 0 -print0 | xargs -0 srm $v -r | |
fi | |
fi | |
done | |
printf "Done.\n" | |
fi | |
exit | |
fi | |
# handle remaining arguments as if they were files | |
else | |
#printf "argument: '%s'\n" $1 | |
#printf "destination: '%s'\n" $TRASH | |
if $verbose; then v="-v"; fi | |
# does the file we're trashing exist? | |
if [ ! -e "$1" ]; then | |
printf "trash: '%s': No such file or directory\n" "$1" | |
else | |
# determine if we'll tell Finder to trash the file via AppleScript (very easy, plus free undo | |
# support, but Finder must be running for the user and is DOES NOT work from within `screen`) | |
if have_scriptable_finder; then | |
# determine whether we have an absolute path name to the file or not | |
if [ "${1:0:1}" = "/" ]; then | |
file="$1" | |
else | |
# expand relative to absolute path | |
if $verbose; then printf "Determining absolute path for '%s'... " "$1"; fi | |
file="$(realpath "$1")" | |
if [ $? -eq 0 ]; then | |
if $verbose; then printf "Done.\n"; fi | |
else | |
if $verbose; then printf "ERROR!\n"; fi | |
fi | |
fi | |
if $verbose; then printf "Telling Finder to trash '%s'... " "$file"; fi | |
if /usr/bin/osascript -e "tell application \"Finder\" to delete POSIX file \"$file\"" ; then | |
if $verbose; then printf "Done.\n"; fi | |
else | |
if $verbose; then printf "ERROR!\n"; fi | |
fi | |
# Finder isn't available for this user, so don't rely on it (we'll do all the dirty work ourselves) | |
else | |
# determine whether we should be putting this in a volume-specific .Trashes or user's .Trash | |
IFS=/ read -r -d '' _ _ vol _ <<< "$1" | |
if [[ ("${1:0:9}" == "/Volumes/") && (-n "$vol") && ($(readlink "/Volumes/$vol") != "/") ]]; then | |
trash="/Volumes/${vol}/.Trashes/${uid}/" | |
else | |
trash="/Users/${user}/.Trash/" | |
fi | |
# create the trash folder if necessary | |
if [ ! -d "$trash" ]; then | |
mkdir $v "$trash" | |
fi | |
# move the file to the trash | |
if [ ! -e "${trash}$1" ]; then | |
mv $v "$1" "$trash" | |
else | |
# determine if the filename has an extension | |
ext=false | |
case "$1" in | |
*.*) ext=true ;; | |
esac | |
# keep incrementing a number to append to the filename to mimic Finder | |
i=1 | |
if $ext; then | |
new="${trash}${1%%.*} ${i}.${1##*.}" | |
else | |
new="${trash}$1 $i" | |
fi | |
while [ -e "$new" ]; do | |
((i=$i + 1)) | |
if $ext; then | |
new="${trash}${1%%.*} ${i}.${1##*.}" | |
else | |
new="${trash}$1 $i" | |
fi | |
done | |
#move the file to the trash with the new name | |
mv $v "$1" "$new" | |
fi | |
fi | |
fi | |
shift | |
fi | |
done | |
else | |
printf "No files were specified to be moved to the trash.\n\n" | |
usage | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment