Как перенаправить stdout проекта С# в файл с использованием аргументов командной строки Visual Studio - программирование
Подтвердить что ты не робот

Как перенаправить stdout проекта С# в файл с использованием аргументов командной строки Visual Studio

Я пытаюсь перенаправить вывод программы С# в файл. При использовании "cmd.exe" я могу просто запустить его с помощью myprogram.exe arg1 arg2 > out.txt, но я хотел бы сделать то же самое с помощью параметров запуска Visual Studio.

Я создал проект С# Empty и добавил этот код:

using System;
class Test
{
    public static void Main(string[] args)
    {
        foreach (var arg in args) Console.WriteLine(arg);
    }
}

Затем я редактировал аргументы командной строки в настройках проекта: Project Properties

Запуск проекта с помощью Ctrl + F5 не работает должным образом. Я получаю аргументы командной строки, напечатанные в консоли, а не в выходном файле:

arg1
arg2
>
output.txt

Если я изменил аргументы командной строки на: arg1 arg2 "> output.txt", я получаю следующий вывод:

arg1
arg2
^> output.txt

Я заметил, что в папке "Выход" создается пустой файл output.txt.

Можно ли это выполнить, или я вынужден продолжать использовать cmd.exe для запуска моей программы?

4b9b3361

Ответ 1

В строгом смысле, вы вынуждены использовать командную строку для запуска программы с перенаправленным выходом. В противном случае вам нужно будет самостоятельно проанализировать командную строку, оболочка графического интерфейса может не сделать этого.

Если вы просто хотите перенаправить вывод, когда вы Start Debugging, затем снимите флажок Enable the Visual Studio hosting process, все готово.

Если вы этого не сделали, и "output.txt", который вы видели там, на самом деле, это не, сгенерированное вашим приложением, но "YourApplication.vshost.exe", которое порождается до того, как вы начали отлаживать, Visual Studio IDE. Содержимое всегда будет пустым и не может быть написано; потому что он заблокирован Хостинг.

fo1e8.png

Однако, если вы хотите, чтобы приложение работало так же, как и в том режиме, в котором вы его запускаете, все сложнее.

Когда вы начинаете отладку с приложением, он начинается с:

"YourApplication.exe" arg1 arg2

потому что выход уже перенаправлен IDE.

И когда вы Start Without Debugging, он начинался с:

"% comspec%" /c "YourApplication.exe" arg1 arg2 ^ > output.txt и приостановка"

Это правильный способ предоставить вашему приложению все аргументы, которые вы указали.

Возможно, вам стоит взглянуть на мой предыдущий ответ Как определить, если "Нажмите любую клавишу, чтобы продолжить.,." будет отображаться?.

Здесь я использую такой подход, как атавистический возврат в коде ниже:

  • Код приложения

    using System.Diagnostics;
    using System.Linq;
    using System;
    
    class Test {
        public static void Main(string[] args) {
            foreach(var arg in args)
                Console.WriteLine(arg);
        }
    
        static Test() {
            var current=Process.GetCurrentProcess();
            var parent=current.GetParentProcess();
            var grand=parent.GetParentProcess();
    
            if(null==grand
                ||grand.MainModule.FileName!=current.MainModule.FileName)
                using(var child=Process.Start(
                    new ProcessStartInfo {
                        FileName=Environment.GetEnvironmentVariable("comspec"),
                        Arguments="/c\x20"+Environment.CommandLine,
                        RedirectStandardOutput=true,
                        UseShellExecute=false
                    })) {
                    Console.Write(child.StandardOutput.ReadToEnd());
                    child.WaitForExit();
                    Environment.Exit(child.ExitCode);
                }
    #if false // change to true if child process debugging is needed 
            else {
                if(!Debugger.IsAttached)
                    Debugger.Launch();
    
                Main(Environment.GetCommandLineArgs().Skip(1).ToArray());
                current.Kill(); // or Environment.Exit(0); 
            }
    #endif
        }
    }
    

Нам также нужен следующий код, чтобы он мог работать:

  • Код методов расширения

    using System.Management; // add reference is required
    
    using System.Runtime.InteropServices;
    using System.Diagnostics;
    
    using System.Collections.Generic;
    using System.Linq;
    using System;
    
    public static partial class NativeMethods {
        [DllImport("kernel32.dll")]
        public static extern bool TerminateThread(
            IntPtr hThread, uint dwExitCode);
    
        [DllImport("kernel32.dll")]
        public static extern IntPtr OpenThread(
            uint dwDesiredAccess, bool bInheritHandle, uint dwThreadId);
    }
    
    public static partial class ProcessThreadExtensions /* public methods */ {
        public static void Abort(this ProcessThread t) {
            NativeMethods.TerminateThread(
                NativeMethods.OpenThread(1, false, (uint)t.Id), 1);
        }
    
        public static IEnumerable<Process> GetChildProcesses(this Process p) {
            return p.GetProcesses(1);
        }
    
        public static Process GetParentProcess(this Process p) {
            return p.GetProcesses(-1).SingleOrDefault();
        }
    }
    
    partial class ProcessThreadExtensions /* non-public methods */ {
        static IEnumerable<Process> GetProcesses(
            this Process p, int direction) {
            return
                from format in new[] { 
                    "select {0} from Win32_Process where {1}" }
                let selectName=direction<0?"ParentProcessId":"ProcessId"
                let filterName=direction<0?"ProcessId":"ParentProcessId"
                let filter=String.Format("{0} = {1}", p.Id, filterName)
                let query=String.Format(format, selectName, filter)
                let searcher=new ManagementObjectSearcher("root\\CIMV2", query)
                from ManagementObject x in searcher.Get()
                let process=
                    ProcessThreadExtensions.GetProcessById(x[selectName])
                where null!=process
                select process;
        }
    
        // not a good practice to use generics like this; 
        // but for the convenience .. 
        static Process GetProcessById<T>(T processId) {
            try {
                var id=(int)Convert.ChangeType(processId, typeof(int));
                return Process.GetProcessById(id);
            }
            catch(ArgumentException) {
                return default(Process);
            }
        }
    }
    

Поскольку родительский элемент был бы IDE Visual Studio (в настоящее время называется "devenv") при отладке. Родительский и дедушканый процесс на самом деле различны, и нам нужно правило, чтобы выполнить некоторую проверку.

Сложная часть заключается в том, что внук действительно попадает в Main. Код проверяет процесс дедушки и бабушки каждый раз, когда он запускается. Если бабушка и дедушка были null, тогда она появляется, но порожденный процесс будет %comspec%, который также является родителем нового процесса, который он начнет с того же исполняемого файла текущего. Таким образом, если бабушка и дедушка совпадают с самим собой, то она не будет продолжать появляться, просто пробегает Main.

Static Constructor используется в коде, который запускается до Main. Ответ на вопрос SO: Как работает статический конструктор?.

Когда мы начинаем отладку, мы отлаживаем процесс дедушки и бабушки (который появляется). Для отладки с процессом внука я сделал Debugger.Launch с условной компиляцией, которая будет вызывать Main для сохранения Main clear.

Ответ на вопрос об отладчике также будет полезен: Прикрепить отладчик в С# к другому процессу.

Ответ 2

Я не уверен, что это можно сделать в Visual Studio. Моим решением было бы установить новый вывод для консоли. Этот пример из MSDN:

Console.WriteLine("Hello World");
FileStream fs = new FileStream("Test.txt", FileMode.Create);
// First, save the standard output.
TextWriter tmp = Console.Out;
StreamWriter sw = new StreamWriter(fs);
Console.SetOut(sw);
Console.WriteLine("Hello file");
Console.SetOut(tmp);
Console.WriteLine("Hello World");
sw.Close();

http://msdn.microsoft.com/en-us/library/system.console.setout.aspx

Ответ 3

В разделе "Параметры запуска" измените текстовое поле аргументов командной строки таким образом

args1 args2 1>output.txt

Это перенаправляет стандартный вывод (1), создавая файл с именем output.txt
Если вы хотите добавить к предыдущей версии файла запись

args1 args2 1>>output.txt

Теперь вы можете отлаживать программу "Шаг за шагом", когда удаленная консоль перенаправляется

Ответ 4

Вы можете открыть .csproj.user во внешнем редакторе и изменить StartArguments на:

<StartArguments>arg1 arg2 &gt; output.txt</StartArguments>

к

<StartArguments>arg1 arg2 > output.txt</StartArguments>

Ответ 5

вам нужно будет продолжать использовать cmd.exe, если вы хотите передать свой вывод в файл. аргументы командной строки: для аргументов командной строки, поэтому все, что вы пытаетесь, будет экранировано, чтобы быть аргументом командной строки. Трубы и перенаправитель не являются аргументами командной строки, поэтому они ускользают.

Я бы просто создал .bat файл, который вызывает программу, как мне нравится. Вы можете связать это с панелью задач и просто запустить.

Ответ 6

Я бы предложил лучший способ без необходимости писать какой-либо код!

The start up settings

Просто создайте визуальную студию для запуска своей программы в качестве внешней программы.