public static void DeleteDirectory(string path, bool contentsOnly, bool continueOnContentDeleteError, CancellationToken cancellationToken) { ArgUtil.NotNullOrEmpty(path, nameof(path)); DirectoryInfo directory = new DirectoryInfo(path); if (!directory.Exists) { return; } if (!contentsOnly) { // Remove the readonly flag. RemoveReadOnly(directory); // Check if the directory is a reparse point. if (directory.Attributes.HasFlag(FileAttributes.ReparsePoint)) { // Delete the reparse point directory and short-circuit. directory.Delete(); return; } } // Initialize a concurrent stack to store the directories. The directories // cannot be deleted until the files are deleted. var directories = new ConcurrentStack <DirectoryInfo>(); if (!contentsOnly) { directories.Push(directory); } // Create a new token source for the parallel query. The parallel query should be // canceled after the first error is encountered. Otherwise the number of exceptions // could get out of control for a large directory with access denied on every file. using (var tokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken)) { try { // Recursively delete all files and store all subdirectories. Enumerate(directory, tokenSource) .AsParallel() .WithCancellation(tokenSource.Token) .ForAll((FileSystemInfo item) => { bool success = false; try { // Remove the readonly attribute. RemoveReadOnly(item); // Check if the item is a file. if (item is FileInfo) { // Delete the file. item.Delete(); } else { // Check if the item is a directory reparse point. var subdirectory = item as DirectoryInfo; ArgUtil.NotNull(subdirectory, nameof(subdirectory)); if (subdirectory.Attributes.HasFlag(FileAttributes.ReparsePoint)) { try { // Delete the reparse point. subdirectory.Delete(); } catch (DirectoryNotFoundException) { // The target of the reparse point directory has been deleted. // Therefore the item is no longer a directory and is now a file. // // Deletion of reparse point directories happens in parallel. This case can occur // when reparse point directory FOO points to some other reparse point directory BAR, // and BAR is deleted after the DirectoryInfo for FOO has already been initialized. File.Delete(subdirectory.FullName); } } else { // Store the directory. directories.Push(subdirectory); } } success = true; } catch (Exception) when(continueOnContentDeleteError) { // ignore any exception when continueOnContentDeleteError is true. success = true; } finally { if (!success) { tokenSource.Cancel(); // Cancel is thread-safe. } } }); } catch (Exception) { tokenSource.Cancel(); throw; } } // Delete the directories. foreach (DirectoryInfo dir in directories.OrderByDescending(x => x.FullName.Length)) { cancellationToken.ThrowIfCancellationRequested(); dir.Delete(); } }
public static string Which(string command, bool require = false, ITraceWriter trace = null) { ArgUtil.NotNullOrEmpty(command, nameof(command)); trace?.Info($"Which: '{command}'"); string path = Environment.GetEnvironmentVariable(PathUtil.PathVariable); if (string.IsNullOrEmpty(path)) { trace?.Info("PATH environment variable not defined."); path = path ?? string.Empty; } string[] pathSegments = path.Split(new Char[] { Path.PathSeparator }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < pathSegments.Length; i++) { pathSegments[i] = Environment.ExpandEnvironmentVariables(pathSegments[i]); } foreach (string pathSegment in pathSegments) { if (!string.IsNullOrEmpty(pathSegment) && Directory.Exists(pathSegment)) { string[] matches = null; #if OS_WINDOWS string pathExt = Environment.GetEnvironmentVariable("PATHEXT"); if (string.IsNullOrEmpty(pathExt)) { // XP's system default value for PATHEXT system variable pathExt = ".com;.exe;.bat;.cmd;.vbs;.vbe;.js;.jse;.wsf;.wsh"; } string[] pathExtSegments = pathExt.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries); // if command already has an extension. if (pathExtSegments.Any(ext => command.EndsWith(ext, StringComparison.OrdinalIgnoreCase))) { try { matches = Directory.GetFiles(pathSegment, command); } catch (UnauthorizedAccessException ex) { trace?.Info("Ignore UnauthorizedAccess exception during Which."); trace?.Verbose(ex.ToString()); } if (matches != null && matches.Length > 0) { trace?.Info($"Location: '{matches.First()}'"); return(matches.First()); } } else { string searchPattern; searchPattern = StringUtil.Format($"{command}.*"); try { matches = Directory.GetFiles(pathSegment, searchPattern); } catch (UnauthorizedAccessException ex) { trace?.Info("Ignore UnauthorizedAccess exception during Which."); trace?.Verbose(ex.ToString()); } if (matches != null && matches.Length > 0) { // add extension. for (int i = 0; i < pathExtSegments.Length; i++) { string fullPath = Path.Combine(pathSegment, $"{command}{pathExtSegments[i]}"); if (matches.Any(p => p.Equals(fullPath, StringComparison.OrdinalIgnoreCase))) { trace?.Info($"Location: '{fullPath}'"); return(fullPath); } } } } #else try { matches = Directory.GetFiles(pathSegment, command); } catch (UnauthorizedAccessException ex) { trace?.Info("Ignore UnauthorizedAccess exception during Which."); trace?.Verbose(ex.ToString()); } if (matches != null && matches.Length > 0) { trace?.Info("Location: '{matches.First()}'"); return(matches.First()); } #endif } } trace?.Info("Not found."); if (require) { throw new FileNotFoundException( message: StringUtil.Loc("FileNotFound", command), fileName: command); } return(null); }
public string Which(string command) { ArgUtil.NotNullOrEmpty(command, nameof(command)); Trace.Verbose($"{nameof(command)}={command}"); #if OS_WINDOWS string path = Environment.GetEnvironmentVariable("Path"); #else string path = Environment.GetEnvironmentVariable("PATH"); #endif if (string.IsNullOrEmpty(path)) { return(null); } #if OS_WINDOWS char pathSep = ';'; #else char pathSep = ':'; #endif string[] pathSegments = path.Split(new Char[] { pathSep }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < pathSegments.Length; i++) { pathSegments[i] = Environment.ExpandEnvironmentVariables(pathSegments[i]); } foreach (string pathSegment in pathSegments) { if (!string.IsNullOrEmpty(pathSegment) && Directory.Exists(pathSegment)) { string[] matches; #if OS_WINDOWS string pathExt = Environment.GetEnvironmentVariable("PATHEXT"); if (string.IsNullOrEmpty(pathExt)) { // XP's system default value for PATHEXT system variable pathExt = ".com;.exe;.bat;.cmd;.vbs;.vbe;.js;.jse;.wsf;.wsh"; } string[] pathExtSegments = pathExt.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries); // if command already has an extension. if (pathExtSegments.Any(ext => command.EndsWith(ext, StringComparison.OrdinalIgnoreCase))) { matches = Directory.GetFiles(pathSegment, command); if (matches != null && matches.Length > 0) { return(matches.First()); } } else { string searchPattern; searchPattern = StringUtil.Format($"{command}.*"); matches = Directory.GetFiles(pathSegment, searchPattern); if (matches != null && matches.Length > 0) { // add extension. for (int i = 0; i < pathExtSegments.Length; i++) { string fullPath = Path.Combine(pathSegment, StringUtil.Format($"{command}{pathExtSegments[i]}")); if (matches.Any(p => p.Equals(fullPath, StringComparison.OrdinalIgnoreCase))) { return(fullPath); } } } } #else matches = Directory.GetFiles(pathSegment, command); if (matches != null && matches.Length > 0) { return(matches.First()); } #endif } } return(null); }
public async Task <int> ExecuteAsync( string workingDirectory, string fileName, string arguments, IDictionary <string, string> environment, bool requireExitCodeZero, Encoding outputEncoding, bool killProcessOnCancel, InputQueue <string> redirectStandardIn, bool inheritConsoleHandler, CancellationToken cancellationToken) { ArgUtil.Null(_proc, nameof(_proc)); ArgUtil.NotNullOrEmpty(fileName, nameof(fileName)); Trace.Info("Starting process:"); Trace.Info($" File name: '{fileName}'"); Trace.Info($" Arguments: '{arguments}'"); Trace.Info($" Working directory: '{workingDirectory}'"); Trace.Info($" Require exit code zero: '{requireExitCodeZero}'"); Trace.Info($" Encoding web name: {outputEncoding?.WebName} ; code page: '{outputEncoding?.CodePage}'"); Trace.Info($" Force kill process on cancellation: '{killProcessOnCancel}'"); Trace.Info($" Redirected STDIN: '{redirectStandardIn != null}'"); Trace.Info($" Persist current code page: '{inheritConsoleHandler}'"); _proc = new Process(); _proc.StartInfo.FileName = fileName; _proc.StartInfo.Arguments = arguments; _proc.StartInfo.WorkingDirectory = workingDirectory; _proc.StartInfo.UseShellExecute = false; _proc.StartInfo.CreateNoWindow = !inheritConsoleHandler; _proc.StartInfo.RedirectStandardInput = true; _proc.StartInfo.RedirectStandardError = true; _proc.StartInfo.RedirectStandardOutput = true; // Ensure we process STDERR even the process exit event happen before we start read STDERR stream. if (_proc.StartInfo.RedirectStandardError) { Interlocked.Increment(ref _asyncStreamReaderCount); } // Ensure we process STDOUT even the process exit event happen before we start read STDOUT stream. if (_proc.StartInfo.RedirectStandardOutput) { Interlocked.Increment(ref _asyncStreamReaderCount); } #if OS_WINDOWS // If StandardErrorEncoding or StandardOutputEncoding is not specified the on the // ProcessStartInfo object, then .NET PInvokes to resolve the default console output // code page: // [DllImport("api-ms-win-core-console-l1-1-0.dll", SetLastError = true)] // public extern static uint GetConsoleOutputCP(); StringUtil.EnsureRegisterEncodings(); #endif if (outputEncoding != null) { _proc.StartInfo.StandardErrorEncoding = outputEncoding; _proc.StartInfo.StandardOutputEncoding = outputEncoding; } // Copy the environment variables. if (environment != null && environment.Count > 0) { foreach (KeyValuePair <string, string> kvp in environment) { _proc.StartInfo.Environment[kvp.Key] = kvp.Value; } } // Set the TF_BUILD env variable. _proc.StartInfo.Environment["TF_BUILD"] = "True"; // Hook up the events. _proc.EnableRaisingEvents = true; _proc.Exited += ProcessExitedHandler; // Start the process. _stopWatch = Stopwatch.StartNew(); _proc.Start(); // Start the standard error notifications, if appropriate. if (_proc.StartInfo.RedirectStandardError) { StartReadStream(_proc.StandardError, _errorData); } // Start the standard output notifications, if appropriate. if (_proc.StartInfo.RedirectStandardOutput) { StartReadStream(_proc.StandardOutput, _outputData); } if (_proc.StartInfo.RedirectStandardInput) { if (redirectStandardIn != null) { StartWriteStream(redirectStandardIn, _proc.StandardInput); } else { // Close the input stream. This is done to prevent commands from blocking the build waiting for input from the user. _proc.StandardInput.Close(); } } using (var registration = cancellationToken.Register(async() => await CancelAndKillProcessTree(killProcessOnCancel))) { Trace.Info($"Process started with process id {_proc.Id}, waiting for process exit."); while (true) { Task outputSignal = _outputProcessEvent.WaitAsync(); var signaled = await Task.WhenAny(outputSignal, _processExitedCompletionSource.Task); if (signaled == outputSignal) { ProcessOutput(); } else { _stopWatch.Stop(); break; } } // Just in case there was some pending output when the process shut down go ahead and check the // data buffers one last time before returning ProcessOutput(); Trace.Info($"Finished process {_proc.Id} with exit code {_proc.ExitCode}, and elapsed time {_stopWatch.Elapsed}."); } cancellationToken.ThrowIfCancellationRequested(); // Wait for process to finish. if (_proc.ExitCode != 0 && requireExitCodeZero) { throw new ProcessExitCodeException(exitCode: _proc.ExitCode, fileName: fileName, arguments: arguments); } return(_proc.ExitCode); }
public static void DeleteDirectory(string path, CancellationToken cancellationToken) { ArgUtil.NotNullOrEmpty(path, nameof(path)); DirectoryInfo directory = new DirectoryInfo(path); if (!directory.Exists) { return; } // Initialize a concurrent stack to store the directories. The directories // cannot be deleted until the files are deleted. var directories = new ConcurrentStack <DirectoryInfo>(); // Remove the readonly flag and store the root directory. RemoveReadOnly(directory); directories.Push(directory); // Create a new token source for the parallel query. The parallel query should be // canceled after the first error is encountered. Otherwise the number of exceptions // could get out of control for a large directory with access denied on every file. using (var tokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken)) { // Delete all files and store all subdirectories. directory .EnumerateFileSystemInfos("*", SearchOption.AllDirectories) .AsParallel() .WithCancellation(tokenSource.Token) .ForAll((FileSystemInfo item) => { bool success = false; try { // Check if the item is a file. var file = item as FileInfo; if (file != null) { // Delete the file. RemoveReadOnly(file); file.Delete(); success = true; return; } // The item is a directory. var subdirectory = item as DirectoryInfo; ArgUtil.NotNull(subdirectory, nameof(subdirectory)); // Remove the readonly attribute and store the subdirectory. RemoveReadOnly(subdirectory); directories.Push(subdirectory); success = true; } finally { if (!success) { tokenSource.Cancel(); // Cancel is thread-safe. } } }); } // Delete the directories. foreach (DirectoryInfo dir in directories.OrderByDescending(x => x.FullName.Length)) { cancellationToken.ThrowIfCancellationRequested(); dir.Delete(); } }