File path

ИМЯ

Файл :: Путь — создание или удаление дерева каталогов

ВЕРСИЯ

В этом документе описывается версия 2.09 файла :: Path, выпущенная в 2013-01-17 гг.

СИНТАКСИС

use File::Path qw(make_path remove_tree);  make_path('foo/bar/baz', '/zug/zwang'); make_path('foo/bar/baz', '/zug/zwang', {  verbose => 1,  mode => 0711, });  remove_tree('foo/bar/baz', '/zug/zwang'); remove_tree('foo/bar/baz', '/zug/zwang', {  verbose => 1,  error => my $err_list, });  # legacy (interface promoted before v2.00) mkpath('/foo/bar/baz'); mkpath('/foo/bar/baz', 1, 0711); mkpath(['/foo/bar/baz', 'blurfl/quux'], 1, 0711); rmtree('foo/bar/baz', 1, 1); rmtree(['foo/bar/baz', 'blurfl/quux'], 1, 1);  # legacy (interface promoted before v2.06) mkpath('foo/bar/baz', '/zug/zwang', { verbose => 1, mode => 0711 }); rmtree('foo/bar/baz', '/zug/zwang', { verbose => 1, mode => 0711 }); 

ОПИСАНИЕ

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

Предоставляются следующие функции:


  • make_path ($ dir1, $ dir2, ….)
  • make_path ($ dir1, $ dir2, …., % opts)

    Функция make_path создает указанные каталоги, если они не существуют раньше, подобно команде Unix mkdir -p .

    Функция принимает список создаваемых каталогов. Его поведение может быть настроено с помощью необязательного hashref, появляющегося в качестве последнего параметра в вызове.

    Функция возвращает список каталогов, фактически созданных во время вызова; в скалярном контексте число созданных каталогов.

    Следующие ключи распознаются в хэш-опции:

    • mode => $ num

      Режим числовых разрешений для каждого созданного каталога (по умолчанию — 0777), который должен быть изменен текущей umask . Если каталог уже существует (и, следовательно, его не нужно создавать), разрешения не будут изменены.

      mask распознается как псевдоним для этого параметра.


    • verbose => $ bool

      Если он присутствует, заставит make_path печатать имя каждого каталога по мере его создания. По умолчанию ничего не печатается.

    • error => $ err

      Если он присутствует, он должен быть ссылкой на скаляр. Этот скаляр будет привязан к массиву, который будет использоваться для хранения любых возникающих ошибок. Дополнительную информацию см. В разделе « ОБРАБОТКА ОШИБКИ ».

      Если этот параметр не используется, некоторые условия ошибки могут привести к фатальной ошибке, которая приведет к остановке программы, если она не окажется в блоке eval .

    • владелец => $ владелец
    • user => $ owner
    • uid => $ owner

      Если присутствует, приведет к тому, что любой созданный каталог будет принадлежать $owner . Если значение является числовым, оно будет интерпретироваться как uid, иначе, поскольку имя пользователя предполагается. Ошибка будет выдана, если имя пользователя не может быть сопоставлено с uid, или uid не существует, или процесс не имеет привилегий для изменения права собственности.

      Собственные каталоги, которые уже существуют, не будут изменены.

      user и uid являются псевдонимами owner .


    • group => $ group

      Если присутствует, приведет к тому, что любой созданный каталог будет принадлежать группе $group . Если значение является числовым, оно будет интерпретироваться как gid, иначе как предполагается имя группы. Ошибка будет выдана, если имя группы не может быть сопоставлено с gid или gid не существует, или в процессе отсутствуют привилегии для изменения права собственности на группу.

      Групповое владение каталогами, которые уже существуют, не будет изменено.

      make_path '/var/tmp/webcache', {owner=>'nobody', group=>'nogroup'}; 
  • mkpath ($ dir)
  • mkpath ($ dir, $ verbose, $ mode)
  • mkpath ([$ dir1, $ dir2, …], $ verbose, $ mode)
  • mkpath ($ dir1, $ dir2, …, % opt)

    Функция mkpath () предоставляет устаревший интерфейс make_path () с другой интерпретацией переданных аргументов. Поведение и возвращаемое значение функции в остальном идентичны make_path ().


  • remove_tree ($ dir1, $ dir2, ….)
  • remove_tree ($ dir1, $ dir2, …., % opts)

    Функция remove_tree удаляет указанные каталоги и любые файлы и подкаталоги, которые они могут содержать, подобно команде Unix rm -r или del /s в Windows.

    Функция принимает список каталогов, которые нужно удалить. Его поведение может быть настроено с помощью необязательного hashref, появляющегося в качестве последнего параметра в вызове.

    Функции возвращают количество удаленных файлов.

    Следующие ключи распознаются в хэш-опции:

    • verbose => $ bool

      Если присутствует, приведет к тому, что remove_tree напечатает имя каждого файла по мере его отсоединения. По умолчанию ничего не печатается.

    • safe => $ bool

      Если установлено значение true, это приведет к тому, что remove_tree пропустит файлы, для которых в процессе отсутствуют необходимые привилегии, необходимые для удаления файлов, такие как привилегии удаления на VMS. Другими словами, код не будет пытаться изменять права доступа к файлам. Таким образом, если процесс прерывается, объект файловой системы не будет оставлен в более разрешительном режиме.


    • keep_root => $ bool

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

      remove_tree( '/tmp', {keep_root => 1} ); 
    • result => $ res

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

      remove_tree( '/tmp', {result => my $list} ); print "unlinked $_n" for @$list; 

      Это полезная альтернатива verbose ключу.

    • error => $ err

      Если он присутствует, он должен быть ссылкой на скаляр. Этот скаляр будет привязан к массиву, который будет использоваться для хранения любых возникающих ошибок. Дополнительную информацию см. В разделе « ОБРАБОТКА ОШИБКИ ».

      Удаление вещей — гораздо более опасное предложение, чем создание вещей. Таким образом, существуют определенные условия, с remove_tree
      может столкнуться remove_tree , которые настолько опасны, что единственное нормальное действие — убить программу.

      Используйте error чтобы уловить все разумные (проблемы с разрешениями и т. П.), И пусть она умрет, если что-то выходит из-под контроля. Это самый безопасный курс действий.

  • rmtree ($ dir)
  • rmtree ($ dir, $ verbose, $ safe)
  • rmtree ([$ dir1, $ dir2, …], $ verbose, $ safe)
  • rmtree ($ dir1, $ dir2, …, % opt)

    Функция rmtree () предоставляет устаревший интерфейс remove_tree () с другой интерпретацией переданных аргументов. Поведение и возвращаемое значение функции в остальном идентичны функции remove_tree ().

ОБРАБОТКА ОШИБОК

  • ЗАМЕТКА:

    Следующий механизм обработки ошибок считается экспериментальным и может изменяться в ожидании обратной связи от пользователей.

Если make_path
или remove_tree столкнулись с ошибкой, диагностическое сообщение будет напечатано в STDERR через carp (для нефатальных ошибок) или через croak (для фатальных ошибок).

Если это поведение нежелательно, атрибут error может использоваться для хранения ссылки на переменную, которая будет использоваться для хранения диагностических данных. Переменная делается ссылкой на массив хеш-ссылок. Каждый хэш содержит одну пару ключ / значение, где ключ — это имя файла, а это значение является сообщением об ошибке (включая содержимое $! При необходимости). Если встречается общая ошибка, диагностический ключ будет пустым.

Пример использования выглядит так:

remove_tree( 'foo/bar', 'bar/rat', {error => my $err} ); if (@$err) {  for my $diag (@$err) {  my ($file, $message) = %$diag;  if ($file eq '') {  print "general error: $messagen";  }  else {  print "problem unlinking $file: $messagen";  }  } } else {  print "No error encounteredn"; } 

Обратите внимание: если ошибки не встречаются, $err будет ссылаться на пустой массив. Это означает, что $err
всегда будет TRUE; поэтому вам нужно проверить @$err чтобы определить, произошли ли ошибки.

ЗАМЕТКИ

File::Path слепо экспортирует mkpath и rmtree в текущее пространство имен. В наши дни это считается плохим стилем, но для его изменения теперь будет слишком много кода. Тем не менее, вам предлагается указать, что именно вы ожидаете использовать:

use File::Path 'rmtree'; 

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

use File::Path 'remove_tree'; 

Обратите внимание, что побочный эффект вышесказанного состоит в том, что mkpath и rmtree больше не экспортируются вообще. Это связано с тем, как работает модуль Exporter . Если вы переносите кодовую базу для использования нового интерфейса, вам нужно будет указать все явно. Но это все-таки хорошая практика.

use File::Path qw(remove_tree rmtree); 

API ИЗМЕНЕНИЯ

API был изменен в ветке 2.0. Некоторое время mkpath
и rmtree безуспешно пытались разобраться с двумя разными механизмами вызова. Этот подход считался провалом.

Новая семантика теперь доступна только с make_path и remove_tree . Старая семантика доступна только через mkpath и rmtree . Пользователям настоятельно рекомендуется обновить не менее 2.08, чтобы избежать сюрпризов.

ОБСУЖДЕНИЕ БЕЗОПАСНОСТИ

Были условия гонки 1.x реализация функции rmtree файла :: Path (хотя иногда исправлялась в зависимости от дистрибутива ОС или платформы). Версия 2.0 содержит код, чтобы избежать проблемы, упомянутой в CVE-2002-0435.

Для получения дополнительной информации см. Следующие страницы:

http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=286905 http://www.nntp.perl.org/group/perl.perl5.porters/2005/01/msg97623.html http://www.debian.org/security/2005/dsa-696 

Кроме того, если не задан safe
параметр (или третий параметр в традиционном интерфейсе равен TRUE), следует ли прервать remove_tree , файлы, которые изначально были доступны только для чтения, теперь могут иметь свои разрешения для чтения-записи (или » delete OK «).

ДИАГНОСТИКИ

Ошибки FATAL заставят программу остановиться ( croak ), так как проблема настолько серьезная, что было бы опасно продолжать. (Это всегда можно поймать в ловушку с помощью eval , но это не очень хорошая идея. В этих условиях умереть — это лучшее, что можно сделать).

Серьезные ошибки могут быть захвачены с использованием современного интерфейса. Если они не захвачены или используется старый интерфейс, такая ошибка приведет к остановке программы.

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

  • mkdir [путь]: [errmsg] (SEVERE)

    make_path не удалось создать путь. Вероятно, какая-то ошибка разрешений в точке отправления или недостаточные ресурсы (такие как бесплатные inodes в Unix).

  • Не указан корневой путь (ы)

    make_path не был предоставлен никаких путей для создания. Это сообщение выдается только в том случае, если процедура вызывается с использованием традиционного интерфейса. Современный интерфейс будет оставаться безмолвным, если ему нечего делать.

  • Данный файл или каталог отсутствует

    В Windows, если make_path дает вам это предупреждение, это может означать, что вы превысили максимальную длину вашей файловой системы.

  • не может получить начальный рабочий каталог: [errmsg]

    remove_tree попыталась определить исходный каталог, вызвав Cwd::getcwd , но по какой-либо причине вызов Cwd::getcwd неудачно. Не будет предпринята попытка удалить что-либо.

  • не может ставить начальный рабочий каталог: [errmsg]

    remove_tree попытался установить исходный каталог (после успешного получения его имени через getcwd ), однако по какой-либо причине вызов getcwd неудачно. Не будет предпринята попытка удалить что-либо.

  • не может chdir в [dir]: [errmsg]

    remove_tree попытался установить рабочий каталог, чтобы начать удаление объектов в нем, но не увенчался успехом. Обычно это вопрос с разрешениями. Процедура будет продолжать удалять другие вещи, но этот каталог будет оставлен нетронутым.

  • каталог [dir] изменен до chdir, ожидается dev = [n] ino = [n], фактический dev = [n] ino = [n], прерывание. (Фатальный)

    remove_tree записал устройство и inode каталога, а затем переместился в него. Затем он выполнил stat в текущем каталоге и обнаружил, что устройство и инод уже не совпадают. Поскольку это находится в центре проблемы состояния гонки, программа умрет на этом этапе.

  • не может сделать каталог [dir] read + writeable: [errmsg]

    remove_tree попыталась изменить разрешения для текущего каталога, чтобы гарантировать, что последующие неполадки не будут запущены в проблемы, но не смогли этого сделать. Разрешения остаются такими, какими они были, и программа будет продолжаться, делая все возможное.

  • не может прочитать [dir]: [errmsg]

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

  • не может выполнить сброс chmod [dir]: [errmsg]

    remove_tree , remove_tree все в каталоге, попытался восстановить его права на исходное состояние, но не удалось. Справочник может закончиться тем, что его оставили.

  • не может удалить [dir], когда cwd is [dir]

    Текущий рабочий каталог программы — это / some / path / to / here, и вы пытаетесь удалить предка, например / some / path . Дерево каталогов остается нетронутым.

    Решение состоит из chdir из дочернего каталога в место, находящееся за пределами дерева каталогов, которое нужно удалить.

  • не может chdir [parent-dir] из [child-dir]: [errmsg], прерывание. (Фатальный)

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

  • не может ставить предыдущий рабочий каталог [dir]: [errmsg], прерывание. (Фатальный)

    remove_tree не смог установить родительский каталог после возврата из дочернего remove_tree . Поскольку нет способа узнать, вернемся ли мы туда, где мы думаем, что мы должны быть (сравнивая устройство и индекс inode), единственный выход — это croak .

  • предыдущий каталог [parent-dir] изменен перед входом [child-dir], ожидаемый dev = [n] ino = [n], фактический dev = [n] ino = [n], прерывание. (Фатальный)

    Когда remove_tree возвращается из удаления файлов в дочернем каталоге, проверка показала, что родительский каталог, который он вернул, был не тем, с которого он начинал. Это считается признаком злонамеренной деятельности.

  • не может сделать каталог [dir] доступным для записи: [errmsg]

    Перед удалением каталога (после того, как он успешно удалил все, что он содержал), remove_tree попыталась установить разрешения в каталоге, чтобы убедиться, что он может быть удален и провален. Выполнение программы продолжается, но каталог, возможно, не удаляется.

  • не удается удалить каталог [dir]: [errmsg]

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

  • не может восстановить разрешения от [dir] до [0nnn]: [errmsg]

    После того, как не удалось удалить каталог, remove_tree не смог восстановить свои разрешения из разрешающего состояния обратно к возможно более ограничительной настройке. (Разрешения приведены в восьмеричном).

  • не может сделать файл [файл] доступным для записи: [errmsg]

    remove_tree попыталась принудительно предоставить разрешения файла, чтобы убедиться, что его можно удалить, но этого не удалось. Тем не менее, он все же попытается отсоединить файл.

  • не может восстановить разрешения [file] на [0nnn]: [errmsg]

    После того, как не удалось удалить файл, remove_tree также не смог восстановить разрешения для файла до, возможно, менее разрешительной установки. (Разрешения приведены в восьмеричном).

  • невозможно сопоставить [владельца] с uid, право собственности не изменено »);

    make_path было дано указание предоставить владельцам созданных каталогов символическое имя [owner], но getpwnam не вернул соответствующий числовой uid. Каталог будет создан, но право собственности не будет изменено.

  • не удалось сопоставить [group] с gid, право собственности группы не изменилось

    make_path было поручено предоставить группе право собственности на созданные каталоги на символическое имя [group], но getgrnam не вернул соответствующий числовой gid. Каталог будет создан, но групповое владение не будет изменено.

СМОТРИТЕ ТАКЖЕ

  • File::Remove

    Позволяет перемещать файлы и каталоги в корзину Trashcan / Recycle Bin (где они могут быть позже восстановлены, если необходимо), если операционная система поддерживает такую ​​функциональность. В один прекрасный день эта функция может быть доступна непосредственно в File::Path .

  • File::Find::Rule

    При удалении деревьев каталогов, если вы хотите проверить каждый файл, чтобы решить, удалять его (и, возможно, оставлять большие фрагменты отдельно), File :: Find :: Rule предлагает удобный и гибкий подход к изучению деревьев каталогов.

ОШИБКИ

Сообщите об ошибках в очереди RT:

http://rt.cpan.org/NoAuth/Bugs.html?Dist=File-Path

Вы также можете отправлять запросы на перенос в репозиторий Github:

https://github.com/dland/File-Path

БЛАГОДАРНОСТЬ

Пол Сабо первоначально определил условие гонки, и Брендан О’Ди написал для Debian реализацию, которая рассматривала проблему. Этот код использовался в качестве основы для текущего кода. Их усилия приветствуются.

Gisle Aas внес ряд улучшений в документацию на 2.07, и его советы и помощь также получили высокую оценку.

АВТОРЫ

Тим Банс и Чарльз Бейли. В настоящее время поддерживается Дэвидом Ландгреном < [email protected] >.

Этот модуль является авторским правом (C) Charles Bailey, Tim Bunce и David Landgren 1995-2013. Все права защищены.

ЛИЦЕНЗИЯ

Эта библиотека — бесплатное программное обеспечение; вы можете перераспределить его и / или изменить его на тех же условиях, что и сам Perl.

code-examples.net

Traditional DOS paths

A standard DOS path can consist of three components:

  • A volume or drive letter followed by the volume separator (:).
  • A directory name. The directory separator character separates subdirectories within the nested directory hierarchy.
  • An optional filename. The directory separator character separates the file path and the filename.

If all three components are present, the path is absolute. If no volume or drive letter is specified and the directory names begins with the directory separator character, the path is relative from the root of the current drive. Otherwise, the path is relative to the current directory. The following table shows some possible directory and file paths.

Path Description
C:DocumentsNewslettersSummer2018.pdf An absolute file path from the root of drive C:
Program FilesCustom UtilitiesStringFinder.exe An absolute path from the root of the current drive.
2018January.xlsx A relative path to a file in a subdirectory of the current directory.
..PublicationsTravelBrochure.pdf A relative path to file in a directory that is a peer of the current directory.
C:Projectsapilibraryapilibrary.sln An absolute path to a file from the root of drive C:
C:Projectsapilibraryapilibrary.sln A relative path from the current directory of the C: drive.

You can determine whether a file path is fully qualified (that is, it the path is independent of the current directory and does not change when the current directory changes) by calling the IsPathFullyQualified method. Note that such a path can include relative directory segments (. and ..) and still be fully qualified if the resolved path always points to the same location.

The following example illustrates the difference between absolute and relative paths. It assumes that the directory D:FY2018 exists, and that you haven’t set any curent directory for D: from the command prompt before running the example.

using System; using System.Diagnostics; using System.IO; using System.Reflection;  public class Example {  public static void Main(string[] args)  {  Console.WriteLine($"Current directory is '{Environment.CurrentDirectory}'");  Console.WriteLine("Setting current directory to 'C:\'");    Directory.SetCurrentDirectory(@"C:");  string path = Path.GetFullPath(@"D:FY2018");  Console.WriteLine($"'D:\FY2018' resolves to {path}");  path = Path.GetFullPath(@"D:FY2018");  Console.WriteLine($"'D:FY2018' resolves to {path}");   Console.WriteLine("Setting current directory to 'D:\Docs'");  Directory.SetCurrentDirectory(@"D:Docs");   path = Path.GetFullPath(@"D:FY2018");  Console.WriteLine($"'D:\FY2018' resolves to {path}");  path = Path.GetFullPath(@"D:FY2018");   // This will be "D:DocsFY2018" as it happens to match the drive of the current directory  Console.WriteLine($"'D:FY2018' resolves to {path}");   Console.WriteLine("Setting current directory to 'C:\'");  Directory.SetCurrentDirectory(@"C:");   path = Path.GetFullPath(@"D:FY2018");  Console.WriteLine($"'D:\FY2018' resolves to {path}");   // This will be either "D:FY2018" or "D:FY2018FY2018" in the subprocess. In the sub process,  // the command prompt set the current directory before launch of our application, which  // sets a hidden environment variable that is considered.  path = Path.GetFullPath(@"D:FY2018");  Console.WriteLine($"'D:FY2018' resolves to {path}");   if (args.Length < 1)  {  Console.WriteLine(@"Launching again, after setting current directory to D:FY2018");  Uri currentExe = new Uri(Assembly.GetExecutingAssembly().GetName().CodeBase, UriKind.Absolute);  string commandLine = $"/C cd D:\FY2018 & "{currentExe.LocalPath}" stop";  ProcessStartInfo psi = new ProcessStartInfo("cmd", commandLine); ;  Process.Start(psi).WaitForExit();   Console.WriteLine("Sub process returned:");  path = Path.GetFullPath(@"D:FY2018");  Console.WriteLine($"'D:\FY2018' resolves to {path}");  path = Path.GetFullPath(@"D:FY2018");  Console.WriteLine($"'D:FY2018' resolves to {path}");  }  Console.WriteLine("Press any key to continue... ");  Console.ReadKey();  } } // The example displays the following output: // Current directory is 'C:Programsfile-paths' // Setting current directory to 'C:' // 'D:FY2018' resolves to D:FY2018 // 'D:FY2018' resolves to d:FY2018 // Setting current directory to 'D:Docs' // 'D:FY2018' resolves to D:FY2018 // 'D:FY2018' resolves to D:DocsFY2018 // Setting current directory to 'C:' // 'D:FY2018' resolves to D:FY2018 // 'D:FY2018' resolves to d:FY2018 // Launching again, after setting current directory to D:FY2018 // Sub process returned: // 'D:FY2018' resolves to D:FY2018 // 'D:FY2018' resolves to d:FY2018 // The subprocess displays the following output: // Current directory is 'C:' // Setting current directory to 'C:' // 'D:FY2018' resolves to D:FY2018 // 'D:FY2018' resolves to D:FY2018FY2018 // Setting current directory to 'D:Docs' // 'D:FY2018' resolves to D:FY2018 // 'D:FY2018' resolves to D:DocsFY2018 // Setting current directory to 'C:' // 'D:FY2018' resolves to D:FY2018 // 'D:FY2018' resolves to D:FY2018FY2018 
Imports System.Diagnostics Imports System.IO Imports System.Reflection  Public Module Example   Public Sub Main(args() As String)  Console.WriteLine($"Current directory is '{Environment.CurrentDirectory}'")  Console.WriteLine("Setting current directory to 'C:'")  Directory.SetCurrentDirectory("C:")    Dim filePath As String = Path.GetFullPath("D:FY2018")  Console.WriteLine($"'D:\FY2018' resolves to {filePath}")  filePath = Path.GetFullPath("D:FY2018")  Console.WriteLine($"'D:FY2018' resolves to {filePath}")   Console.WriteLine("Setting current directory to 'D:\Docs'")  Directory.SetCurrentDirectory("D:Docs")   filePath = Path.GetFullPath("D:FY2018")  Console.WriteLine($"'D:\FY2018' resolves to {filePath}")  filePath = Path.GetFullPath("D:FY2018")   ' This will be "D:DocsFY2018" as it happens to match the drive of the current directory  Console.WriteLine($"'D:FY2018' resolves to {filePath}")   Console.WriteLine("Setting current directory to 'C:\'")  Directory.SetCurrentDirectory("C:")   filePath = Path.GetFullPath("D:FY2018")  Console.WriteLine($"'D:\FY2018' resolves to {filePath}")   ' This will be either "D:FY2018" or "D:FY2018FY2018" in the subprocess. In the sub process,  ' the command prompt set the current directory before launch of our application, which  ' sets a hidden environment variable that is considered.  filePath = Path.GetFullPath("D:FY2018")  Console.WriteLine($"'D:FY2018' resolves to {filePath}")   If args.Length < 1 Then  Console.WriteLine("Launching again, after setting current directory to D:FY2018")  Dim currentExe As Uri = New Uri(Assembly.GetExecutingAssembly().GetName().CodeBase, UriKind.Absolute)  Dim commandLine As String = $"/C cd D:FY2018 & ""{currentExe.LocalPath}"" stop"  Dim psi As ProcessStartInfo = New ProcessStartInfo("cmd", commandLine)   Process.Start(psi).WaitForExit()   Console.WriteLine("Sub process returned:")  filePath = Path.GetFullPath("D:FY2018")  Console.WriteLine($"'D:\FY2018' resolves to {filePath}")  filePath = Path.GetFullPath("D:FY2018")  Console.WriteLine($"'D:FY2018' resolves to {filePath}")  End If  Console.WriteLine("Press any key to continue... ")  Console.ReadKey()  End Sub End Module ' The example displays the following output: ' Current directory is 'C:Programsfile-paths' ' Setting current directory to 'C:' ' 'D:FY2018' resolves to D:FY2018 ' 'D:FY2018' resolves to d:FY2018 ' Setting current directory to 'D:Docs' ' 'D:FY2018' resolves to D:FY2018 ' 'D:FY2018' resolves to D:DocsFY2018 ' Setting current directory to 'C:' ' 'D:FY2018' resolves to D:FY2018 ' 'D:FY2018' resolves to d:FY2018 ' Launching again, after setting current directory to D:FY2018 ' Sub process returned: ' 'D:FY2018' resolves to D:FY2018 ' 'D:FY2018' resolves to d:FY2018 ' The subprocess displays the following output: ' Current directory is 'C:' ' Setting current directory to 'C:' ' 'D:FY2018' resolves to D:FY2018 ' 'D:FY2018' resolves to D:FY2018FY2018 ' Setting current directory to 'D:Docs' ' 'D:FY2018' resolves to D:FY2018 ' 'D:FY2018' resolves to D:DocsFY2018 ' Setting current directory to 'C:' ' 'D:FY2018' resolves to D:FY2018 ' 'D:FY2018' resolves to D:FY2018FY2018 

UNC paths

Universal naming convention (UNC) paths, which are used to access network resources, have the following format:

  • A server or host name, which is prefaced by \. The server name can be a NetBIOS machine name or an IP/FQDN address (IPv4 as well as v6 are supported).
  • A share name, which is separated from the host name by . Together, the server and share name make up the volume.
  • A directory name. The directory separator character separates subdirectories within the nested directory hierarchy.
  • An optional filename. The directory separator character separates the file path and the filename.

The following are some examples of UNC paths:

Path Description
\system07C$ The root directory of the C: drive on system07.
\Server2ShareTestFoo.txt The Foo.txt file in the Test directory of the \Server2Share volume.

UNC paths must always be fully qualified. They can include relative directory segments (. and ..), but these must be part of a fully qualified path. You can use relative paths only by mapping a UNC path to a drive letter.

DOS device paths

The Windows operating system has a unified object model that points to all resources, including files. These object paths are accessible from the console window and are exposed to the Win32 layer through a special folder of symbolic links that legacy DOS and UNC paths are mapped to. This special folder is accessed via the DOS device path syntax, which is one of:

\.C:TestFoo.txt
\?C:TestFoo.txt

The DOS device path consists of the following components:

  • The device path specifier (\. or \?), which identifies the path as a DOS device path.

  • A symbolic link to the "real" device object (C: in this case).

    The first segment of the DOS device path after the device path specifier identifies the volume or drive. (For example, \?C: and \.BootPartition.)

    There is a specific link for UNCs that is called, not surprisingly, UNC. For example:

    \.UNCServerShareTestFoo.txt
    \?UNCServerShareTestFoo.txt

    For device UNCs, the server/share portion is forms the volume. For example, in \?server1e:utilities\filecomparer, the server/share portion is server1utilities. This is significant when calling a method such as Path.GetFullPath(String, String) with relative directory segments; it is never possible to navigate past the volume.

DOS device paths are fully qualified by definition. Relative directory segments (. and ..) are not allowed. Current directories never enter into their usage.

Example: Ways to refer to the same file

The following example illustrates some of the ways in which you can refer to a file when using the APIs in the System.IO namespace. The example instantiates a FileInfo object and uses its Name and Length properties to display the filename and the length of the file.

using System; using System.IO;  class Program {  static void Main()  {  string[] filenames = {  @"c:temptest-file.txt",  @"\127.0.0.1c$temptest-file.txt",  @"\LOCALHOSTc$temptest-file.txt",  @"\.c:temptest-file.txt",  @"\?c:temptest-file.txt",  @"\.UNCLOCALHOSTc$temptest-file.txt",  @"\127.0.0.1c$temptest-file.txt" };   foreach (var filename in filenames)  {  FileInfo fi = new FileInfo(filename);  Console.WriteLine($"file {fi.Name}: {fi.Length:N0} bytes");  }  } } // The example displays output like the following: // file test-file.txt: 22 bytes // file test-file.txt: 22 bytes // file test-file.txt: 22 bytes // file test-file.txt: 22 bytes // file test-file.txt: 22 bytes // file test-file.txt: 22 bytes // file test-file.txt: 22 bytes 
Imports System.IO  Module Program  Sub Main()  Dim filenames() As String = {   "c:temptest-file.txt",   "\127.0.0.1c$temptest-file.txt",  "\LOCALHOSTc$temptest-file.txt",   "\.c:temptest-file.txt",  "\?c:temptest-file.txt",  "\.UNCLOCALHOSTc$temptest-file.txt",  "\127.0.0.1c$temptest-file.txt" }   For Each filename In filenames   Dim fi As New FileInfo(filename)   Console.WriteLine($"file {fi.Name}: {fi.Length:N0} bytes")  Next   End Sub End Module 

Path normalization

Almost all paths passed to Windows APIs are normalized. During normalization, Windows performs the following steps:

  • Identifies the path.
  • Applies the current directory to partially qualified (relative) paths.
  • Canonicalizes component and directory separators.
  • Evaluates relative directory components (. for the current directory and .. for the parent directory).
  • Trims certain characters.

This normalization happens implicitly, but you can do it explicitly by calling the Path.GetFullPath method, which wraps a call to the GetFullPathName() function. You can also call the Windows GetFullPathName() function directly using P/Invoke.

Identifying the path

The first step in path normalization is identifying the type of path. Paths fall into one of a few categories:

  • They are device paths; that is, they begin with two separators and a question mark or period (\? or \.).
  • They are UNC paths; that is, they begin with two separators without a question mark or period.
  • They are fully qualified DOS paths; that is, they begin with a drive letter, a volume separator, and a component separator (C:).
  • They designate a legacy device (CON, LPT1).
  • They are relative to the root of the current drive; that is, they begin with a single component separator ().
  • They are relative to the current directory of a specified drive; that is, they begin with a drive letter, a volume separator, and no component separator (C:).
  • They are relative to the current directory; that is, they begin with anything else (temptestfile.txt).

The type of the path determines whether or not a current directory is applied in some way. It also determines what the "root" of the path is.

Handling legacy devices

If the path is a legacy DOS device such as CON, COM1, or LPT1, it is converted into a device path by prepending \. and returned.

A path that begins with a legacy device name is always interpreted as a legacy device by the Path.GetFullPath(String) method. For example, the DOS device path for CON.TXT is \.CON, and the DOS device path for COM1.TXTfile1.txt is \.COM1.

Applying the current directory

If a path isn’t fully qualified, Windows applies the current directory to it. UNCs and device paths do not have the current directory applied. Neither does a full drive with separator C:.

If the path starts with a single component separator, the drive from the current directory is applied. For example, if the file path is utilities and the current directory is C:temp, normalization produces C:utilities.

If the path starts with a drive letter, volume separator, and no component separator, the last current directory set from the command shell for the specified drive is applied. If the last current directory was not set, the drive alone is applied. For example, if the file path is D:sources, the current directory is C:Documents, and the last current directory on drive D: was D:sources, the result is D:sourcessources. These "drive relative" paths are a common source of program and script logic errors. Assuming that a path beginning with a letter and a colon isn’t relative is obviously not correct.

If the path starts with something other than a separator, the current drive and current directory are applied. For example, if the path is filecompare and the current directory is C:utilities, the result is C:utilitiesfilecompare.

Canonicalizing separators

All forward slashes (/) are converted into the standard Windows separator, the back slash (). If they are present, a series of slashes that follow the first two slashes are collapsed into a single slash.

Evaluating relative components

As the path is processed, any components or segments that are composed of a single or a double period (. or ..) are evaluated:

  • For a single period, the current segment is removed, since it refers to the current directory.

  • For a double period, the current segment and the parent segment are removed, since the double period refers to the parent directory.

    Parent directories are only removed if they aren’t past the root of the path. The root of the path depends on the type of path. It is the drive (C:) for DOS paths, the server/share for UNCs (\ServerShare), and the device path prefix for device paths (\? or \.).

Trimming characters

Along with the runs of separators and relative segments removed earlier, some additional characters are removed during normalization:

  • If a segment ends in a single period, that period is removed. (A segment of a single or double period is normalized in the previous step. A segment of three or more periods is not normalized and is actually a valid file/directory name.)

  • If the path doesn’t end in a separator, all trailing periods and spaces (U+0020) are removed. If the last segment is simply a single or double period, it falls under the relative components rule above.

    This rule means that you can create a directory name with a trailing space by adding a trailing separator after the space.

Skipping normalization

Normally, any path passed to a Windows API is (effectively) passed to the GetFullPathName function and normalized. There is one important exception: a device path that begins with a question mark instead of a period. Unless the path starts exactly with \? (note the use of the canonical backslash), it is normalized.

Why would you want to skip normalization? There are three major reasons:

  1. To get access to paths that are normally unavailable but are legal. A file or directory called hidden., for example, is impossible to access in any other way.

  2. To improve performance by skipping normalization if you’ve already normalized.

  3. On the .NET Framework only, to skip the MAX_PATH check for path length to allow for paths that are greater than 259 characters. Most APIs allow this, with some exceptions.

Skipping normalization and max path checks is the only difference between the two device path syntaxes; they are otherwise identical. Be careful with skipping normalization, since you can easily create paths that are difficult for "normal" applications to deal with.

Paths that start with \? are still normalized if you explicitly pass them to the GetFullPathName function.

Note that you can paths of more than MAX_PATH characters to GetFullPathName without \?. It supports arbitrary length paths up to the maximum string size that Windows can handle.

Case and the Windows file system

A peculiarity of the Windows file system that non-Windows users and developers find confusing is that path and directory names are case-insensitive. That is, directory and file names reflect the casing of the strings used when they are created. For example, the method call

Directory.Create("TeStDiReCtOrY"); 
Directory.Create("TeStDiReCtOrY") 

creates a directory named TeStDiReCtOrY. If you rename a directory or file to change its case, the directory or file name reflects the case of the string used when you rename it. For example, the following code renames a file named test.txt to Test.txt:

using System.IO;  class Example {  static void Main()  {  var fi = new FileInfo(@".test.txt");  fi.MoveTo(@".Test.txt");  } } 
Imports System.IO  Module Example  Public Sub Main()  Dim fi As New FileInfo(".test.txt")  fi.MoveTo(".Test.txt")  End Sub End Module 

However, directory and file name comparisons are case-insensitive. If you search for a file named "test.txt", .NET file system APIs ignore case in the comparison. Test.txt, TEST.TXT, test.TXT, and any other combination of upper- and lowercase letters will match "test.txt".

You May Also Like

About the Author: admind

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

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

Adblock
detector