如何从python读取,更改和写入macOS文件别名 [英] how to read, change, and write macOS file alias from python

查看:117
本文介绍了如何从python读取,更改和写入macOS文件别名的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有什么方法可以读取macOS文件别名,修改其内容(尤其是目标文件路径)并写回修改后的别名?



例如,如果我具有以下目录结构:

  ./ one / file.txt 
./two/file。 txt
./file_alias

其中 file_alias 解析为 ./ one / file.txt 。我希望能够以编程方式在Python中读取 ./ file_alias ,确定其路径,将一更改为二,并写出修改后的别名,覆盖 ./ file_alias 。完成后, file_alias 将解析为 ./ two / file.txt



搜索我找到了一个提示它不能完成的相关问题的答案(@Milliway对[1]的回答),一个没有实质性文档的Carbon模块,并且声明其功能已经实现。删除了[2],依赖于Carbon的部分弃用的macostools模块[3],等效的,未解决的问题(尝试使用PyObjC的建议除外)[4],以及最近更新的mac_alias软件包[5],但尚未找到



最初的mac_alias包似乎很有趣,但是我发现没有办法导入构造in所需的字节。现有别名文件中的-memory Alias 对象(使用别名文件的二进制读取中的字节会产生错误),即使我可以构造内存中的 Alias 记录并修改它,无法将其写出到磁盘中。



我要在其中运行10.12.x(Sierra)的计算机,并且我正在使用内置的python 2.7.10。我发现我实际上可以导入Carbon和macostools,并且怀疑Carbon.File可能提供了我所需要的,但是我找不到任何文档。我可以升级到High Sierra和/或安装并使用Python 3.x,但是在现阶段这些似乎没有帮助或相关。



我意识到别名还包含一个索引节点,经过这样的更改后会过时,但值得庆幸的是,部分原因是我提交了一个错误,以及当我在Apple时遇到了一些持久性,别名首先解析了路径,只是回退到了



任何帮助,建议和指针,我们都感激不尽。

/ p>

[1] 如何使用os.walk()处理Python中的OSX别名?

[2] https://docs.python.org/2/library/carbon.html

[3] https:// d ocs.python.org/2/library/macostools.html

[4] 更改别名目标python

[5] https://pypi.python.org/pypi/mac_alias

解决方案

尽管存在,但仍使用PyObjC解决了该问题几乎没有关于PyObjC的文档。您必须使用此网站,同时在此处中所述。



@MagerValp对此问题的回复中的代码有助于弄清楚如何获取别名的目标。我必须弄清楚如何用经过修改的目标来创建新别名。



下面是一个包含并执行所有所需功能的测试程序。



我是一个坏人,没有做文档字符串或输入和返回值的描述,但是我保持所有函数简短和单功能,希望我已经对所有变量和函数进行了足够清晰的命名,以致不需要它们。公认的CamelCaps和underscore_separated变量和函数名的组合很奇怪。通常,我将CamelCaps用于全局常量,并将下划线分隔的函数和变量名称用于这种情况,但在这种情况下,我想保持PyObjC调用中使用camelCaps引用的变量和数据类型不变,从而避免出现奇怪的混合情况。



请注意,Mac Finder会缓存一些有关别名的信息。因此,如果您在运行该程序后立即在 file_alias 上执行获取信息或解决方案,即使它确实无法正常工作。您必须将一个文件夹拖到垃圾箱,并清空垃圾箱,只有这样,获取信息或解析为 file_alias 表明它确实确实指向 ./ two / file.txt 。 (发牢骚,发牢骚。)幸运的是,这不会影响我对这些技术的使用,也不会影响大多数人的使用。程序的目的通常是基于一个固定的别名来替换一个损坏的别名,该别名基于一些简单的事情的改变,例如本示例中的文件夹名称,或我实际应用中的卷名称。 / p>

最后,代码:

 #!/ usr / bin / env python 

#fix_alias.py
#一个测试程序,用于行使针对macOS文件别名(书签)的重定向功能。
#作者:Larry Yaeger,2018年2月20日

#创建一个文件和目录层次结构,如下所示:

#一个
#文件.txt
#两个
#file.txt
#file_alias

#其中一个和两个是文件夹,file.txt文件实际上是任何文件,并且
#file_alias是一个Mac文件别名,它指向./one/file.txt。然后在一个,两个和file_alias所在的文件夹中运行该程序
#。它将用
#替换file_alias#别名指向./two/file.txt。

#注意,file_alias不是符号链接,即使Mac Finder有时
#假装符号链接是别名;他们不是。

导入os
导入字符串

从Foundation导入*


OldFolder ='one'
NewFolder ='两个'
AliasPath ='file_alias'


def get_bookmarkData(alias_path):
alias_url = NSURL.fileURLWithPath_(alias_path)
书签数据,错误= NSURL.bookmarkDataWithContentsOfURL_error_(alias_url,None)
返回书签数据


def get_target_of_bookmarkData(bookmarkData):
如果书签数据为None:
返回None
选项= NSURLBookmarkResolutionWithoutUI | $ URL b
$ b def create_bookmarkData(new_path):
new_url = NSURL.fileURLWithPath_(new_path)
选项= NSURLBookmarkCreationSuitableForBookmarkFile
new_bookmarkData,错误= \
new_url.bookmarkDataWithOptions_包括错误资源值(ForKey)
选项,无,无,无)
返回new_bookmarkData


def create_alias(bookmarkData,alias_path):
alias_url = NSURL.fileURLWithPath_(alias_path)
选项= NSURLBookmarkCreationSuitableForBookmarkFile
成功,错误= NSURL.writeBookmarkData_toURL_options_error_(bookmarkData,alias_url,options,None)
返回成功


def main():
old_bookmarkData = get_bookmarkData(A liasPath)
old_path = get_target_of_bookmarkData(old_bookmarkData)
打印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)
打印new_path
os.remove(AliasPath)
create_alias(new_bookmarkData,AliasPath)


main()


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 the following directory structure:

./one/file.txt
./two/file.txt
./file_alias

where file_alias resolves to ./one/file.txt. I would like to be able to programmatically, in Python, read ./file_alias, determine its path, change 'one' to 'two', and write the revised alias out, overwriting ./file_alias. Upon completion, file_alias would resolve to ./two/file.txt.

Searching I've found an answer to a related question that suggests it can't be done (@Milliway's answer to [1]), a Carbon module with no substantive documentation and a statement that its functionality has been removed [2], a partially deprecated macostools module that depends on Carbon [3], an equivalent, unanswered question (except a tentative suggestion to use PyObjC) [4], and a recently updated mac_alias package [5], but have not found a way to accomplish the task based on any of these.

The mac_alias package at first seemed interesting, but I have found no way to import the bytes needed to construct an in-memory Alias object from an existing alias file (using bytes from a binary read of the alias file produces errors) and even if I could construct an in-memory Alias record and modify it, there is no way to write it out to disk.

The machine where I want this is running 10.12.x (Sierra) and I am using the built-in python 2.7.10. I find I can actually import Carbon and macostools, and suspect Carbon.File might conceivably provide what I need, but I cannot find any documentation for it. I could upgrade to High Sierra and/or install and use Python 3.x, but those don't seem to be helpful or relevant at this stage.

I realize that the alias also contains an inode, that will be stale after such a change, but thankfully, in part due to a bug I filed and a bit of persistence back when I was with Apple, an alias resolves the path first, only falls back to the inode if the path fails to resolve, and updates the inode if the path does resolve (and the inode has changed).

Any help, suggestions, pointers appreciated.

[1] How to handle OSX Aliases in Python with os.walk()?
[2] https://docs.python.org/2/library/carbon.html
[3] https://docs.python.org/2/library/macostools.html
[4] change an alias target python
[5] https://pypi.python.org/pypi/mac_alias

解决方案

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()

这篇关于如何从python读取,更改和写入macOS文件别名的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆