uniblab

standard UNIX sed ported to C#

Jul 20th, 2026
33,830
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 5.06 KB | None | 0 0
  1. // Original behavior/reference: sed (Lee E. McMahon)
  2. // Ported to .NET by Timothy J. Bruce <[email protected]>
  3.  
  4. namespace Icod.CoreUtils.Sed;
  5.  
  6. using System;
  7. using System.Collections.Generic;
  8. using System.IO;
  9. using System.Text.RegularExpressions;
  10. using System.Text;
  11.  
  12. /// <summary>
  13. /// sed: stream editor (simplified).
  14. /// Supported features:
  15. ///   -n        suppress automatic printing
  16. ///   -e script add script to the commands to run
  17. /// Script forms supported (simple subset):
  18. ///   s/old/new/g   substitute (supports regex) with optional g flag
  19. /// Multiple -e accepted; first script applied if none specified.
  20. /// This is a small, portable subset for common use cases.
  21. /// </summary>
  22. public static class Command
  23. {
  24.     public static int Run(string[] args, TextReader? stdin = null, TextWriter? stdout = null, TextWriter? stderr = null)
  25.     {
  26.         stdin ??= Console.In;
  27.         stdout ??= Console.Out;
  28.         stderr ??= Console.Error;
  29.  
  30.         var scripts = new List<string>();
  31.         var suppress = false;
  32.         var files = new List<string>();
  33.  
  34.         var i = 0;
  35.         for (; i < args.Length; i++)
  36.         {
  37.             var a = args[i];
  38.             if (!a.StartsWith("-"))
  39.             {
  40.                 break;
  41.             }
  42.  
  43.             if (a == "-n")
  44.             {
  45.                 suppress = true;
  46.             }
  47.             else if (a == "-e")
  48.             {
  49.                 if (i + 1 < args.Length)
  50.                 {
  51.                     i++;
  52.                     scripts.Add(args[i]);
  53.                 }
  54.             }
  55.             else
  56.             {
  57.                 // ignore other options
  58.             }
  59.         }
  60.  
  61.         for (; i < args.Length; i++)
  62.         {
  63.             files.Add(args[i]);
  64.         }
  65.  
  66.         if (scripts.Count == 0)
  67.         {
  68.             stderr.WriteLine("sed: no scripts provided");
  69.             return 2;
  70.         }
  71.  
  72.         if (files.Count == 0)
  73.         {
  74.             files.Add("-");
  75.         }
  76.  
  77.         try
  78.         {
  79.             foreach (var path in files)
  80.             {
  81.                 TextReader reader;
  82.                 if (path == "-")
  83.                 {
  84.                     reader = stdin;
  85.                 }
  86.                 else
  87.                 {
  88.                     reader = new StreamReader(path, Encoding.UTF8, detectEncodingFromByteOrderMarks: true);
  89.                 }
  90.  
  91.                 using (reader)
  92.                 {
  93.                     string? line;
  94.                     while ((line = reader.ReadLine()) is not null)
  95.                     {
  96.                         var outLine = line;
  97.                         foreach (var script in scripts)
  98.                         {
  99.                             if (script.StartsWith("s/"))
  100.                             {
  101.                                 // parse s/old/new/flags
  102.                                 var m = Regex.Match(script, @"^s/(?<old>.*?)/(?<new>.*?)/(?<flags>.*)$");
  103.                                 if (!m.Success)
  104.                                 {
  105.                                     m = Regex.Match(script, @"^s/(?<old>.*?)/(?<new>.*)$");
  106.                                 }
  107.  
  108.                                 if (m.Success)
  109.                                 {
  110.                                     var oldPat = m.Groups["old"].Value;
  111.                                     var newText = m.Groups["new"].Value;
  112.                                     var flags = m.Groups["flags"].Success ? m.Groups["flags"].Value : string.Empty;
  113.                                     var regexOptions = RegexOptions.None;
  114.                                     var replaceCount = flags.Contains("g") ? -1 : 1;
  115.                                     outLine = RegexReplace(outLine, oldPat, newText, regexOptions, replaceCount);
  116.                                 }
  117.                             }
  118.                             else
  119.                             {
  120.                                 // unsupported script: ignore
  121.                             }
  122.                         }
  123.  
  124.                         if (!suppress)
  125.                         {
  126.                             stdout.WriteLine(outLine);
  127.                         }
  128.                     }
  129.                 }
  130.             }
  131.  
  132.             return 0;
  133.         }
  134.         catch (Exception ex)
  135.         {
  136.             stderr.WriteLine($"sed: {ex.Message}");
  137.             return 1;
  138.         }
  139.     }
  140.  
  141.     private static string RegexReplace(string input, string pattern, string replacement, RegexOptions options, int maxReplacements)
  142.     {
  143.         try
  144.         {
  145.             if (maxReplacements == -1)
  146.             {
  147.                 return Regex.Replace(input, pattern, replacement, options);
  148.             }
  149.  
  150.             var regex = new Regex(pattern, options);
  151.             var count = 0;
  152.             return regex.Replace(input, m =>
  153.             {
  154.                 count++;
  155.                 if (count <= maxReplacements)
  156.                 {
  157.                     return replacement;
  158.                 }
  159.  
  160.                 return m.Value;
  161.             });
  162.         }
  163.         catch
  164.         {
  165.             return input;
  166.         }
  167.     }
  168. }
  169.  
Advertisement
Add Comment
Please, Sign In to add comment