Bat, .Net и уровни каталогов
Довольно-таки необычный вопрос, который завел меня в тупик. Итак - есть приложение написанное на C++ и есть приложение написанное на .Net 7. Дерево каталогов выглядит так:
Data\Run.bat
Data\Input\
Data\Output\
Data\Win64\Bin\App-C-plus.exe
Data\Win64\Bin\App-Dot-Net.exe
Содержимое .bat выглядит так:
@echo off
Data\Win64\Bin\App-C-plus.exe ..\..\Data\Input -build ..\..\Data\Output
Data\Win64\Bin\App-Dot-Net.exe ..\..\Data\Input -build ..\..\Data\Output
Содержимое приложения на .Net:
static void Main(string[] args)
{
if (args.Length > 0)
{
if (args[1] == "-build")
{
try
{
File.Copy(args[0], args[2], true);
Console.WriteLine($"File copied from {args[0]} to {args[1]}");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
}
}
Вот мы и подобрались к сути вопроса: - почему данная команда в .bat файле для приложения написанного на .Net работает не так, как ожидалось:
Data\Win64\Bin\App-Dot-Net.exe ..\..\Data\Input -build ..\..\Data\Output
Но если изменить ее вот так:
Data\Win64\Bin\App-Dot-Net.exe Data\Input -build Data\Output
Чтобы получилось вот так:
@echo off
Data\Win64\Bin\App-C-plus.exe ..\..\Data\Input -build ..\..\Data\Output
Data\Win64\Bin\App-Dot-Net.exe Data\Input -build Data\Output
Все отлично работает. Причем команда для приложения написанного на C++ работает исправно. Мы можем спокойно перейти на уровень каталога выше. Я просто не понимаю, почему стандартная команда перехода на каталог выше, работает не так как я этого ожидал. Не то чтобы меня это беспокоит, просто не могу найти на это ответ. То есть приложение инициализирует запуск, как будто непосредственно находится в папке Data.
UPD: Добавил код, но вариант, предложенный товарищем @rotabor гениален, именно что-то такое я и предпологал:
Directory.SetCurrentDirectory(System.Reflection.Assembly.GetExecutingAssembly().Location);
Правда я его еще не протестировал, но думаю что это то, что нужно.
UPD-1: Протестировал, получаю это:
Error: The value cannot be an empty string. (Parameter 'path')
Код выглядит так:
static void Main(string[] args)
{
if (args.Length > 0)
{
if (args.Length == 2)
{
try
{
Directory.SetCurrentDirectory(System.Reflection.Assembly.GetExecutingAssembly().Location);
File.Copy(args[0], args[1], true);
Console.WriteLine($"File copied from {args[0]} to {args[1]}");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
}
}
Ответы (1 шт):
Секрет как раз и заключается в строке "//Copy file here {From source to Destination}", равно как и в соотвествующей строке приложения на С++, а вы их не показали. Похоже, что последнее работает от собственного каталога, а .NET - от каталога запуска. Ну и бибилиотеки System.IO и соотвествующая в С++ могут работать по-разному.
В общем, пока удивительного ничего нет.
Вы еще позапускайте батник из командной строки из разных папок, наверняка что-то пойдёт не так.
UPD В документации всё подробно расписано https://learn.microsoft.com/en-us/dotnet/api/system.io.path.getfullpath?view=net-7.0
Пример:
using System;
using System.IO;
namespace NsPathUncertanity {
class PathUncertanity {
static void Main(string[] args) {
try {
switch (args.Length) {
case 0: Console.WriteLine("nop"); break;
case 1: Console.WriteLine(Path.GetFullPath(args[0])); break;
// case 2: Console.WriteLine(Path.GetFullPath(args[0], args[1])); break;
}
}
catch (Exception objException) { Console.WriteLine(objException.Message); }
}
}
}
Результат:
C:\Users\auser\Documents\My Projects\Stackoverflow>ar graf.txt
C:\Users\auser\Documents\My Projects\Stackoverflow\graf.txt
C:\Users\auser\Documents\My Projects\Stackoverflow>ar ..\drum.txt
C:\Users\auser\Documents\My Projects\drum.txt
C:\Users\auser\Documents\My Projects\Stackoverflow>ar ..\..\drum.txt
C:\Users\auser\Documents\drum.txt
Всё работает согласно документации. Возможно, вы где-то ранее меняете текущий путь, что влияет на интерпретацию ".."
UPD UPD Если вам нужно просто повторить то, что написано на С++, то используйте
Directory.SetCurrentDirectory(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location));
перед File.Copy