Wątek przeniesiony 2023-03-16 11:57 z Hardware/Software przez Riddle.

Wieloplatformowe wykrywanie ścieżek plików w konsoli

1

Chciałbym opracować uniwersalny algorytm pobierania nazwy pliku z konsoli, czyli działający prawidłowo w Windows, MacOS i Linux (dowolna wersja i dowolna dystrybucja).

Założenie: Jest program pracujący w konsoli, w którym należy podać nazwę pliku. W momencie, jak program czeka na nazwę, użytkownik bierze ikonę pliku i upuszcza na konsolę, wpisuje się ścieżka i nazwa, po czym naciska Enter. Sama konsola interpretuje tę czynność jako wpisanie całej nazwy z klawiatury. Wobec tego, zbieramy naciśnięcia wszystkich klawiszy od chwili rozpoczęcia oczekiwania na plik, do naciśnięcia Enter. Otrzymaną nazwę zapisujemy jako napis (string).

Wszystko pięknie i ładnie, ale w praktyce, w Windows 10 i Ubuntu Linux dopisywane są dodatkowe znaki, które uniemożliwiają wykorzystanie tak podanej nazwy pliku, jak jest, trzeba ją odpowiednio przygotować.

Ja zauważyłem takie prawidłowości:

  1. W Windows, jeżeli nazwa pliku lub któryś folder po drodze zawiera spację, to dopisuje się podwójny cudzysłów. Przypadek z cudzysłowem w nazwie nie jest możliwy, gdyż Windows nie pozwala na podwójny cudzysłów w nazwie. Jeżeli nie ma spacji, to nic się nie dopisuje.

  2. W Ubuntu Linux, jeżeli nazwa zawiera spacje lub pojedynczy cudzysłów, nazwa jest obejmowana w pojedynczy cudzysłów, ale też na końcu dopisywana jest spacja. Ponadto, jeżeli w nazwie występuje pojedynczy cudzysłów, to każde wystąpienie ' jest zamieniane na '\''.

Jak jest w przypadku MacOS X? Jak jest w przypadku innych dystrybucji Linuxa?

Chodzi o to, żebym zrobił taki algorytm, który z tekstu wpisanego do konsoli poprzez przeciągnięcie ikony zrobił poprawną nazwę pliku ze ścieżką. Czy powyższe założenia i algorytm jest kompletny, czy coś jeszcze muszę uwzględnić, bo np. w MacOS jest jeszcze inaczej, a ja nie mam możliwości sprawdzić? Sam algorytm byłby jeden dla każdego OS.

Póki co, sam wymyśliłem taką funkcję przygotowującą nazwę pliku w C#, choć w tym przypadku język programowania nie ma większego znaczenia, chodzi o sam algorytm (nie wykluczone, że kiedyś będę chciał to samo zrobić w C++ lub w Java, przerobić na inny język to żaden problem).

public static string PrepareFileName(string NewFile_)
{
    // Lista bialych znakow
    List<char> Whitespaces = new List<char>();
    Whitespaces.Add((char)0x20);
    Whitespaces.Add((char)0xA0);
    Whitespaces.Add((char)0x0D);
    Whitespaces.Add((char)0x0A);
    Whitespaces.Add((char)0x09);

    // Obcinanie bialych znakow z poczatku
    while ((NewFile_.Length > 0) && (Whitespaces.Contains(NewFile_[0])))
    {
        NewFile_ = NewFile_.Substring(1);
    }

    // Obcinanie bialych znakow z konca
    while ((NewFile_.Length > 0) && (Whitespaces.Contains(NewFile_[NewFile_.Length - 1])))
    {
        NewFile_ = NewFile_.Substring(0, NewFile_.Length - 1);
    }

    if (NewFile_.Length >= 2)
    {
        // Nazwa pliku w podwojnym cudzyslowie - nalezy obciac cudzyslow, nic wiecej
        if ((NewFile_[0] == '\"') && (NewFile_[NewFile_.Length - 1] == '\"'))
        {
            NewFile_ = NewFile_.Substring(1, NewFile_.Length - 2);
            return NewFile_;
        }

        // Nazwa w pojedynczym cudzyslowie, nalezy obciac cudzyslow,
        // ale oprocz tego zamienic "escape characters" na odpowiednie znaki
        if ((NewFile_[0] == '\'') && (NewFile_[NewFile_.Length - 1] == '\''))
        {
            // Obcinanie cudzyslowu
            NewFile_ = NewFile_.Substring(1, NewFile_.Length - 2);

            // Interpretowanie "escape characters" i zamiana na odpowiednie znaki
            string NewFile_0 = "";
            bool InsideESC = false;
            for (int i = 0; i < NewFile_.Length; i++)
            {
                if (NewFile_[i] == '\'')
                {
                    InsideESC = !InsideESC;
                }
                else
                {
                    if (InsideESC && (NewFile_[i] == '\\'))
                    {
                        i++;
                        switch (NewFile_[i])
                        {
                            case '\'':
                            case '\"':
                            case '\\':
                                NewFile_0 = NewFile_0 + NewFile_[i];
                                break;
                        }
                    }
                    else
                    {
                        NewFile_0 = NewFile_0 + NewFile_[i];
                    }
                }
            }

            return NewFile_0;
        }
    }
    return NewFile_;
}

Drugie pytanie: W Windows 10 i 11, jak konsola uruchomiona jest jako administrator, to przeciąganie pliku na konsolę nie działa. Jak to umożliwić?

2

Obcinanie białych znaków możesz zamienić na .Trim();

2
andrzejlisek napisał(a):

Drugie pytanie: W Windows 10 i 11, jak konsola uruchomiona jest jako administrator, to przeciąganie pliku na konsolę nie działa. Jak to umożliwić?

Nie da się, procesy odpalone jako zwykły user nie mogą prowadzić interakcji z procesami odpalonymi jako admin - nie przechodzą żadne komunikaty. Możesz przeciągać z innych programów odpalonych jako admin.

0

W międzyczasie miałem możliwość sprawdzenia wpisywania nazwa w terminalu Cygwin64 i tam możliwy jest jeszcze inny przypadek: Nazwa jest w pojedycznym cudzyszłowie, dopisany $ na początku, a znak ' w nazwie zamieniony na \'. W swoim kodzie dodałem obsługę takiego przypadku.

Wrzucam też przypadki testowe. Prosze o informację, czy mozliwe sa jeszcze inne przypadki, szczególnie, jeśli chodzi o MacOS X i różne dystrybucje Linux.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;

namespace TestThr2
{
    class MainClass
    {
        public static string PrepareFileName(string NewFile_)
        {
            // Obcinanie bialych znakow
            NewFile_ = NewFile_.Trim();

            if (NewFile_.Length >= 2)
            {
                // Nazwa pliku w podwojnym cudzyslowie - nalezy obciac cudzyslow, nic wiecej
                if ((NewFile_[0] == '\"') && (NewFile_[NewFile_.Length - 1] == '\"'))
                {
                    NewFile_ = NewFile_.Substring(1, NewFile_.Length - 2);
                    return NewFile_;
                }

                // Nazwa w pojedynczym cudzyslowie, nalezy obciac cudzyslow,
                // ale oprocz tego zamienic "escape characters" na cudzyslow
                if (((NewFile_[0] == '\'') || (NewFile_.StartsWith("$'"))) && (NewFile_[NewFile_.Length - 1] == '\''))
                {
                    bool InsideESC = false;

                    if (NewFile_[0] == '$')
                    {
                        // Obciecie dolara i cudzyslowu
                        InsideESC = true;
                        NewFile_ = NewFile_.Substring(2, NewFile_.Length - 3);
                    }
                    else
                    {
                        // Obciecie cudzyslowu
                        NewFile_ = NewFile_.Substring(1, NewFile_.Length - 2);
                    }

                    // Interpretowanie "escape characters" i zamiana na odpowiednie znaki
                    string NewFile_0 = "";
                    for (int i = 0; i < NewFile_.Length; i++)
                    {
                        if (NewFile_[i] == '\'')
                        {
                            InsideESC = !InsideESC;
                        }
                        else
                        {
                            if (InsideESC && (NewFile_[i] == '\\'))
                            {
                                i++;
                                switch (NewFile_[i])
                                {
                                    case '\'':
                                    case '\"':
                                    case '\\':
                                        NewFile_0 = NewFile_0 + NewFile_[i];
                                        break;
                                }
                            }
                            else
                            {
                                NewFile_0 = NewFile_0 + NewFile_[i];
                            }
                        }
                    }

                    return NewFile_0;
                }
            }
            return NewFile_;
        }


        static void PrepareFileNameTest(string Src, string Dst)
        {
            if (PrepareFileName(Src) != Dst)
            {
                Console.WriteLine("ERROR: [" + Src + "] [" + PrepareFileName(Src) + "] [" + Dst + "]");
            }
            else
            {
                Console.WriteLine("OK: [" + Src + "] [" + PrepareFileName(Src) + "]");
            }
        }

        public static void Main(string[] args)
        {
            Console.WriteLine("Start");
            PrepareFileNameTest("C:\\qqq\\www", "C:\\qqq\\www");
            PrepareFileNameTest("\"C:\\qqq\\www\"", "C:\\qqq\\www");
            PrepareFileNameTest("'/qqq/www' ", "/qqq/www");
            PrepareFileNameTest("'/qqq/w'\\''ww' ", "/qqq/w'ww");
            PrepareFileNameTest("'/qqq/w'\\'\\''ww' ", "/qqq/w''ww");
            PrepareFileNameTest("'/qqq/w'\\'''\\''ww' ", "/qqq/w''ww");
            PrepareFileNameTest("$'/qqq/w\\'ww' ", "/qqq/w'ww");
            PrepareFileNameTest("$'/qqq/w\\'\\'ww' ", "/qqq/w''ww");
            Console.WriteLine("Stop");
        }
    }
}
0

A tak z ciekawości,

jeżeli ma to być GUI (przesuwanie myszką), to czemu nie okienkowe z file dialogiem?

0
WeiXiao napisał(a):

A tak z ciekawości,

jeżeli ma to być GUI (przesuwanie myszką), to czemu nie okienkowe z file dialogiem?

Ja nad tym siedzę nie na użytek jednego konkretnego projektu, tylko w celu znalezienia uniwersalnego sposobu. Nieraz tworze prosty program, wtedy to najprościej i najszybciej w interfejsie tekstowym. Jak program zapyta o plik, to najprościej upuścić ikonę pliku zamiast wpisać nazwę, w ten sposób można uruchamiać na wielu plikach, widocznych w oknie obok. Podobnie, jak testuje się apkę z parametrem, którym jest nazwa pliku, nawet nie trzeba poprawiać wpisanej nazwy.

0
obscurity napisał(a):
andrzejlisek napisał(a):

Drugie pytanie: W Windows 10 i 11, jak konsola uruchomiona jest jako administrator, to przeciąganie pliku na konsolę nie działa. Jak to umożliwić?

Nie da się, procesy odpalone jako zwykły user nie mogą prowadzić interakcji z procesami odpalonymi jako admin - nie przechodzą żadne komunikaty. Możesz przeciągać z innych programów odpalonych jako admin.

Mam dwa komputery, jeden to wirtualny z Windows 10 skonfigurowany samodzielnie za pomoca obrazu płyty pobranego z Microsoft, drugi to laptop z Windows 11 otrzymany już skonfigurowany od pracodawcy.

Na tym pierwszym, mogę uruchomić wiersz polecenia jako user i jako admin, przy czym w przypadku uruchamiania jako admin należy potwierdzić. Nie wiem, jak uruchomić widok folderu w eksploratorze jako admin, ale można uruchomić Total Commander jako admin. Jak oba programy (TC i cmd.exe) sa jako admin to komunikat przechodzi i ścieżka się wpisuje.

Natomiast na laptopie z Win11 (jest ustawione logowanie do domeny), Cmd i Powershell już z automatu uruchamia się jako admin i nie muszę potwierdzać uruchamiania. Mimo to, jak uruchomię Total Commander jako admin, to i tak przeciąnie ikony z plikiem nie działa. Natomiast, jak na tym laptopie uruchomię terminal Cygwin64 i Total Commander, to działa za każdym razem, niezależnie od tego, który program, jak uruchomię (czy zwyczajnie, czy PPM i uruchom jako admin). Również działa przeciąganie nazwy pliku z eksploratora do terminala Cygwin64 uruchomionego jako admin.

Skąd bierze się ta różnica w działaniu między Win10 i Win11? Jak zmusić Cmd.exe do współpracy z ikonami na laptopie, tym bardziej, że inny program uruchomiony jako admin prawidłowo reaguje na upuszczenie ikony?

0

Co do OSX, to nikt nie wie, ale miałem możliwość samemu to sprawdzić (kolega udostepnił mi swój MacBook na 5 minut w tym celu).

W OSX standardowo są dwa programy będące konsolą (już nie pamiętam nazw) i w obu jest tak samo. Jak się przeciągnie plik na konsolę, to nigdy nie dopisuje żadnego cudzysłowu, natomiast, jak wystąpią w nazwie specjalne znaki, w tym spacje, to każdy taki znak jest poprzedzony znakiem \.

Na przykład, plik /Users/abc/Desktop/Jakis plik'znak''.txt
będzie wpisany jako /Users/abc/Desktop/Jakis\ plik\'znak\'\'.txt.

Z tego, co widzę, da się pogodzić to z pierwotnym algorytmem. Jeżeli podana ścieżka spełnia jednocześnie warunki, to jest to OSX:

  1. Nie jest objęta w żaden cudzysłów
  2. Zaczyna się od znaku /
  3. Na drugim miejscu jest znak inny niż :
    W przypadku Windows znak \ w nazwie pliku nie jest możliwy, a Linux obejmuje taką ścieżkę w cudzysłów.
0

Nie do końca rozumiem problem. W przypadku Linuksa tam musi przecież być w apostrofach, bo inaczej będzie to błędna ścieżka, np gdyby zawierała spacje. W Windows musi to być chyba cudzysłów.

Nie wiem co chcesz osiągnąć, po prostu trzeba podać programowi, który czeka taką nazwę jaka zostanie wygenerowana i będzie zawsze dobrze. Gdybyś posuwał np cudzysłowy to zepsujesz nazwę, po co chcesz to robić?

A co do wyciągania nazwy pliku ze ścieżki, to c++ ma na to gotowe funkcje:
https://en.cppreference.com/w/cpp/filesystem/path/filename

Prawdopodobnie działa to na każdym systemie prawidłowo.

0

Problem jest moim zdaniem bardzo prosty. Piszę jakiś program w C# lub C++. W wielu przypadkach z różnych względów najbardziej pasuje mi zbudować program pracujący w trybie tekstowym w konsoli.

Chyba nie bez powodu, konsola w każdym OS ma taką funkcję, że jak się przeciągnie ikonę pliku widoczną obok (z pulpitu lub jakiegoś folderu), to wpisze się nazwa pliku z całą ścieżką, a ja nie mam powodu, żeby nie korzystać z tej funkcji. Np. program potrzebuje dwóch plików, to w odpowiednich miejscach wywołuję Console.ReadLine() (w przypadku C#) i w tych momentach najwygodniej jest przeciągnąć ikonę i nacisnąć Enter, tym bardziej, że na ekranie obok jest ikona potrzebnego pliku.

Sednem sprawy jest to, że jak się tak przeciągnie ikonę, to do konsoli wpisze się ścieżka do pliku, ale zmieniona w określony sposób (np. dodany cudzysłów, pozmieniane niektóre znaki). Ja natomiast chciałbym osiągnąć algorytm, który odwraca tą zmianę, nic więcej. Taka zmiana jest potrzebna, żeby ścieżka wpisana do konsoli była zdatna do postawienia w funkcjach operacji na plikach. O ile jest to możliwe, żeby to była jedna funkcja działająca na wszystkich trzech systemach (Windows, Linux, OSX). Jak widać, własnym wysiłkiem napisałem funkcję działającą na Ubuntu Linux, Cygwin64 i Windows.

Oczywiście, jestem chętny własnym wysiłkiem rozwiązać problem do końca, ale najpierw muszę dokładnie poznać zasady modyfikacji ścieżki we wszystkich systemach, dlatego założyłem ten temat.

Prawdziwa ścieżka nigdy nie jest w cudzysłowie. Jedynie przy przeciąganiu ikony do konsoli jest wprowadzana na cudzysłów (w przypadku Windows) i na to ja nie mam wpływu (jakiś programista w Microsoft tak wymyślił i tyle). Oczywiście, po przeciągnięciu ikony i przed naciśnięciem Enter mogę usunąć cudzysłów, ale przecież można zautomatyzować tą czynność.

A kolejne kroki, czyli co zrobić z "poprawionym" wpisem (otwarcie pliku, wydzielenie nazwy folderu, pliku i rozszerzenia), które można wykonać się za pomocą stosownych funkcji w bibliotece standardowej, to już inna sprawa, poza przedmiotem tego tematu.

0
andrzejlisek napisał(a):

Sednem sprawy jest to, że jak się tak przeciągnie ikonę, to do konsoli wpisze się ścieżka do pliku, ale zmieniona w określony sposób (np. dodany cudzysłów, pozmieniane niektóre znaki). Ja natomiast chciałbym osiągnąć algorytm, który odwraca tą zmianę, nic więcej. Taka zmiana jest potrzebna, żeby ścieżka wpisana do konsoli była zdatna do postawienia w funkcjach operacji na plikach. O ile jest to możliwe, żeby to była jedna funkcja działająca na wszystkich trzech systemach (Windows, Linux, OSX). Jak widać, własnym wysiłkiem napisałem funkcję działającą na Ubuntu Linux, Cygwin64 i Windows.

Hmm dalej nie bardzo rozumiem problem. Ta ścieżka z konsoli powinna być zdatna do użytku w standardowych metodach do operacji na plikach. Bez żadnych zmian, a tym bardziej usuwania cudzysłowie. Niezależnie od tego, na jakiej platformie skompilujesz aplikację, bo funkcje biblioteczne do obsługi plików powinny akceptować ścieżki zgodnie z formatem systemu operacyjnego, na którym skompilowałeś aplikację. Format generowany w terminalu jest prawidłowy i taka ścieżka powinna działać bez żadnych zmian

0

Jeżeli ścieżka jest jako parametr uruchomienia programu, to być może konsola sama zamienia z powrotem i to programu przekazuje tak, jak powinno. Parametry są oddzielane spacją, a musi istnieć jakiś sposób na przekazanie parametru zawierajacego spację w sobie, dlatego wymyślono cudzysłów lub jakieś inne sposoby odróżnienia spacji w parametrze od spacji między parametrami.

Natomiast podanie ścieżki w cudzysłowie w czasie pracy programu powoduje, że przy próbie otwarcia pliku powoduje wyjątek.Wyjatek2.png

Moim zdaniem, w tym przypadku nie ma znaczenia, czy to jest apka konsolowa, czy WinForms, bo konstruktor FileStream działa tak samo w obu przypadkach. Jakiś czas temu, zwyczajnie o tym zapomniałem i program się wysypał. Akurat swego czasu interesował mnie tylko Windows, więc usuwanie cudzysłowu rozwiązało problem.

0

Prościej chyba nie umiem wytłumaczyć, ale spróbuję na przykładzie w C#.

Jest sobie poniższy program. Nie ważne, czy taka realizacja ma sens i że można inaczej (np. nazwa pliku jako parametr przekazywany w tablicy args), bo to jest demo, równie dobrze może to być część jakiegoś większego programu. Uruchamiam klikając plik exe, albo ewentualnie otwieram konsolę i wpisuję polecenie dotnet prog.dll. Jak pojawi się napis "Enter file name", to zamiast ręcznie wpisywać lub wklejać nazwy, najłatwiej chwycić ikonę i puścić na konsoli. Nazwa pliku wpisze się sama, ale w sposób zmieniony. Jak nacisnę Enter tak, jak jest, to zamiast pokazać się zawartość, prawdopodobnie program się wysypie (możliwy powód widać na screenie w poprzednim poście). A jak wpisze nazwę ręcznie bez cudzysłowu, ani żadnych innych zmian w stosunku do tego, jak system prezentuje ścieżkę, to program zadziała poprawnie, czyli odczyta plik.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Text;
using System.Windows.Forms;

namespace Prog
{
    class MainClass
    {
        statuc string CorrectFileName(string FN)
        {
            return FN;
        }
    
        public static void Main(string[] args)
        {
            Console.Write("Enter file name: ");
            string FileName = Console.ReadLine();
            FileName = CorrectFileName(FileName);
            FileStream FS = new FileStream(FileName, FileMode.Open, FileAccess.Read);
            StreamReader SR = new StreamReader(FS);
            string FileContents = SR.ReadToEnd();
            SR.Close();
            FS.Close();
            Console.WriteLine(FileContents);
        }
    }
}

Celem jest funkcja, która na wejściu przyjmuje to, co zostało wpisane poprzez przeciągnięcie ikony, a na wyjściu zwraca poprawną ścieżkę i nazwę pliku (odpowiednia rozbudowa funkcji CorrectFileName).

Ostatecznie, jak opieramy się na wyżej podanym kodzie, ten program miałby działać tak, że uruchamiam program, przeciągam ikonę, naciskam Enter, pokazuje się treść pliku.

Czy teraz jest zrozumiałe?

0

Ogarnąłem cały algorytm, do którego napisałem zestaw danych testowych. Po lewej stronie jest to, co wkleja się do konsoli jako tekst po przeciągnięciu ikony, a po prawej stronie jest faktyczna nazwa pliku ze ścieżką. Dodatkowo, jeżeli na wejście poda się surową ścieżkę dostępu, to również algorytm również powinien sobie poradzić, wprost przepisując na wyjście. Jedynie może być problem w przypadku, gdy na MacOS w nazwie pliku jest znak \, ale z drugiej strony jest to problem tak marginalny, że nie warto sobie tym zawracać głowy.

Jak w konsoli jest znak zachęty i wprowadza się polecenie wykonania programu z parametrami, to modyfikacja ścieżki pliku (na przykład dopisanie cudzysłowu) jest potrzebna i słuszna, natomiast, jeżeli pracuje jakiś program i to on woła o nazwę pliku za pomocą std::getline (std::cin,name) w C++ lub Console.ReadLine() w C#, to wtedy modyfikacja nie jest pożądana i należy ją odwrócić.

Poniżej przypadki testowe z podziałem na OS. Mam nadzieję, ze wystarczające.

Windows
[\\qqq\www] -> [\\qqq\www]
["\\qqq\www"] -> [\\qqq\www]
[C:\qqq\www] -> [C:\qqq\www]
["C:\qqq\www"] -> [C:\qqq\www]
Linux
['/qqq/www' ] -> [/qqq/www]
['/qqq/w'\''ww' ] -> [/qqq/w'ww]
['/qqq/w'\'\''ww' ] -> [/qqq/w''ww]
['/qqq/w'\'''\''ww' ] -> [/qqq/w''ww]
[$'/qqq/w\'ww' ] -> [/qqq/w'ww]
[$'/qqq/w\'\'ww' ] -> [/qqq/w''ww]
[/qqq/www] -> [/qqq/www]
[/qqq/w'ww] -> [/qqq/w'ww]
[/qqq/w''ww] -> [/qqq/w''ww]
['/qqq/www/asd fg.txt'] -> [/qqq/www/asd fg.txt]
['/qqq/www/asd'\'' fg.txt'] -> [/qqq/www/asd' fg.txt]
['/qqq/www/a"sd'\'''\'' fg.txt'] -> [/qqq/www/a"sd'' fg.txt]
MacOS
[/Users/prg/Plik\'.txt] -> [/Users/prg/Plik'.txt]
[/Users/prg/Plik\'\'.txt] -> [/Users/prg/Plik''.txt]
[/Users/prg/Plik\<.txt] -> [/Users/prg/Plik<.txt]
[/Users/prg/Plik\ x.txt] -> [/Users/prg/Plik x.txt]
[/Users/prg/Plik\\x.txt] -> [/Users/prg/Plik\x.txt]
[/Users/prg/Plik'.txt] -> [/Users/prg/Plik'.txt]
[/Users/prg/Plik''.txt] -> [/Users/prg/Plik''.txt]
[/Users/prg/Plik<.txt] -> [/Users/prg/Plik<.txt]
[/Users/prg/Plik x.txt] -> [/Users/prg/Plik x.txt]

Funkcja zmiany nazwy w C#, jakby ktoś był ciekawy:

        public static string PrepareFileName(string NewFile_)
        {
            // Obcięcie ewentualnych spacji z obu stron
            NewFile_ = NewFile_.Trim();

            // Prawidłowa ścieżka, która moze być objęta cudzysłowem
            // lub zawiera znaki specjalne, zawsze ma minimum dwa znaki.
            if (NewFile_.Length > 2)
            {
                // Możliwe tylko w Windows:
                // Jeżeli ścieką jest objęta podwójnym cudzysłowem,
                // to należy obciąć cyudzysłów i zwrócić bez cudzysłowu
                if ((NewFile_[0] == '\"') && (NewFile_[NewFile_.Length - 1] == '\"'))
                {
                    NewFile_ = NewFile_.Substring(1, NewFile_.Length - 2);
                    return NewFile_;
                }

                // Możliwe tylko w Linux lub Cygwin:
                // Ścieżka wymaga przetworzenia w przypadku, gdy jest objęta
                // pojedynczym cudzysłowem. Dodatkowo, może zaczynać się od znaku dolara
                if (((NewFile_[0] == '\'') || ((NewFile_[0] == '$') && (NewFile_[1] == '\''))) && (NewFile_[NewFile_.Length - 1] == '\''))
                {
                    // Są dwa możliwe stany podczas analizy ścieżki w Linux.
                    // Jeżeli nie ma znaku dolara, to ścieżka zaczyna się
                    // od elementu zawierającego słowo wprost, jeżeli jest znak dolara,
                    // to zaczyna się od elementu zawierającego znaki specjalne.
                    bool InsideESC = false;
                    if (NewFile_[0] == '$')
                    {
                        InsideESC = true;
                        NewFile_ = NewFile_.Substring(2, NewFile_.Length - 3);
                    }
                    else
                    {
                        NewFile_ = NewFile_.Substring(1, NewFile_.Length - 2);
                    }
                    string NewFile_0 = "";
                    for (int i = 0; i < NewFile_.Length; i++)
                    {
                        // Znak pojedynczego cudzysłowu, niepoprzedzony znakiem backslash
                        // zmienia stan przetwarzania na przeciwny
                        if (NewFile_[i] == '\'')
                        {
                            InsideESC = !InsideESC;
                        }
                        else
                        {
                            // Znaki cudzysłowu i backslach występujące w ścieżke
                            // są zawsze poprzedzone znakiem backslach, aby odróżnić
                            // znak w nazwie od znaku sterującego
                            if (InsideESC && (NewFile_[i] == '\\'))
                            {
                                i++;
                                switch (NewFile_[i])
                                {
                                    case '\'':
                                    case '\"':
                                    case '\\':
                                        NewFile_0 = NewFile_0 + NewFile_[i];
                                        break;
                                }
                            }
                            else
                            {
                                NewFile_0 = NewFile_0 + NewFile_[i];
                            }
                        }
                    }
                    return NewFile_0;
                }

                // Możliwe tylko w MacOS:
                // Każda ścieżka w MacOS zaczyna się od znaku slash, nie ma dwukropka
                // na drugim miejscu, nie ma cudzysłowu, co odróżnia ścieżkę w Windows.
                // Każdy znak inny niż litera, cyfra jest poprzedzony znakiem backslash.
                // W praktyce, wystarczy usunąć wszystkie znaki backslash
                // Teoretycznie, nastąpi błąd, gdy poda się ścieżkę oryginalną
                // (niezmodyfikowaną) z Linux lub MacOS zawierającą znak backslach w nazwie,
                // w praktyce taki przypadek zdarza się niezmiernie rzadko. Obsłużenie
                // niemodyfikacji ścieżki Linux/MacOS z backslash w nazwie i jednocześnie
                // odwrócenie modyfikacji zmodyfikowanej ścieżki w MacOS nie jest możliwe.
                if ((NewFile_[0] == '/') && (NewFile_[1] != ':'))
                {
                    for (int i = 0; i < NewFile_.Length; i++)
                    {
                        if (NewFile_[i] == '\\')
                        {
                            NewFile_ = NewFile_.Remove(i, 1);
                        }
                    }
                }
            }
            return NewFile_;
        }

Temat manipulacji na nazwie pliku wstawianej do konsoli uważam za zakończony, natomiast cały czas pozostaje aktualne pytanie, które już zadałem:

Mam dwa komputery, jeden to wirtualny z Windows 10 skonfigurowany samodzielnie za pomoca obrazu płyty pobranego z Microsoft, drugi to laptop z Windows 11 otrzymany już skonfigurowany od pracodawcy.

Na tym pierwszym, mogę uruchomić wiersz polecenia jako user i jako admin, przy czym w przypadku uruchamiania jako admin należy potwierdzić. Nie wiem, jak uruchomić widok folderu w eksploratorze jako admin, ale można uruchomić Total Commander jako admin. Jak oba programy (TC i cmd.exe) sa jako admin to komunikat przechodzi i ścieżka się wpisuje.

Natomiast na laptopie z Win11 (jest ustawione logowanie do domeny), Cmd i Powershell już z automatu uruchamia się jako admin i nie muszę potwierdzać uruchamiania. Mimo to, jak uruchomię Total Commander jako admin, to i tak przeciąnie ikony z plikiem nie działa. Natomiast, jak na tym laptopie uruchomię terminal Cygwin64 i Total Commander, to działa za każdym razem, niezależnie od tego, który program, jak uruchomię (czy zwyczajnie, czy PPM i uruchom jako admin). Również działa przeciąganie nazwy pliku z eksploratora do terminala Cygwin64 uruchomionego jako admin.

Skąd bierze się ta różnica w działaniu między Win10 i Win11? Jak zmusić Cmd.exe do współpracy z ikonami na laptopie, tym bardziej, że inny program uruchomiony jako admin prawidłowo reaguje na upuszczenie ikony?

1 użytkowników online, w tym zalogowanych: 0, gości: 1