2

I want to open a file in predefined text editor (like notepad for Windows or vim for Linux) using .NET Core. Basically that's my code:

var process = new Process();
process.StartInfo.FileName = file;
process.StartInfo.Arguments = argument;
process.StartInfo.UseShellExecute = true;
process.Start();

On Windows everything goes fine, I can specify editor as FileName and file which I want to open as Arguments.

But on Linux I can only open file by default if I set its name as FileName. If I set vi/vim/less as FileName and file as Arguments, nothing happens. (Though they are installed in the system and I can do the same from terminal)

How can I open the file in a predefined program in a cross-platform way or just so it works on Linux (since I have the working code for Windows)?

8
  • This might be related to your question. Imho there is no simple way, you need to handle it per OS. Commented Feb 13, 2023 at 7:17
  • @Eldar Platform specific was is fine, what I want to know how to handle it for Linux Commented Feb 13, 2023 at 8:10
  • The link includes the method of how you handle it in Linux. You need to start xdg-open process. VS code handles it like this. Commented Feb 13, 2023 at 8:12
  • @Eldar Not sure how this helps. I don't need to open with a default app, I want to specify what app should be used to open the file Commented Feb 13, 2023 at 11:27
  • Have you tried setting UseShellExecute to false? So FileName="vim"; Arguments = your_file; UseShellExecute = false Commented Feb 13, 2023 at 11:56

2 Answers 2

0

You can simply run a terminal instance, and then open the file via commandline

using System.Diagnostics;

var process = new Process();
const string program = "nano";
const string file = "test.txt";
if (OperatingSystem.IsWindows())
{
    process.StartInfo.FileName = program;
    process.StartInfo.Arguments = file;
}
else if (OperatingSystem.IsLinux())
{
    process.StartInfo.FileName = "/bin/bash";
    process.StartInfo.Arguments = $"-c \"{program} {file}\"";
}

process.Start();
Console.ReadLine();

I tried and tested this on Manjaro Linux, and it seems to work fine for me.

(Note starting it in a terminal editor such as vim seemed to cause issues for me since the program would open it and then immediately stop the program, to fix this I had to add the Console.ReadLine() so it doesn't immediately close)

Sign up to request clarification or add additional context in comments.

Comments

0

My solution explained.

1. Try to find the command, checking for the existence of the file using all paths stored in the "path" Environment variable.

The function "CommandExists" it returns command if command file exists, or empty.

Thanks to this autors Taking inspiration from this post

e.g. CommandExists("notepad") it returns "C:\Windows\system32\notepad.exe".

2. Start process with command and arguments.

if command is command line app. Process needs to be waited for finish.

3. Text editor is opened with file content.


Code

You can check/test/clone this code from Github https://github.com/JomaStackOverflowAnswers/RunDefaultEditor

Main program

using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Runtime.InteropServices;

Console.WriteLine("███ Opening file with text Editor");
string editor = string.Empty;
string parameters = string.Empty;
while (!editor.Equals("exit!"))
{
    Console.WriteLine(@"Type the editor command/filename OR exit [exit]:");
    editor = Console.ReadLine() ?? string.Empty;
    if (editor.Equals("exit"))
    {
        break;
    }
    Console.WriteLine(@"Type [True] OR [False] to set if the editor is command line app:");
    _ = bool.TryParse(Console.ReadLine(), out bool wait);
    Console.WriteLine(@"Type additional parameters:");
    parameters = Console.ReadLine() ?? string.Empty;

    await EditorLauncher.LaunchAsync(new EditorInfo { Command = editor, IsCommandLineApp = wait, Parameters = parameters }, "File.txt", verbose: true);
}
//Try to Launch multiple editors.
//await EditorLauncher.LaunchAsync(EditorLauncher.NotepadEditor, "File.txt", true);
//await EditorLauncher.LaunchAsync(EditorLauncher.VisualStudioEditorWindows, "File.txt", true);
//await EditorLauncher.LaunchAsync(EditorLauncher.NotepadPlusPlusEditor, "File.txt", true);
//await EditorLauncher.LaunchAsync(EditorLauncher.SublimeTextEditorWindows, "File.txt", true);
//await EditorLauncher.LaunchAsync(EditorLauncher.NotepadPlusPlusEditorFullPath, "File.txt", true);
//await EditorLauncher.LaunchAsync(EditorLauncher.SublimeTextEditorWindowsFullPath, "File.txt", true);
//await EditorLauncher.LaunchAsync(EditorLauncher.CodeEditor, "File.txt", true);
//await EditorLauncher.LaunchAsync(EditorLauncher.SublimeTextEditorUnix, "File.txt", true);
//await EditorLauncher.LaunchAsync(EditorLauncher.NanoEditor, "File.txt", true);
//await EditorLauncher.LaunchAsync(EditorLauncher.VimEditor, "File.txt", true);
//await EditorLauncher.LaunchAsync(EditorLauncher.ViEditor, "File.txt", true);
//await EditorLauncher.LaunchAsync(EditorLauncher.OpenDefaultEditorMacOS, "File.txt", true);
//await EditorLauncher.LaunchAsync(EditorLauncher.OpenTextEditEditorMacOS, "File.txt", true);
//await EditorLauncher.LaunchAsync(new EditorInfo { Command ="gedit", WaitForExit = false, Parameters = "-s"}, "File.txt", true);

EditorInfo class

public class EditorInfo
{
    public string Command { get; init; } = null!;
    public bool IsCommandLineApp { get; init; } = false;
    public string Parameters { get; set; } = string.Empty;
}

EditorLauncher class

public class EditorLauncher
{
    public static readonly EditorInfo NotepadEditor = new() { Command = "notepad" };
    public static readonly EditorInfo VisualStudioEditorWindows = new() { Command = "devenv.exe" };
    public static readonly EditorInfo NotepadPlusPlusEditor = new() { Command = "notepad++.exe" };
    public static readonly EditorInfo SublimeTextEditorWindows = new() { Command = "subl.exe" };
    public static readonly EditorInfo NotepadPlusPlusEditorFullPath = new() { Command = "C:/Program Files/Notepad++/notepad++.exe" };
    public static readonly EditorInfo SublimeTextEditorWindowsFullPath = new() { Command = "C:/Program Files/Sublime Text/sublime_text.exe" };
    public static readonly EditorInfo CodeEditor = new() { Command = "code" };
    public static readonly EditorInfo SublimeTextEditorUnix = new() { Command = "subl" };
    public static readonly EditorInfo NanoEditor = new() { Command = "nano", IsCommandLineApp = true };
    public static readonly EditorInfo VimEditor = new() { Command = "vim", IsCommandLineApp = true };
    public static readonly EditorInfo ViEditor = new() { Command = "vi", IsCommandLineApp = true };
    public static readonly EditorInfo OpenDefaultEditorMacOS = new() { Command = "open", IsCommandLineApp = true, Parameters = @"-t" };
    public static readonly EditorInfo OpenTextEditEditorMacOS = new() { Command = "open", IsCommandLineApp = true, Parameters = @"-e" };

    private static readonly ReadOnlyDictionary<string, EditorInfo> WindowsEditors = new ReadOnlyDictionary<string, EditorInfo>(
        new Dictionary<string, EditorInfo>
        {
            [NotepadEditor.Command] = NotepadEditor,
            [CodeEditor.Command] = CodeEditor,
            [NotepadPlusPlusEditor.Command] = NotepadPlusPlusEditor,
            [SublimeTextEditorWindows.Command] = SublimeTextEditorWindows,
            [NotepadPlusPlusEditorFullPath.Command] = NotepadPlusPlusEditor,
            [SublimeTextEditorWindowsFullPath.Command] = SublimeTextEditorWindows,
            [VisualStudioEditorWindows.Command] = VisualStudioEditorWindows,
            [VimEditor.Command] = VimEditor
        });

    private static readonly ReadOnlyDictionary<string, EditorInfo> LinuxEditors = new ReadOnlyDictionary<string, EditorInfo>(
        new Dictionary<string, EditorInfo>
        {
            [nameof(NanoEditor)] = NanoEditor,
            [nameof(VimEditor)] = VimEditor,
            [nameof(ViEditor)] = ViEditor,
            [nameof(CodeEditor)] = CodeEditor,
            [nameof(SublimeTextEditorUnix)] = SublimeTextEditorUnix
        });

    private static readonly ReadOnlyDictionary<string, EditorInfo> MacOSEditors = new ReadOnlyDictionary<string, EditorInfo>(
        new Dictionary<string, EditorInfo>
        {
            [nameof(OpenDefaultEditorMacOS)] = OpenDefaultEditorMacOS,
            [nameof(OpenTextEditEditorMacOS)] = OpenTextEditEditorMacOS,
            [nameof(NanoEditor)] = NanoEditor,
            [nameof(VimEditor)] = VimEditor,
            [nameof(ViEditor)] = ViEditor,
            [nameof(CodeEditor)] = CodeEditor,
            [nameof(SublimeTextEditorUnix)] = SublimeTextEditorUnix
        });

    private static async Task OpenProcessAsync(EditorInfo editor, string textFilename, bool verbose)
    {
        if (!string.IsNullOrWhiteSpace(editor.Command))
        {
            if (verbose)
            {
                Console.WriteLine($@"Opening ""{textFilename}"" with ""{editor.Command}"" {(editor.IsCommandLineApp? "command line " : string.Empty)}editor.");
            }
            var process = new Process();
            process.StartInfo.UseShellExecute = true;
            process.StartInfo.FileName = editor.Command;
            process.StartInfo.Arguments = @$"{editor.Parameters} ""{textFilename}""";
            process.Start();
            if (editor.IsCommandLineApp)
            {
                await process.WaitForExitAsync();
            }
            return;
        }
        Console.WriteLine(@$"Invalid command.");
    }

    /// <summary>
    /// Launchs the specified editor. Using Process class, to start new process of PowerShell(pwsh) with the script as parameter and the script required parameters. If editor specified is not found, it try to launch the default.
    /// </summary>
    /// <param name="textFilename">Path of text file.</param>
    /// <param name="editor">The editor command.</param>
    /// <param name="tryVsCode">If Visual Studio Code is available. The file is opened with VSCode. Editor value is ignored.</param>
    /// <param name="verbose">Prints parameters.</param>
    public static async Task LaunchAsync(EditorInfo editor, string textFilename, bool tryVsCode = false, bool verbose = false)
    {
        string command;
        if (tryVsCode)
        {
            command = CommandExists(CodeEditor.Command);
            if (!string.IsNullOrWhiteSpace(command))
            {
                await OpenProcessAsync(CodeEditor, textFilename, verbose);
                return;
            }
        }

        command = CommandExists(editor.Command);
        if (!string.IsNullOrWhiteSpace(command))
        {
            await OpenProcessAsync(editor, textFilename, verbose);
            return;
        }
        string message = @$"No editor was found for file ""{textFilename}"".";
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
        {
            foreach (var cmd in WindowsEditors)
            {
                command = CommandExists(cmd.Value.Command);
                if (!string.IsNullOrWhiteSpace(command))
                {
                    await OpenProcessAsync(cmd.Value, textFilename, verbose);
                    return;
                }
            }
            Console.WriteLine(message);
        }

        if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
        {
            foreach (var cmd in LinuxEditors)
            {
                command = CommandExists(cmd.Value.Command);
                if (!string.IsNullOrWhiteSpace(command))
                {
                    await OpenProcessAsync(cmd.Value, textFilename, verbose);
                    return;
                }
            }
            Console.WriteLine(message);
        }

        if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
        {
            foreach (var cmd in MacOSEditors)
            {
                command = CommandExists(cmd.Value.Command);
                if (!string.IsNullOrWhiteSpace(command))
                {
                    await OpenProcessAsync(cmd.Value, textFilename, verbose);
                    return;
                }
            }
            Console.WriteLine(message);
        }
        throw new NotSupportedException(RuntimeInformation.OSDescription);
    }


    private static string CommandExists(string filename)
    {
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
        {
            var paths = new[] { Environment.CurrentDirectory }
                    .Concat(Environment.GetEnvironmentVariable("PATH")!.Split(';'))
                    .Where(p => !string.IsNullOrWhiteSpace(p));

            var extensions = new[] { String.Empty }
                    .Concat(Environment.GetEnvironmentVariable("PATHEXT")!.Split(';')
                               .Where(e => e.StartsWith(".")));

            var combinations = paths.SelectMany(x => extensions,
                    (path, extension) => Path.Combine(path, filename + extension));
            return combinations.FirstOrDefault(File.Exists) ?? string.Empty;
        }
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
        {
            var paths = new[] { Environment.CurrentDirectory }
                    .Concat(Environment.GetEnvironmentVariable("PATH")!.Split(':'))
                    .Where(p => !string.IsNullOrWhiteSpace(p)).Select(p => Path.Combine(p, filename));
            return paths.FirstOrDefault(File.Exists) ?? string.Empty;
        }

        if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
        {
            var paths = new[] { Environment.CurrentDirectory }
                    .Concat(Environment.GetEnvironmentVariable("PATH")!.Split(':'))
                    .Where(p => !string.IsNullOrWhiteSpace(p)).Select(p => Path.Combine(p, filename));
            return paths.FirstOrDefault(File.Exists) ?? string.Empty;
        }
        throw new NotSupportedException(RuntimeInformation.OSDescription);
    }

}


Screenshots

Windows

All ok. IsCommandLineApp = true, for vim editor.

Windows

Windows

Windows

MacOS

All ok. IsCommandLineApp = true, for vim, vi, nano editors.

MacOS

MacOS

MacOS

MacOS

MacOS

Linux - Ubuntu - WSL

All ok. IsCommandLineApp = true for command line apps (vi, vim, nano).

It need to be tested in real Linux OS.

Ubuntu WSL

Ubuntu WSL

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.