Skip to content

Instantly share code, notes, and snippets.

@jasalt
Created May 31, 2022 17:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jasalt/1e0c1dcc110c2a74aaa1887dede8904e to your computer and use it in GitHub Desktop.
Save jasalt/1e0c1dcc110c2a74aaa1887dede8904e to your computer and use it in GitHub Desktop.
Replace file shortcuts in given path with files they point to (on MacOS).
#!/usr/bin/env python
import glob
import os
import sys
def print_help():
print(''' Replaces file shortcuts in given path with files they represent (on MacOS).
Directory shortcuts are skipped. Requires Python 3.10.
Usage:
python deshortcut_path.py # process current workdir
python deshortcut_path.py # process given dir
''')
match length := len(sys.argv):
case 1:
print("No argument supplied, using current workdir.")
search_path = os.getcwd()
case 2:
search_path_relative = sys.argv[1]
if not os.path.isdir(search_path_relative):
print("ERR: Argument is not a directory.")
print_help()
exit(1)
search_path = os.path.abspath(search_path_relative)
case _:
print("ERR: Multiple arguments not supported.")
print_help()
exit(1)
print(f"Processing path {search_path}")
filepaths = os.listdir(search_path)
from mac_alias import resolve_osx_alias, isAlias
from shutil import copyfile
count_successful = 0
for file in filepaths:
try:
if not isAlias(file):
continue
except Exception as e:
# TODO throws exception for some reason (MacOS Monterey), isAlias is buggy...
print(f"Skipped something, maybe not alias {file}")
continue
file_alias = file
file_original = resolve_osx_alias(file_alias)
if not os.path.isfile(file_original):
print(f"Skipped shortcut with missing original file {file_original}")
continue
os.remove(file_alias) # remove alias/shortcut file
copyfile(file_original, file_alias) # move original file in it's place
print(f"Replaced alias {file_alias} with {file_original}")
count_successful += 1
print(f'Replaced {count_successful} shortcuts with original files.')
#!/usr/bin/env python3
#
# Resolve Mac OS X 'aliases' by finding where they point to
# Author: Scott H. Hawley
#
# Description:
# Mac OSX aliases are not symbolic links. Trying to read one will probably crash your code.
# Here a few routines to help. Run these to change the filename before trying to read a file.
# Intended to be called from within other python code
#
# Python port modified from https://hints.macworld.com/article.php?story=20021024064107356
#
# Requirements: osascript (AppleScript), platform, subprocess, shlex
#
# TODO: - could make it work in parallel when mutliple filenames are given
#
# NOTE: By default, this only returns the names of the original source files,
# but if you set convert=True, it will also convert aliases to symbolic links.
# SOURCE: https://drscotthawley.github.io/Resolving-OSX-Aliases/
import subprocess
import platform
import os
import shlex
# returns true if a file is an OSX alias, false otherwise
def isAlias(path, already_checked_os=False):
if (not already_checked_os) and ('Darwin' != platform.system()): # already_checked just saves a few microseconds ;-)
return False
checkpath = os.path.abspath(path) # osascript needs absolute paths
# Next several lines are AppleScript
line_1='tell application "Finder"'
line_2='set theItem to (POSIX file "'+checkpath+'") as alias'
line_3='if the kind of theItem is "alias" then'
line_4=' return true'
line_5='else'
line_6=' return false'
line_7='end if'
line_8='end tell'
cmd = "osascript -e '"+line_1+"' -e '"+line_2+"' -e '"+line_3+"' -e '"+line_4+"' -e '"+line_5+"' -e '"+line_6+"' -e '"+line_7+"' -e '"+line_8+"'"
args = shlex.split(cmd) # shlex splits cmd up appropriately so we can call subprocess.Popen with shell=False (better security)
p = subprocess.Popen(args, shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
retval = p.wait()
if (0 == retval):
line = p.stdout.readlines()[0]
line2 = line.decode('UTF-8').replace('\n','')
if ('true' == line2):
return True
else:
return False
else:
print('resolve_osx_alias: Error: subprocess returned non-zero exit code '+str(retval))
return None
# returns the full path of the file "pointed to" by the alias
def resolve_osx_alias(path, already_checked_os=False, convert=False): # single file/path name
if (not already_checked_os) and ('Darwin' != platform.system()): # already_checked just saves a few microseconds ;-)
return path
checkpath = os.path.abspath(path) # osascript needs absolute paths
# Next several lines are AppleScript
line_1='tell application "Finder"'
line_2='set theItem to (POSIX file "'+checkpath+'") as alias'
line_3='if the kind of theItem is "alias" then'
line_4=' get the posix path of (original item of theItem as text)'
line_5='else'
line_6='return "'+checkpath+'"'
line_7 ='end if'
line_8 ='end tell'
cmd = "osascript -e '"+line_1+"' -e '"+line_2+"' -e '"+line_3+"' -e '"+line_4+"' -e '"+line_5+"' -e '"+line_6+"' -e '"+line_7+"' -e '"+line_8+"'"
args = shlex.split(cmd) # shlex splits cmd up appropriately so we can call subprocess.Popen with shell=False (better security)
p = subprocess.Popen(args, shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
retval = p.wait()
if (0 == retval):
line = p.stdout.readlines()[0]
source = line.decode('UTF-8').replace('\n','')
if (convert):
os.remove(checkpath)
os.symlink(source, checkpath)
else:
print('resolve_osx_aliases: Error: subprocess returned non-zero exit code '+str(retval))
source = ''
return source
# used for multiple files at a time, just a looped call to resolve_osx_alias
def resolve_osx_aliases(filelist, convert=False): # multiple files
#print("filelist = ",filelist)
if ('Darwin' != platform.system()):
return filelist
outlist = []
for infile in filelist:
source = resolve_osx_alias(infile, already_checked_os=True, convert=convert)
if ('' != source):
outlist.append(source)
#print("outlist = ",outlist)
return outlist
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description='Resolve OSX aliases')
parser.add_argument('file', help="alias files to resolve", nargs='+')
args = parser.parse_args()
outlist = resolve_osx_aliases(args.file)
print("outlist = ",outlist)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment