how to read, change, and write macOS file alias from python

前端 未结 2 1495
暖寄归人
暖寄归人 2021-01-13 21:28

Is there any way to read a macOS file alias, modify its contents (particularly the target file path), and write the modified alias back out?

For example, if I have t

2条回答
  •  孤街浪徒
    2021-01-13 21:53

    Solved it, using PyObjC, despite there being almost no documentation for PyObjC. You have to carefully convert ObjectiveC interfaces for NSURL to PyObjC calls, using the techniques described in "An Introduction to PyObjC" found on this site while referring to the NSURL interfaces described here.

    Code in @MagerValp's reply to this question helped figure out how to get the target of an alias. I had to work out how to create a new alias with a revised target.

    Below is a test program that contains and exercises all the functionality needed. Its setup and use are documented with comments in the code.

    I'm a bad person and didn't do doc strings or descriptions of inputs and return values, but I've kept all functions short and single-functioned and hopefully I've named all variables and functions sufficiently clearly that they are not needed. There's an admittedly weird combination of CamelCaps and underscore_separated variable and function names. I normally use CamelCaps for global constants and underscore_separated names for functions and variables, but in this case I wanted to keep the variables and data types referred to in the PyObjC calls, which use camelCaps, unchanged, hence the odd mix.

    Be warned, the Mac Finder caches some information about aliases. So if you do a Get Info or a resolve on file_alias immediately after running this program, it will look like it didn't work, even though it did. You have to drag the one folder to the Trash and empty the Trash, and only then will a Get Info or resolve of file_alias show that it does indeed now point to ./two/file.txt. (Grumble, grumble.) Fortunately this will not impact my use of these techniques, nor will it affect most people's use, I suspect. The point of the program will normally be to replace a broken alias with a fixed one, based on the fact that some single, simple thing changed, like the folder name in this example, or the volume name in my real application for this.

    Finally, the code:

    #!/usr/bin/env python
    
    # fix_alias.py
    # A test program to exercise functionality for retargeting a macOS file alias (bookmark).
    # Author: Larry Yaeger, 20 Feb 2018
    #
    # Create a file and directory hierarchy like the following:
    #
    # one
    #   file.txt
    # two
    #   file.txt
    # file_alias
    #
    # where one and two are folders, the file.txt files are any files really, and
    # file_alias is a Mac file alias that points to ./one/file.txt.  Then run this program
    # in the same folder as one, two, and file_alias.  It will replace file_alias with
    # an alias that points to ./two/file.txt.
    #
    # Note that file_alias is NOT a symbolic link, even though the Mac Finder sometimes
    # pretends symbolic links are aliases; they are not.
    
    import os
    import string
    
    from Foundation import *
    
    
    OldFolder = 'one'
    NewFolder = 'two'
    AliasPath = 'file_alias'
    
    
    def get_bookmarkData(alias_path):
      alias_url = NSURL.fileURLWithPath_(alias_path)
      bookmarkData, error = NSURL.bookmarkDataWithContentsOfURL_error_(alias_url, None)
      return bookmarkData
    
    
    def get_target_of_bookmarkData(bookmarkData):
      if bookmarkData is None:
        return None
      options = NSURLBookmarkResolutionWithoutUI | NSURLBookmarkResolutionWithoutMounting
      resolved_url, stale, error = \
        NSURL.URLByResolvingBookmarkData_options_relativeToURL_bookmarkDataIsStale_error_(
          bookmarkData, options, None, None, None)
      return resolved_url.path()
    
    
    def create_bookmarkData(new_path):
      new_url = NSURL.fileURLWithPath_(new_path)
      options = NSURLBookmarkCreationSuitableForBookmarkFile
      new_bookmarkData, error = \
        new_url.bookmarkDataWithOptions_includingResourceValuesForKeys_relativeToURL_error_(
          options, None, None, None)
      return new_bookmarkData
    
    
    def create_alias(bookmarkData, alias_path):
      alias_url = NSURL.fileURLWithPath_(alias_path)
      options = NSURLBookmarkCreationSuitableForBookmarkFile
      success, error = NSURL.writeBookmarkData_toURL_options_error_(bookmarkData, alias_url, options, None)
      return success
    
    
    def main():
      old_bookmarkData = get_bookmarkData(AliasPath)
      old_path = get_target_of_bookmarkData(old_bookmarkData)
      print old_path
      new_path = string.replace(old_path, OldFolder, NewFolder, 1)
      new_bookmarkData = create_bookmarkData(new_path)
      new_path = get_target_of_bookmarkData(new_bookmarkData)
      print new_path
      os.remove(AliasPath)
      create_alias(new_bookmarkData, AliasPath)
    
    
    main()
    

提交回复
热议问题