20

Python で NTFS ジャンクション ポイントを作成する方法はありますか? ユーティリティを呼び出すことができることはわかっていjunctionますが、外部ツールに依存しない方がよいでしょう。

4

5 に答える 5

12

Python 3.5 以降CreateJunction_winapiモジュールに関数があります。

import _winapi
_winapi.CreateJunction(source, target)
于 2016-02-01T18:46:26.633 に答える
9

同様の質問でこれに答えたので、その回答を以下にコピーします。その答えを書いて以来、このフォルダーにあるジャンクションを作成、読み取り、およびチェックするためのpython-only(ctypes python-onlyを使用するモジュールを呼び出すことができる場合)モジュールを作成しました。それが役立つことを願っています。

また、 CreateSymbolicLinkA APIを使用する回答とは異なり、リンクされた実装は、ジャンクションをサポートするすべての Windows バージョンで動作するはずです。CreateSymbolicLinkA は、Vista 以降でのみサポートされています。

答え:

python ntfslink 拡張機能

または、pywin32 を使用したい場合は、前述の方法を使用して、以下を使用して読み取ることができます。

from win32file import *
from winioctlcon import FSCTL_GET_REPARSE_POINT

__all__ = ['islink', 'readlink']

# Win32file doesn't seem to have this attribute.
FILE_ATTRIBUTE_REPARSE_POINT = 1024
# To make things easier.
REPARSE_FOLDER = (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT)

# For the parse_reparse_buffer function
SYMBOLIC_LINK = 'symbolic'
MOUNTPOINT = 'mountpoint'
GENERIC = 'generic'

def islink(fpath):
    """ Windows islink implementation. """
    if GetFileAttributes(fpath) & REPARSE_FOLDER:
        return True
    return False


def parse_reparse_buffer(original, reparse_type=SYMBOLIC_LINK):
    """ Implementing the below in Python:

    typedef struct _REPARSE_DATA_BUFFER {
        ULONG  ReparseTag;
        USHORT ReparseDataLength;
        USHORT Reserved;
        union {
            struct {
                USHORT SubstituteNameOffset;
                USHORT SubstituteNameLength;
                USHORT PrintNameOffset;
                USHORT PrintNameLength;
                ULONG Flags;
                WCHAR PathBuffer[1];
            } SymbolicLinkReparseBuffer;
            struct {
                USHORT SubstituteNameOffset;
                USHORT SubstituteNameLength;
                USHORT PrintNameOffset;
                USHORT PrintNameLength;
                WCHAR PathBuffer[1];
            } MountPointReparseBuffer;
            struct {
                UCHAR  DataBuffer[1];
            } GenericReparseBuffer;
        } DUMMYUNIONNAME;
    } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;

    """
    # Size of our data types
    SZULONG = 4 # sizeof(ULONG)
    SZUSHORT = 2 # sizeof(USHORT)

    # Our structure.
    # Probably a better way to iterate a dictionary in a particular order,
    # but I was in a hurry, unfortunately, so I used pkeys.
    buffer = {
        'tag' : SZULONG,
        'data_length' : SZUSHORT,
        'reserved' : SZUSHORT,
        SYMBOLIC_LINK : {
            'substitute_name_offset' : SZUSHORT,
            'substitute_name_length' : SZUSHORT,
            'print_name_offset' : SZUSHORT,
            'print_name_length' : SZUSHORT,
            'flags' : SZULONG,
            'buffer' : u'',
            'pkeys' : [
                'substitute_name_offset',
                'substitute_name_length',
                'print_name_offset',
                'print_name_length',
                'flags',
            ]
        },
        MOUNTPOINT : {
            'substitute_name_offset' : SZUSHORT,
            'substitute_name_length' : SZUSHORT,
            'print_name_offset' : SZUSHORT,
            'print_name_length' : SZUSHORT,
            'buffer' : u'',
            'pkeys' : [
                'substitute_name_offset',
                'substitute_name_length',
                'print_name_offset',
                'print_name_length',
            ]
        },
        GENERIC : {
            'pkeys' : [],
            'buffer': ''
        }
    }

    # Header stuff
    buffer['tag'] = original[:SZULONG]
    buffer['data_length'] = original[SZULONG:SZUSHORT]
    buffer['reserved'] = original[SZULONG+SZUSHORT:SZUSHORT]
    original = original[8:]

    # Parsing
    k = reparse_type
    for c in buffer[k]['pkeys']:
        if type(buffer[k][c]) == int:
            sz = buffer[k][c]
            bytes = original[:sz]
            buffer[k][c] = 0
            for b in bytes:
                n = ord(b)
                if n:
                    buffer[k][c] += n
            original = original[sz:]

    # Using the offset and length's grabbed, we'll set the buffer.
    buffer[k]['buffer'] = original
    return buffer

def readlink(fpath):
    """ Windows readlink implementation. """
    # This wouldn't return true if the file didn't exist, as far as I know.
    if not islink(fpath):
        return None

    # Open the file correctly depending on the string type.
    handle = CreateFileW(fpath, GENERIC_READ, 0, None, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT, 0) \
                if type(fpath) == unicode else \
            CreateFile(fpath, GENERIC_READ, 0, None, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT, 0)

    # MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16384 = (16*1024)
    buffer = DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, None, 16*1024)
    # Above will return an ugly string (byte array), so we'll need to parse it.

    # But first, we'll close the handle to our file so we're not locking it anymore.
    CloseHandle(handle)

    # Minimum possible length (assuming that the length of the target is bigger than 0)
    if len(buffer) < 9:
        return None
    # Parse and return our result.
    result = parse_reparse_buffer(buffer)
    offset = result[SYMBOLIC_LINK]['substitute_name_offset']
    ending = offset + result[SYMBOLIC_LINK]['substitute_name_length']
    rpath = result[SYMBOLIC_LINK]['buffer'][offset:ending].replace('\x00','')
    if len(rpath) > 4 and rpath[0:4] == '\\??\\':
        rpath = rpath[4:]
    return rpath

def realpath(fpath):
    from os import path
    while islink(fpath):
        rpath = readlink(fpath)
        if not path.isabs(rpath):
            rpath = path.abspath(path.join(path.dirname(fpath), rpath))
        fpath = rpath
    return fpath


def example():
    from os import system, unlink
    system('cmd.exe /c echo Hello World > test.txt')
    system('mklink test-link.txt test.txt')
    print 'IsLink: %s' % islink('test-link.txt')
    print 'ReadLink: %s' % readlink('test-link.txt')
    print 'RealPath: %s' % realpath('test-link.txt')
    unlink('test-link.txt')
    unlink('test.txt')

if __name__=='__main__':
    example()

CreateFile の属性を必要に応じて調整しますが、通常の状況では機能するはずです。気軽に改善してください。

SYMBOLIC_LINK の代わりに MOUNTPOINT を使用すると、フォルダー ジャンクションでも機能するはずです。

あなたはそれを確認する方法があるかもしれません

sys.getwindowsversion()[0] >= 6

この形式のシンボリック リンクは Vista+ でのみサポートされているため、これをリリースするものに入れる場合。

于 2012-04-19T02:59:18.880 に答える
8

たとえば、python win32 APIモジュールを使用できます

import win32file

win32file.CreateSymbolicLink(srcDir, targetDir, 1)

詳細については、 http://docs.activestate.com/activepython/2.5/pywin32/win32file__CreateSymbolicLink_meth.htmlを参照してください。

それにも頼りたくない場合は、いつでも ctypes を使用して CreateSymbolicLinl win32 API を直接呼び出すことができます。これはとにかく単純な呼び出しです。

これはctypesを使用した呼び出しの例です

import ctypes

kdll = ctypes.windll.LoadLibrary("kernel32.dll")

kdll.CreateSymbolicLinkA("d:\testdir", "d:\testdir_link", 1)

MSDNによると、サポートされている最小クライアント Windows Vista

于 2009-07-17T13:38:16.193 に答える
1

外部ツールに依存したくないが、特定の環境に依存してもかまいませんか? 実行しているのが NTFS である場合、ジャンクション ユーティリティはおそらくそこにあると想定しても問題ないと思います。

しかし、外部プログラムを呼び出したくないということであれば、ctypesの機能は非常に貴重であることがわかりました。これにより、Windows DLL を Python から直接呼び出すことができます。そして、最近の標準の Python リリースに含まれていると確信しています。

API 呼び出し (または Windows が呼び出しているもの) がどのWindows DLL に含まれているかを把握しCreateJunction()、パラメーターと呼び出しを設定するだけで済みます。幸いなことに、マイクロソフトはそれをうまくサポートしていないようです。SysInternalsプログラムまたは他のツールのいずれかを逆アセンブルして、その方法を調べることができます。junctionlinkd

私、私はかなり怠け者ですjunction。外部プロセスとして呼び出すだけです:-)

于 2009-07-17T13:30:26.843 に答える