Created
May 31, 2022 17:02
-
-
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).
This file contains 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
#!/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.') |
This file contains 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
#!/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