public CommandResult <(List <FileSystemPath> items, FindCounts counts)> Find( CommandEvaluationContext context, [Parameter("search path")] DirectoryPath path, [Option("p", "select names that matches the pattern", true, true)] string pattern, [Option("i", "if set and p is set, perform a non case sensisitive search")] bool ignoreCase, [Option("f", "check pattern on fullname instead of name")] bool checkPatternOnFullName, [Option("c", "files that contains the string", true, true)] string contains, [Option("a", "print file system attributes")] bool attributes, [Option("s", "print short pathes")] bool shortPathes, [Option("all", "select files and directories")] bool all, [Option("d", "select only directories")] bool dirs, [Option("t", "search in top directory only")] bool top ) { if (path.CheckExists()) { var sp = string.IsNullOrWhiteSpace(pattern) ? "*" : pattern; var counts = new FindCounts(); var items = FindItems(context, path.FullName, sp, top, all, dirs, attributes, shortPathes, contains, checkPatternOnFullName, counts, true, false, ignoreCase); var f = DefaultForegroundCmd; counts.Elapsed = DateTime.Now - counts.BeginDateTime; if (items.Count > 0) { context.Out.Echoln(); } context.Out.Echoln($"found {ColorSettings.Numeric}{Plur("file",counts.FilesCount,f)} and {ColorSettings.Numeric}{Plur("folder",counts.FoldersCount,f)}. scanned {ColorSettings.Numeric}{Plur("file",counts.ScannedFilesCount,f)} in {ColorSettings.Numeric}{Plur("folder",counts.ScannedFoldersCount,f)} during {TimeSpanDescription(counts.Elapsed, ColorSettings.Numeric.ToString(), f)}"); return(new CommandResult <(List <FileSystemPath>, FindCounts)>((items, counts))); } return(new CommandResult <(List <FileSystemPath>, FindCounts)>((new List <FileSystemPath>(), new FindCounts()), ReturnCode.Error)); }
public CommandResult <List <TextFileInfo> > More( CommandEvaluationContext context, [Parameter("file or folder path")] WildcardFilePath path, [Option("h", "hide-line-numbers", "hide line numbers")] bool hideLineNumbers, [Option("r", "raw", "turn on raw output")] bool raw ) { if (path.CheckExists(context)) { var counts = new FindCounts(); var items = FindItems(context, path.FullName, path.WildCardFileName ?? "*", true, false, false, true, false, null, false, counts, false, false); var r = new List <TextFileInfo>(); foreach (var item in items) { PrintFile(context, (FilePath)item, hideLineNumbers, raw); r.Add(new TextFileInfo((FilePath)item, null, OSPlatform.Create("?"), null)); } if (items.Count == 0) { context.Errorln($"more: no such file: {path.OriginalPath}"); return(new CommandResult <List <TextFileInfo> >(new List <TextFileInfo> { new TextFileInfo(new FilePath(path.OriginalPath), null, OSPlatform.Create("?"), null) }, ReturnCode.Error)); } context.Out.ShowCur(); return(new CommandResult <List <TextFileInfo> >(r)); } else { return(new CommandResult <List <TextFileInfo> >(new List <TextFileInfo> { new TextFileInfo(new FilePath(path.FullName), null, OSPlatform.Create("?"), null) }, ReturnCode.Error)); } }
public CommandResult <(List <FileSystemPath> items, FindCounts counts)> Dir( CommandEvaluationContext context, [Parameter("path where to list files and folders. if not specified is equal to the current directory. use wildcards * and ? to filter files and folders names", true)] WildcardFilePath path, [Option("na", "do not print file system attributes")] bool noattributes, [Option("r", "also list files and folders in sub directories. force display files full path")] bool recurse, [Option("w", "displays file names on several columns so output fills console width (only if not recurse mode). disable print of attributes")] bool wide ) { var r = new List <FileSystemPath>(); path ??= new WildcardFilePath(Environment.CurrentDirectory); if (path.CheckExists()) { var counts = new FindCounts(); var items = FindItems(context, path.FullName, path.WildCardFileName ?? "*", !recurse, true, false, !noattributes, !recurse, null, false, counts, false, false); var f = DefaultForegroundCmd; long totFileSize = 0; var cancellationTokenSource = new CancellationTokenSource(); if (wide) { noattributes = true; } void postCmd(object o, EventArgs e) { sc.CancelKeyPress -= cancelCmd; context.Out.Echoln($"{Tab}{ColorSettings.Numeric}{Plur("file", counts.FilesCount, f),-30}{HumanFormatOfSize(totFileSize, 2," ", ColorSettings.Numeric.ToString(), f)}"); context.Out.Echoln($"{Tab}{ColorSettings.Numeric}{Plur("folder", counts.FoldersCount, f),-30}{Drives.GetDriveInfo(path.FileSystemInfo.FullName,false, ColorSettings.Numeric.ToString(), f," ",2)}"); } void cancelCmd(object o, ConsoleCancelEventArgs e) { e.Cancel = true; cancellationTokenSource.Cancel(); } int printResult() { var i = 0; int maxitlength = 0; counts.FilesCount = counts.FoldersCount = 0; foreach (var item in items) { if (item.IsFile) { totFileSize += ((FileInfo)item.FileSystemInfo).Length; counts.FilesCount++; } else { counts.FoldersCount++; } maxitlength = Math.Max(item.Name.Length, maxitlength); } maxitlength += 4; var(id, left, top, right, bottom) = DotNetConsole.ActualWorkArea(); var nbcols = Math.Floor((double)(right - left + 1) / (double)maxitlength); int nocol = 0; foreach (var item in items) { if (cancellationTokenSource.IsCancellationRequested) { return(i); } item.Print(!noattributes, !recurse, "", (!wide || recurse || nocol == nbcols - 1) ? Br : "", (wide && !recurse) ? maxitlength : -1); i++; nocol++; if (nocol == nbcols) { nocol = 0; } } if (!recurse && wide && nocol < nbcols && nocol > 0) { context.Out.Echoln(); } return(i); } sc.CancelKeyPress += cancelCmd; var task = Task.Run <int>(() => printResult(), cancellationTokenSource.Token); try { task.Wait(cancellationTokenSource.Token); } catch (OperationCanceledException) { var res = task.Result; } postCmd(null, null); return(new CommandResult <(List <FileSystemPath>, FindCounts)>((items, counts))); } return(new CommandResult <(List <FileSystemPath>, FindCounts)>((r, new FindCounts()), ReturnCode.Error)); }
public CommandResult <(List <(FileSystemPath source, FileSystemPath target)> items, FindCounts counts)> Mv( CommandEvaluationContext context, [Parameter("source: file/directory or several corresponding to a wildcarded path")] WildcardFilePath source, [Parameter(1, "destination: a file or a directory")] FileSystemPath dest, [Option("i", "prompt before overwrite")] bool interactive, [Option("v", "explain what is being done")] bool verbose ) { if (source.CheckExists()) { var counts = new FindCounts(); var items = FindItems(context, source.FullName, source.WildCardFileName ?? "*", true, true, false, true, false, null, false, counts, false, false); var sourceCount = items.Count; List <(FileSystemPath src, FileSystemPath tgt)> r = new List <(FileSystemPath src, FileSystemPath tgt)>(); if (sourceCount > 1) { if (dest.CheckExists()) { if (!dest.IsDirectory) { Errorln("dest must be a directory"); return(new CommandResult <(List <(FileSystemPath, FileSystemPath)> items, FindCounts counts)>( (new List <(FileSystemPath, FileSystemPath)> { (source, dest) }, counts), ReturnCode.Error )); } else { // move multiple source to dest foreach (var item in items) { var msg = $"move {item.GetPrintableName()} to {dest.GetPrintableName()}"; if (!interactive || Confirm("mv: " + msg)) { if (source.IsFile) { var newdest = Path.Combine(dest.FullName, item.Name); r.Add((item, new FileSystemPath(newdest))); File.Move(item.FullName, newdest); } else { var newdest = Path.Combine(dest.FullName, item.Name); Directory.Move(item.FullName, newdest); r.Add((item, new DirectoryPath(newdest))); } if (verbose) { context.Out.Echoln(msg.Replace("move ", "moved ")); } } } } } } else { if (dest.CheckExists(false)) { if (dest.IsDirectory) { // move one source to dest var msg = $"move {source.GetPrintableNameWithWlidCard()} to {dest.GetPrintableName()}"; if (!interactive || Confirm("mv: " + msg)) { if (source.IsFile) { var newdest = Path.Combine(dest.FullName, source.NameWithWildcard); File.Move(source.FullNameWithWildcard, newdest); r.Add((new FilePath(source.FullNameWithWildcard), new FilePath(newdest))); } else { var newdest = Path.Combine(dest.FullName, source.NameWithWildcard); Directory.Move(source.FullName, newdest); r.Add((source, new DirectoryPath(newdest))); } if (verbose) { context.Out.Echoln(msg.Replace("move ", "moved ")); } } } else { // rename source (file) to dest (overwrite dest) var msg = $"rename {source.GetPrintableNameWithWlidCard()} to {dest.GetPrintableName()}"; if (!interactive || Confirm("mv: " + msg)) { dest.FileSystemInfo.Delete(); File.Move(source.FullNameWithWildcard, dest.FullName); r.Add((new FilePath(source.FullNameWithWildcard), dest)); if (verbose) { context.Out.Echoln(msg.Replace("rename ", "renamed ")); } } } } else { // rename source to dest var msg = $"rename {source.GetPrintableNameWithWlidCard()} to {dest.GetPrintableName()}"; if (!interactive || Confirm("mv: " + msg)) { if (source.IsFile) { File.Move(source.FullNameWithWildcard, dest.FullName); r.Add((new FilePath(source.FullNameWithWildcard), dest)); } else { Directory.Move(source.FullName, dest.FullName); r.Add((source, dest)); } if (verbose) { context.Out.Echoln(msg.Replace("rename ", "renamed ")); } } } } return(new CommandResult <(List <(FileSystemPath source, FileSystemPath target)> items, FindCounts counts)> ((r, counts))); } else { return(new CommandResult <(List <(FileSystemPath, FileSystemPath)> items, FindCounts counts)> ((new List <(FileSystemPath, FileSystemPath)> { (source, null) }, new FindCounts()))); } }
public CommandResult <(List <FileSystemPath> items, FindCounts counts)> Rm( CommandEvaluationContext context, [Parameter("file or folder path")] WildcardFilePath path, [Option("r", "also remove files and folders in sub directories")] bool recurse, [Option("i", "prompt before any removal")] bool interactive, [Option("v", "explain what is being done")] bool verbose, [Option("d", "remove empty directories")] bool rmEmptyDirs, [Option("na", "do not print file system attributes when verbose")] bool noattributes, [Option("s", "don't remove any file/or folder, just simulate the operation (enable verbose)")] bool simulate ) { var r = new List <FileSystemPath>(); var counts = new FindCounts(); if (path.CheckExists()) { var items = FindItems(context, path.FullName, path.WildCardFileName ?? "*", !recurse, true, false, !noattributes, !recurse, null, false, counts, false, false); var cancellationTokenSource = new CancellationTokenSource(); verbose |= simulate; void cancelCmd(object o, ConsoleCancelEventArgs e) { e.Cancel = true; cancellationTokenSource.Cancel(); }; void postCmd(object o, EventArgs e) { sc.CancelKeyPress -= cancelCmd; } List <FileSystemPath> processRemove() { var r = new List <FileSystemPath>(); foreach (var item in items) { if (cancellationTokenSource.IsCancellationRequested) { return(r); } bool deleted = false; if (item.IsFile) { if (item.FileSystemInfo.Exists && !r.Contains(item)) { if (interactive) { if (Confirm("rm: remove file " + item.GetPrintableName(recurse)) && !simulate) { if (!simulate) { item.FileSystemInfo.Delete(); } deleted = true; } } else { if (!simulate) { item.FileSystemInfo.Delete(); } deleted = true; } } } else { var dp = (DirectoryPath)item; if ((rmEmptyDirs && dp.IsEmpty) || recurse) { if (dp.DirectoryInfo.Exists && !r.Contains(dp)) { if (interactive) { r.Merge(RecurseInteractiveDeleteDir(context, dp, simulate, noattributes, verbose, cancellationTokenSource)); } else { if (!simulate) { dp.DirectoryInfo.Delete(recurse); } deleted = true; } } } } if (deleted) { if (verbose) { item.Print(!noattributes, !recurse, "", Br, -1, "removed "); } r.Add(item); } } return(r); }; sc.CancelKeyPress += cancelCmd; var task = Task.Run(() => processRemove(), cancellationTokenSource.Token); try { task.Wait(cancellationTokenSource.Token); } catch (OperationCanceledException) { r = task.Result; } postCmd(null, null); return(new CommandResult <(List <FileSystemPath>, FindCounts)>((r, counts), ReturnCode.OK)); } else { return(new CommandResult <(List <FileSystemPath>, FindCounts)>((r, counts), ReturnCode.Error)); } }
public CommandResult <List <FilePath> > CheckIntegrity( CommandEvaluationContext context, [Parameter("path of a file to be checked or path from where find files to to be checked")] FileSystemPath fileOrDir, [Option("p", "pattern", "select names that matches the pattern", true, true)] string pattern, [Option("i", "non-sensitive", "if set and p is set, perform a non case sensisitive search")] bool ignoreCase, [Option("a", "attr", "print file system attributes")] bool printAttr, [Option("t", "top", "search in top directory only")] bool top, [Option("q", "quiet", "quiet mode: do not print error message below corrupted file name")] bool quiet, [Option("r", "ratio", "acceptable ratio of non printable characters", true, true)] double ratio = 30, [Option("s", "min-size", "minimum size of analysed part of the text", true, true)] int minSeqLength = 1024 ) { var r = new List <FilePath>(); if (fileOrDir.CheckExists(context)) { if (fileOrDir.IsFile) { var(isValid, filePath) = CheckIntegrity(context, new FilePath(fileOrDir.FullName), ratio, printAttr, minSeqLength, quiet); if (!isValid) { r.Add(filePath); } return(new CommandResult <List <FilePath> >(r)); } else { var sp = string.IsNullOrWhiteSpace(pattern) ? "*" : pattern; var counts = new FindCounts(); var items = FindItems(context, fileOrDir.FullName, sp, top, false, false, printAttr, false, null, false, counts, false, false, ignoreCase); var f = context.ShellEnv.Colors.Default.ToString(); var elapsed = DateTime.Now - counts.BeginDateTime; context.Out.Echoln($"found {context.ShellEnv.Colors.Numeric}{Plur("file", counts.FilesCount, f)} and {context.ShellEnv.Colors.Numeric}{Plur("folder", counts.FoldersCount, f)}. scanned {context.ShellEnv.Colors.Numeric}{Plur("file", counts.ScannedFilesCount, f)} in {context.ShellEnv.Colors.Numeric}{Plur("folder", counts.ScannedFoldersCount, f)} during {TimeSpanDescription(elapsed, context.ShellEnv.Colors.Numeric.ToString(), f)}"); if (items.Count > 0) { context.Out.Echoln($"analyzing files ({counts.FilesCount})..."); int corruptedFilesCount = 0; foreach (var item in items) { if (context.CommandLineProcessor.CancellationTokenSource.Token.IsCancellationRequested) { break; } if (item.IsFile) { if (!CheckIntegrity(context, (FilePath)item, ratio, printAttr, minSeqLength, quiet).isValid) { corruptedFilesCount++; r.Add((FilePath)item); } } } if (corruptedFilesCount > 0) { context.Out.Echoln(); } var crprt = (double)corruptedFilesCount / (double)counts.FilesCount * 100d; context.Out.Echoln($"found {context.ShellEnv.Colors.Numeric}{Plur("corrupted file", corruptedFilesCount, f)} in {context.ShellEnv.Colors.Numeric}{Plur("file", counts.FilesCount, f)} corruption ratio={Cyan}{crprt}%"); return(new CommandResult <List <FilePath> >(r)); } else { return(new CommandResult <List <FilePath> >(r)); } } } else { return(new CommandResult <List <FilePath> >(new List <FilePath> { new FilePath(fileOrDir.FullName) }, ReturnCode.Error)); } }
/// <summary> /// search items in file system /// </summary> /// <param name="context"></param> /// <param name="path"></param> /// <param name="pattern"></param> /// <param name="top"></param> /// <param name="all"></param> /// <param name="dirs"></param> /// <param name="attributes"></param> /// <param name="shortPathes"></param> /// <param name="contains"></param> /// <param name="checkPatternOnFullName"></param> /// <param name="counts"></param> /// <param name="print"></param> /// <param name="alwaysSelectDirs"></param> /// <param name="ignoreCase"></param> /// <param name="printMatches"></param> /// <returns></returns> public static List <FileSystemPath> FindItems( CommandEvaluationContext context, string path, string pattern, bool top, bool all, bool dirs, bool attributes, bool shortPathes, string contains, bool checkPatternOnFullName, FindCounts counts, bool print, bool alwaysSelectDirs = false, bool ignoreCase = false, bool printMatches = false) { bool isFile = File.Exists(path); var dinf = isFile ? null : new DirectoryInfo(path); List <FileSystemPath> items = new List <FileSystemPath>(); bool hasPattern = !string.IsNullOrWhiteSpace(pattern); bool hasContains = !string.IsNullOrWhiteSpace(contains); if (context.CommandLineProcessor.IsCancellationRequested) { return(items); } try { if (!isFile) { counts.ScannedFoldersCount++; } var scan = isFile ? new FileSystemInfo[] { new FileInfo(path) } : dinf.GetFileSystemInfos(); foreach (var fsinf in scan) { var sitem = FileSystemPath.Get(fsinf); if (sitem.IsDirectory) { if ((dirs || all) && (alwaysSelectDirs || (!hasPattern || MatchWildcard(pattern, checkPatternOnFullName ? sitem.FileSystemInfo.FullName : sitem.FileSystemInfo.Name, ignoreCase)))) { items.Add(sitem); if (print) { sitem.Echo(new EchoEvaluationContext(context.Out, context, new FileSystemPathFormattingOptions(attributes, shortPathes, "", Br))); } counts.FoldersCount++; } else { sitem = null; } if (!top) { items.AddRange(FindItems(context, fsinf.FullName, pattern, top, all, dirs, attributes, shortPathes, contains, checkPatternOnFullName, counts, print, alwaysSelectDirs, ignoreCase, printMatches)); } } else { counts.ScannedFilesCount++; if (!dirs && (!hasPattern || MatchWildcard(pattern, checkPatternOnFullName ? sitem.FileSystemInfo.FullName : sitem.FileSystemInfo.Name, ignoreCase))) { var matches = new List <string>(); if (hasContains) { try { if (!FilePath.IsBinaryFile(sitem.FileSystemInfo.FullName)) { // skip non text files var(lines, platform, eol) = TextFileReader.ReadAllLines(sitem.FileSystemInfo.FullName); bool match = false; for (int i = 0; i < lines.Length; i++) { if (lines[i].Contains(contains)) { match |= true; if (printMatches) { int j = lines[i].IndexOf(contains); var loc = $"\t{context.ShellEnv.Colors.MarginText} {$"line {i},col {j}".PadRight(24)} "; var txt = lines[i].Replace(contains, context.ShellEnv.Colors.TextExtractSelectionBlock + contains + Rdc + context.ShellEnv.Colors.TextExtract); matches.Add(loc + context.ShellEnv.Colors.TextExtract + txt + Rdc); } } } if (!match) { sitem = null; } } else { sitem = null; } } catch (Exception ex) { context.Errorln($"file read error: {ex.Message} when accessing file: {sitem.PrintableFullName}"); sitem = null; } } if (sitem != null) { counts.FilesCount++; items.Add(sitem); if (print && context.Out.IsModified && matches.Count > 0) { context.Out.Echoln(""); } if (print) { sitem.Echo(new EchoEvaluationContext(context.Out, context, new FileSystemPathFormattingOptions(attributes, shortPathes, "", Br))); } } if (matches.Count > 0) { matches.ForEach(x => context.Out.Echoln(x)); } matches.Clear(); } else { sitem = null; } } if (context.CommandLineProcessor.IsCancellationRequested) { return(items); } } return(items); } catch (UnauthorizedAccessException) { context.Errorln($"unauthorized access to {new DirectoryPath(path).PrintableFullName}"); return(items); } }
public static List <FileSystemPath> FindItems( CommandEvaluationContext context, string path, string pattern, bool top, bool all, bool dirs, bool attributes, bool shortPathes, string contains, bool checkPatternOnFullName, FindCounts counts, bool print, bool alwaysSelectDirs = false, bool ignoreCase = false) { var dinf = new DirectoryInfo(path); List <FileSystemPath> items = new List <FileSystemPath>(); bool hasPattern = !string.IsNullOrWhiteSpace(pattern); bool hasContains = !string.IsNullOrWhiteSpace(contains); if (context.CommandLineProcessor.CancellationTokenSource.Token.IsCancellationRequested) { return(items); } try { counts.ScannedFoldersCount++; var scan = dinf.GetFileSystemInfos(); foreach (var fsinf in scan) { var sitem = FileSystemPath.Get(fsinf); if (sitem.IsDirectory) { if ((dirs || all) && (alwaysSelectDirs || (!hasPattern || MatchWildcard(pattern, checkPatternOnFullName ? sitem.FileSystemInfo.FullName : sitem.FileSystemInfo.Name, ignoreCase)))) { items.Add(sitem); if (print) { sitem.Print(attributes, shortPathes, "", Br); } counts.FoldersCount++; } else { sitem = null; } if (!top) { items.AddRange(FindItems(context, fsinf.FullName, pattern, top, all, dirs, attributes, shortPathes, contains, checkPatternOnFullName, counts, print, alwaysSelectDirs, ignoreCase)); } } else { counts.ScannedFilesCount++; if (!dirs && (!hasPattern || MatchWildcard(pattern, checkPatternOnFullName ? sitem.FileSystemInfo.FullName : sitem.FileSystemInfo.Name, ignoreCase))) { if (hasContains) { try { var str = File.ReadAllText(sitem.FileSystemInfo.FullName); if (!str.Contains(contains)) { sitem = null; } } catch (Exception ex) { Errorln($"file read error: {ex.Message} when accessing file: {sitem.PrintableFullName}"); } } if (sitem != null) { counts.FilesCount++; items.Add(sitem); if (print) { sitem.Print(attributes, shortPathes, "", Br); } } } else { sitem = null; } } if (context.CommandLineProcessor.CancellationTokenSource.Token.IsCancellationRequested) { return(items); } } return(items); } catch (UnauthorizedAccessException) { Errorln($"unauthorized access to {new DirectoryPath(path).PrintableFullName}"); return(items); } }
public CommandResult <(List <FileSystemPath> items, FindCounts counts)> Dir( CommandEvaluationContext context, [Parameter("path where to list files and folders. if not specified is set to the current directory. use wildcards * and ? to filter files and folders names", true)] WildcardFilePath path, [Option("n", "name", "names only: do not print file system attributes")] bool noattributes, [Option("r", "recurse", "also list files and folders in sub directories. force display files full path")] bool recurse, [Option("w", "wide", "displays file names on several columns so output fills console width (only if not recurse mode). disable print of attributes")] bool wide, [Option("f", "file", "filter list on files (exclude directories). avoid -d")] bool filesOnly, [Option("d", "dir", "filter list on directories (exclude files)")] bool dirsOnly, [Option("s", "sort", "sort list of files and folders. default is alphabetic, mixing files and folders", true, true)] DirSort sort = DirSort.dir | DirSort.name ) { var r = new List <FileSystemPath>(); path ??= new WildcardFilePath(Environment.CurrentDirectory); if (path.CheckExists(context)) { var counts = new FindCounts(); var items = FindItems(context, path.FullName, path.WildCardFileName ?? "*", !recurse, true, false, !noattributes, !recurse, null, false, counts, false, false); if (filesOnly) { dirsOnly = false; } if (filesOnly) { items = items.Where(x => x.IsFile).ToList(); } if (dirsOnly) { items = items.Where(x => x.IsDirectory).ToList(); } // apply sorts - default (.net) is name (==DirSort.name==1) - possible DirSort.not_specified==0 == default sort by name if (sort != DirSort.not_specified && sort != DirSort.name) { IEnumerable <IGrouping <string, FileSystemPath> > grp = null; if (sort.HasFlag(DirSort.ext)) { grp = items.GroupBy((x) => Path.GetExtension(x.FullName)); } else if (sort.HasFlag(DirSort.file)) { grp = items.GroupBy((x) => x.IsDirectory + ""); } else if (sort.HasFlag(DirSort.dir)) { grp = items.GroupBy((x) => x.IsFile + ""); } else if (sort.HasFlag(DirSort.size)) { grp = items.GroupBy((x) => !x.IsFile + ""); } var lst = grp.ToList(); lst.Sort((x, y) => x.Key.CompareTo(y.Key)); // sort alpha if (!sort.HasFlag(DirSort.file) && !sort.HasFlag(DirSort.dir) && !sort.HasFlag(DirSort.size) && sort.HasFlag(DirSort.rev)) { lst.Reverse(); } items.Clear(); foreach (var g in lst) { var glst = g.ToList(); if (!sort.HasFlag(DirSort.size)) { glst.Sort((x, y) => x.FullName.CompareTo(y.FullName)); // sort alpha } else { glst.Sort((x, y) => x.Length.CompareTo(y.Length)); // sort size } if (sort.HasFlag(DirSort.rev)) { glst.Reverse(); } items.AddRange(glst); } } var f = DefaultForegroundCmd; long totFileSize = 0; var cancellationTokenSource = new CancellationTokenSource(); if (wide) { noattributes = true; } // here we self handle the console Cancel event so we can output a partial result even if the seek phasis has been broke. print can also be broke. // TODO: seems there is now a simple way to do this. Just use the context cancellation token (as in findItems) : // TODO: if (context.CommandLineProcessor.CancellationTokenSource.Token.IsCancellationRequested) // then cancel com void postCmd(object o, EventArgs e) { sc.CancelKeyPress -= cancelCmd; context.Out.Echoln($"{Tab}{context.ShellEnv.Colors.Numeric}{Plur("file", counts.FilesCount, f),-30}{HumanFormatOfSize(totFileSize, 2, " ", context.ShellEnv.Colors.Numeric.ToString(), f)}"); context.Out.Echoln($"{Tab}{context.ShellEnv.Colors.Numeric}{Plur("folder", counts.FoldersCount, f),-30}{Drives.GetDriveInfo(path.FileSystemInfo.FullName, false, context.ShellEnv.Colors.Numeric.ToString(), f, " ", 2)}"); } void cancelCmd(object o, ConsoleCancelEventArgs e) { e.Cancel = true; cancellationTokenSource.Cancel(); } int printResult() { var i = 0; int maxitlength = 0; counts.FilesCount = counts.FoldersCount = 0; foreach (var item in items) { if (item.IsFile) { totFileSize += ((FileInfo)item.FileSystemInfo).Length; counts.FilesCount++; } else { counts.FoldersCount++; } maxitlength = Math.Max(item.Name.Length, maxitlength); } maxitlength += 4; var(id, left, top, right, bottom) = DotNetConsole.ActualWorkArea(); var nbcols = Math.Floor((double)(right - left + 1) / (double)maxitlength); int nocol = 0; foreach (var item in items) { if (cancellationTokenSource.IsCancellationRequested) { return(i); } item.Echo( new EchoEvaluationContext( context.Out, context, new FileSystemPathFormattingOptions( !noattributes, !recurse, "", ((!wide || recurse || nocol == nbcols - 1) ? Br : ""), (wide && !recurse) ? maxitlength : -1, ""))); i++; nocol++; if (nocol == nbcols) { nocol = 0; } } if (!recurse && wide && nocol < nbcols && nocol > 0) { context.Out.Echoln(); } return(i); } sc.CancelKeyPress += cancelCmd; var task = Task.Run <int>(() => printResult(), cancellationTokenSource.Token); try { task.Wait(cancellationTokenSource.Token); } catch (OperationCanceledException) { var res = task.Result; } postCmd(null, null); return(new CommandResult <(List <FileSystemPath>, FindCounts)>((items, counts))); } return(new CommandResult <(List <FileSystemPath>, FindCounts)>((r, new FindCounts()), ReturnCode.Error)); }