C# консоль для выполнения простых «скриптов»

Вы когда нибудь выполняли массовое переименовывание файлов, или какую-нибудь другую, кажущуюся банально лёгкой, но рутинную задачу? Ни разу не создавали bat'ники, но знаете C#?

Находясь в похожей ситуации, в течение 15 минут было создано довольно простое приложение которое похоже на консоль, но которое понимает C#.

image



Приложение работает по принципу запоминания кода, и выполнения полученного скрипта. Поддерживается выполнение скрипта из файла, который появляется в директории с приложением при первом выполнении.

Были добавлены базовые команды, которые требуются для создания скрипта через консоль и его выполнения:
!show — показывает запомненный скрипт целиком
!del — удаляет последнюю строку в скрипте
!clear — стирает скрипт
!run — выполнит скрипт и сохранит его содержимое в файл «script.cs»
!runsc — выполнит скрипт из файла «script.cs» (для случая если вы редактируете файл текстовым редактором)

Исходник Program.cs (собственно всего приложения):

using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using Microsoft.CSharp;
 
namespace SharpConsole
{
    class Program
    {
        const string header = @"
using System;
using System.IO;
using System.Net;
using System.Threading;
using System.Collections.Generic;
 
namespace CScript
{
    public class Script
    {
        public static void ScriptMethod()
        {
"
;
        const string footer = @"
        }
    }
}"
;
 
        static void Main(string[] args)
        {
            Console.Title = "SharpConsole";
            Console.WriteLine("Welcome to SharpConsole v0.1. Type !help for help.");
 
            List<string> code = new List<string>();
            while (true)
            {
                Console.Write("csharp> ");
                string line = Console.ReadLine();
                switch (line)
                {
                    case "!run":
                        {
                            string program = FormatSources(code);
                            File.WriteAllText("script.cs", program);
                            ExecuteScript(program);
                        }
                        break;
                    case "!show":
                        {
                            Console.WriteLine();
                            Console.WriteLine(FormatSources(code));
                            Console.WriteLine();
                        }
                        break;
                    case "!del":
                        code.RemoveAt(code.Count - 1);
                        break;
                    case "!clear":
                        {
                            code = new List<string>();
                            Console.WriteLine("Done.");
                        }
                        break;
                    case "!runsc":
                        {
                            string script = File.ReadAllText("script.cs");
                            ExecuteScript(script);
                        }
                        break;
                    case "!help":
                        {
                            string[] commands = new string[]
                            {
                                "1. Type your code and it will be added in script",
                                "2. !show will shows your code",
                                "3. !del remove last line from your code",
                                "4. !clear remove all lines from your code",
                                "5. !run will run script in memory and writes it in script.cs",
                                "6. !runsc will run script.cs",
                                "Have fun! :)",
                            };
                            foreach (var str in commands)
                                Console.WriteLine(str);
                        }
                        break;
                    default:
                        {
                            code.Add("              " + line);
                        }
                        break;
                }
 
 
 
            }
        }
 
        private static void ExecuteScript(string program)
        {
            var CSHarpProvider = CSharpCodeProvider.CreateProvider("CSharp");
            CompilerParameters compilerParams = new CompilerParameters()
            {
                GenerateExecutable = false,
                GenerateInMemory = true,
            };
            compilerParams.ReferencedAssemblies.AddRange(new string[]
            {
                "System.dll",
                "System.Core.dll",
                "System.Net.dll",
            });
            var compilerResult = CSHarpProvider.CompileAssemblyFromSource(compilerParams, program);
            if (compilerResult.Errors.Count == 0)
            {
                Console.WriteLine("Executing...");
                try
                {
                    compilerResult.CompiledAssembly.GetType("CScript.Script").GetMethod("ScriptMethod").Invoke(nullnull);
                    Console.WriteLine("Done.");
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.InnerException.Message + "rn" + e.InnerException.StackTrace);
                }
            }
            else
            {
                foreach (var oline in compilerResult.Output)
                    Console.WriteLine(oline);
            }
        }
 
        private static string FormatSources(List<string> code)
        {
            string program = header;
            StringBuilder sb = new StringBuilder(program);
 
            foreach (var sc in code)
            {
                sb.AppendLine(sc);
            }
            sb.AppendLine(footer);
            return sb.ToString();
        }
    }
}


По умолчанию, при выполнении скрипта, к сборке подключаются 3 референса:

compilerParams.ReferencedAssemblies.AddRange(new string[]
            {
                "System.dll",
                "System.Core.dll",
                "System.Net.dll",
            });


Но вы можете добавить другие для ваших нужд.

Проект VS: скачать

Надеюсь это будет кому-нибудь полезно! Буду рад ответить на вопросы.
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 38

    +12
    А зачем?
      0
      В начале статьи описал случай зачем это может понавдобится.
      Заказчик скинул пачку исходного материалла в качестве картинок с рандомными названиями. Что бы упросить в проекте работу с этим материаллом нужно было переименовать все файлы в определённом формате. Написав простенький foreach скрипт файлы были быстро приведены к нужному виду.
        +16
        Вы про PowerShell никогда не слышали?
          –3
          Слышал, но на тот момент не было времени осваивать, дэдлайн близился.
            +6
            Тем не менее, это не отвечает на вопрос «зачем нужна ваша поделка, если есть PowerShell». Время на его освоение для вашей задачи — меньше, чем время написания вашей же программы.
              0
              Может быть вы и правы, возможно на тот момент мной так же двигал интерес к возможности компиляции и выполнения кода внутри другой программы средствами CSharpCodeProvider. Каюсь :)
                +1
                PowerShell освоить за 15 минут никак не выйдет, но создавать такое приложение тоже не вижу смысла.
                Чем вас не устраивает просто сделать New->Project->Console Application?
                Результат тот же будет и не надо в консоли «мудохаться» — пишите как обычно с интелисенсом.
                За изобретение ставлю пять, а по предмету неуд (с)
                  0
                  Кому что быстрее. Мне было быстрее освоить PS — еще и потому, что это знание пригодилось впоследствии неоднократно.

                  Все по Pragmatic Programmer.
            0
            не, ну круто, но просто неужели у вас там уникальный какой-то формат, что простым Total Commander не переименовать?
            Просто интересно…
              0
              Да нет, почему, обычные картинки :) но вот TC я особо никогда не пользовался.
                0
                нет, я имел в виду шаблон, которым вы имя файлу задаёте.
                А если вы не знали об этом, тогда вот хэлп.
                  0
                  Шаблон простейший, просто числа проставить и всё. А по поводу ТС теперь буду в курсе. :)
            +1
            REPL же. Правда это не имеет отношения к задаче топикстартера.
              0
              REPL на C#? Я так и не проникся.
                0
                А почему нет?
                  0
                  Ну не проникся я, мне неудобно.
                    0
                    Не, ну я согласен, что C# не сильно по синтаксису удобен для REPL и что лучше юзать F#.
                    Но кучу сценариев использования я придумать могу.
                0
                Да, про него уже писали коментом ниже :) habrahabr.ru/post/170385/#comment_5910807
                  0
                  Там про Mono.
              +2
                +2
                Кстати, у того же mono есть C# REPL, довольно удобно.

                $ csharp
                Mono C# Shell, type "help;" for help
                 
                Enter statements below.
                csharp> using System.IO;
                csharp> from f in Directory.GetFiles("/etc")
                      >   let fi = new FileInfo(f)  
                      >   where fi.LastWriteTime > DateTime.Now - TimeSpan.FromDays(7) select f; 
                { "/etc/adjtime", "/etc/asound.state", "/etc/mtab", "/etc/printcap", "/etc/resolv.conf" }
                csharp>
                
                  0
                  Да, действительно интересная вещь :) Не знал. Спасибо.
                  +5
                  А как же Roslyn?
                    0
                    А зачем нужно городить весь этот огород, когда есть LinqPad?
                    • UFO just landed and posted this here
                        +3
                        Простое решение простой задачки. Зачем так придираться к человеку, новичкам будет полезно.
                        Кстати, вполне себе читабельный код в отличие от многих поделий на Хабре.
                        Только я бы еще отделил методы и логически завершенные блоки (типа case/break) друг от друга переносом строки, для большей читаемости и, например, этот метод я бы написал так:

                        private static string FormatSources(List<string> code)
                        {
                            StringBuilder program = new StringBuilder(header);
                        
                            foreach (var sc in code)
                            {
                                program.AppendLine(sc);
                            }
                        
                            program.AppendLine(footer);
                            return program.ToString();
                        }
                        


                        Возможно, повторюсь, но это снова тот топик, комменты к которому увеличивают его ценность… Жаль, плюсовалка сломалась ((

                        Leave Divere alone!.. T~T
                          +2
                          PowerShell же!
                          +1
                          Изобрели Sinclair BASIC, только C? Не хватает LOAD, SAVE и номеров строк ))
                            0
                            Я просто оставлю это здесь: forum.antichat.ru/nextoldesttothread242308.html
                            (с) Мопед не мой.
                              0
                              Ну вариантов решения подобной задачи множество :)
                              –6
                              Обожаю windows, но когда смотрю на windows кодеров мне, почему-то, становится их жалко…
                                0
                                Аргументируйте пожалуйста.
                                  –2
                                  Программиста по *nix, к примеру, все до одного для этих целей могут использовать bash и его аналоги.
                                  Под словом могут я понимаю еще и знания.
                                  Под windows есть cmd и PowerShell, но первым крайне неудобно пользоваться. а второй никто не знает.

                                  А вообще Ваш вопрос крайне глупо сформулирован. Как я могу аргументировать свои чувства? Этим занимаются на приеме у соответствующего мед работника…
                                    0
                                    bash тоже есть под windows. и python. и perl.

                                    когда мне последний раз приходилось работать под windows — ставил себе cygwin, python, xming.
                                      0
                                      Под windows есть powershell, через который можно с .net работать
                                      0
                                      Привет вам от тех, кто никто не знает.
                                  0
                                  У вас получился C# repl, думаю, примерно с этого в свое время начиналась идея PowerShell. :)

                                  Only users with full accounts can post comments. Log in, please.