0

我创建了一个脚本,它使用 Robocopy 创建一个 txt 文件,其中包含驱动器上每个文件的完整路径(约 500 万个对象)。然后我将该文件导入 foreach 循环并下载允许大文件路径 (get-ntfsowner) 的模块,该模块将遍历文件路径并在 CSV 中输出文件路径和所有者。然后,我使用该文件运行另一个脚本,我们的支持中心将使用该脚本将所有这些插入 subinacl,以便在用户离开公司时将所有用户的文件所有权更改给他们的经理。

该脚本运行完美,但问题是找到所有者并创建 CSV 需要几天时间。我发现也许哈希表可以帮助我,但我对此非常迷茫。我还认为 get-acl 会快得多,但我遇到了 256 个字符的限制,并且花了几个小时试图让 \?\ 工作但没有成功。我很确定我的瓶颈是 foreach 循环减慢了速度。

有人可以帮助我获取此代码并使其更有效率吗?我正试图将它降低到每晚在隔夜窗口中运行的位置。

#Gets Owner for each file And Create CSV
$Dir = "D:\AutoAssign\Data\RoboLogs"
$Files = Get-ChildItem $Dir
ForEach ($File in $Files){ Start-Job -ScriptBlock {
    Param($Dir,$Files,$File)

$OutputFile = "D:\AutoAssign\Data\Final\$File.csv"
$Results = @()

$Paths = Get-Content $Dir\$File
ForEach ($Path in $Paths){
 $Owner = get-ntfsowner $Path | Select Owner | ft -hidetableheaders | Out-String
 $Owner = $Owner.Trim()

      $Properties = @{

      Path = $Path
      Owner = $Owner

      }

If ($Owner -ne "BUILTIN\Administrators" -and $Owner -ne $null-and $Owner -ne "Domain\Domain Admins" -and $Owner -notlike "S-1*"){
    $Results += New-Object psobject -Property $properties}

    }


$Results | Export-Csv -notypeinformation -Path $OutputFile

} -ArgumentList $Dir,$Files,$File #Ends Initial ForEach

}  #Ends Job Script Block
4

1 回答 1

0

不确定这是否对你有帮助,考虑到你有这么多文件要处理。如果你确实需要像 Mehrdad 解释的那样直接调用 Windows API 你可以尝试合并下面的代码并使用它而不是get-ntfsownerto看看它是否工作得更快。

另外我建议把这部分代码

$Properties = @{
  Path = $Path
  Owner = $Owner
}

一行下来,所以就在

If ($Owner -ne "BUILTIN\Administrators" -and $Owner -ne $null-and $Owner -ne "Domain\Domain Admins" -and $Owner -notlike "S-1*")

当然,它不会做太多事情,但它可以让您在不需要时避免创建散列。也许测试本身可以整理一下,例如:

If (($Owner) -and $Owner -notmatch '^(BUILTIN\\|NT AUTHORITY\\|S-1).+|(.+\\Domain Admins)$')

无论如何,这是使用 Win32 api 克服 Get-Ace 的 260 个字符限制的代码

if (-not ([System.Management.Automation.PSTypeName]'Win32Api.LongFileName').Type) {
    Add-Type -Language CSharp -TypeDefinition @"
    using System;
    using System.Text;
    using System.Runtime.InteropServices;
    using Microsoft.Win32.SafeHandles;
    using System.Security.Principal;

    namespace Win32Api {
        public static class LongFileName
        {
            internal const uint ERROR_SUCCESS = 0;

            internal const int INVALID_FILE_ATTRIBUTES  = -1;
            internal const int FILE_ATTRIBUTE_READONLY  = 0x1;      // A file that is read-only. This attribute is not honored on directories.
            internal const int FILE_ATTRIBUTE_HIDDEN    = 0x2;      // The file or directory is hidden. It is not included in an ordinary directory listing.
            internal const int FILE_ATTRIBUTE_SYSTEM    = 0x4;      // A file or directory that the operating system uses a part of, or uses exclusively.
            internal const int FILE_ATTRIBUTE_NORMAL    = 0x80;     // A file that does not have other attributes set. This attribute is valid only when used alone.
            internal const int FILE_ATTRIBUTE_DIRECTORY = 0x10;     // The handle that identifies a directory.
            internal const int FILE_ATTRIBUTE_ARCHIVE   = 0x20;     // A file or directory that is an archive file or directory.
            internal const int FILE_READ_ATTRIBUTES     = 0x0080;
            internal const int FILE_WRITE_ATTRIBUTES    = 0x0100;

            internal const int FILE_READ_DATA           = 0x0001;
            internal const int FILE_WRITE_DATA          = 0x0002;
            internal const int FILE_APPEND_DATA         = 0x0004;
            internal const int FILE_READ_EA             = 0x0008;
            internal const int FILE_WRITE_EA            = 0x0010;

            internal const int FILE_SHARE_NONE          = 0x00000000;
            internal const int FILE_SHARE_READ          = 0x00000001;

            internal const long READ_CONTROL            = 0x00020000L;
            internal const long STANDARD_RIGHTS_READ    = READ_CONTROL;
            internal const long STANDARD_RIGHTS_WRITE   = READ_CONTROL;
            internal const long SYNCHRONIZE             = 0x00100000L;

            internal const long FILE_GENERIC_WRITE = STANDARD_RIGHTS_WRITE |
                                                     FILE_WRITE_DATA |
                                                     FILE_WRITE_ATTRIBUTES |
                                                     FILE_WRITE_EA |
                                                     FILE_APPEND_DATA |
                                                     SYNCHRONIZE;

            internal const long FILE_GENERIC_READ = STANDARD_RIGHTS_READ |
                                                    FILE_READ_DATA |
                                                    FILE_READ_ATTRIBUTES |
                                                    FILE_READ_EA |
                                                    SYNCHRONIZE;


            internal const int CREATE_NEW    = 1;
            internal const int CREATE_ALWAYS = 2;
            internal const int OPEN_EXISTING = 3;

            internal const int MAX_PATH      = 260;
            internal const int MAX_ALTERNATE = 14;

            enum SE_OBJECT_TYPE
            {
                SE_UNKNOWN_OBJECT_TYPE,
                SE_FILE_OBJECT,
                SE_SERVICE,
                SE_PRINTER,
                SE_REGISTRY_KEY,
                SE_LMSHARE,
                SE_KERNEL_OBJECT,
                SE_WINDOW_OBJECT,
                SE_DS_OBJECT,
                SE_DS_OBJECT_ALL,
                SE_PROVIDER_DEFINED_OBJECT,
                SE_WMIGUID_OBJECT,
                SE_REGISTRY_WOW64_32KEY
            }
            enum SECURITY_INFORMATION
            {
                OWNER_SECURITY_INFORMATION = 1,
                GROUP_SECURITY_INFORMATION = 2,
                DACL_SECURITY_INFORMATION = 4,
                SACL_SECURITY_INFORMATION = 8,
            }

            [DllImport("advapi32.dll", SetLastError = true)]
            static extern uint GetSecurityInfo(IntPtr handle, SE_OBJECT_TYPE objectType, SECURITY_INFORMATION securityInfo, out IntPtr sidOwner, out IntPtr sidGroup, out IntPtr dacl, out IntPtr sacl, out IntPtr securityDescriptor);

            [DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true)]
            static extern bool ConvertSidToStringSid(IntPtr sid, out IntPtr sidString);

            [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
            static extern int GetFileAttributesW(string lpFileName);

            [DllImport("kernel32.dll", SetLastError = true)]
            static extern IntPtr LocalFree(IntPtr handle);

            [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
            static extern SafeFileHandle CreateFile(string lpFileName, int dwDesiredAccess, int dwShareMode, IntPtr lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile);

            [DllImport("kernel32.dll", SetLastError=true)]
            static extern bool CloseHandle(IntPtr handle);

            public static string GetFileOwner(string filename)
            {
                IntPtr fileHandle;
                IntPtr ownerSid;
                IntPtr groupSid;
                IntPtr dacl;
                IntPtr sacl;
                IntPtr securityDescriptor = IntPtr.Zero;
                uint result = 0;

                try
                {
                    fileHandle = GetFileHandle(filename);
                    result = GetSecurityInfo(fileHandle, SE_OBJECT_TYPE.SE_FILE_OBJECT, SECURITY_INFORMATION.OWNER_SECURITY_INFORMATION | SECURITY_INFORMATION.DACL_SECURITY_INFORMATION, out ownerSid, out groupSid, out dacl, out sacl, out securityDescriptor);
                    IntPtr ptrString = IntPtr.Zero;
                    bool success = ConvertSidToStringSid(ownerSid, out ptrString);
                    string strSid = Marshal.PtrToStringAuto(ptrString);
                    Marshal.FreeHGlobal(ptrString);

                    // convert the user sid string to a domain\name. Needs .NET 2.0 or better
                    string account = new SecurityIdentifier(strSid).Translate(typeof(NTAccount)).ToString();
                    if (!string.IsNullOrEmpty(account))
                    {
                        return account;  // domain\user
                    }
                    else
                    {
                        // Console.WriteLine("Could not translate SID to username. User may have been deleted.");
                        return strSid;
                    }
                }
                finally
                {
                    LocalFree(securityDescriptor);
                    // If you use a SafeFileHandle, do not call CloseHandle as the CLR will close it for you (even if it's already closed).
                    // CloseHandle(fileHandle);
                }
            }

            public static IntPtr GetFileHandle(string filename)
            {
                if (filename.Length >= MAX_PATH) filename = AddLongPathPrefix(filename);
                SafeFileHandle hfile = CreateFile(filename, (int)FILE_GENERIC_READ, FILE_SHARE_READ, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
                if (hfile.IsInvalid) ThrowWin32Exception();
                return hfile.DangerousGetHandle();
            }

            public static string AddLongPathPrefix(string path)
            {
                if (path.StartsWith(@"\\?\")) return path;

                var newpath = path;
                if (newpath.StartsWith("\\"))
                {
                    newpath = @"\\?\UNC\" + newpath.Substring(2);
                }
                else if (newpath.Contains(":"))
                {
                    newpath = @"\\?\" + newpath;
                }
                else
                {
                    var currdir = Environment.CurrentDirectory;
                    newpath = CombinePath(currdir, newpath);
                    while (newpath.Contains("\\.\\")) newpath = newpath.Replace("\\.\\", "\\");
                    newpath = @"\\?\" + newpath;
                }
                return newpath.TrimEnd('.');
            }

            public static string CombinePath(string path1, string path2)
            {
                return path1.TrimEnd('\\') + "\\" + path2.TrimStart('\\').TrimEnd('.');
            }

            public static bool Exists(string path)
            {
                if (path.Length < MAX_PATH) return System.IO.File.Exists(path);
                var attr = GetFileAttributesW(AddLongPathPrefix(path));
                return (attr != INVALID_FILE_ATTRIBUTES && (((attr & FILE_ATTRIBUTE_ARCHIVE) == FILE_ATTRIBUTE_ARCHIVE) || 
                                                            ((attr & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)));
            }

            private static void ThrowWin32Exception()
            {
                int code = Marshal.GetLastWin32Error();
                if (code != 0)
                {
                    throw new System.ComponentModel.Win32Exception(code);
                }
            }
        }
    }
"@
}

function Get-FileOwner {
    # Returns the owner of a file as string.
    # The script can handle both 'normal' path lengths aswell as Long paths (up to approx. 32760 characters)
    # The returned string MAY contain names such as "NT AUTHORITY\SYSTEM", "NT AUTHORITY\IUSR" or "BUILTIN\Administrators"
    # so you will have to exclude those afterwards if need be. When a string in the form of a SID string like
    # "S-1-5-21-3623811015-3361044348-30300820-1013" is returned, it usually means the user has already been deleted.
    [CmdletBinding()]
    param(
        [ValidateNotNullOrEmpty()]
        [string]$Path
    )
    if ([Win32Api.LongFileName]::Exists($Path)) {
        return [Win32Api.LongFileName]::GetFileOwner($Path)
    }
    Write-Warning "Path not found '$Path'"
}

该脚本是基于从多个地方收集的信息构建的,特别是通过改编Wolf5的代码

于 2018-08-15T10:34:08.287 回答