Как преобразовать определенные символы в Unicode из BATCH/Powershell

У меня есть код для BATCH

@echo off
setlocal enabledelayedexpansion
for /d %%a in ("%cd%") do set "directory_name=%%~nxa"
powershell -Command "$content = [IO.File]::ReadAllText('file.txt'); $content = $content -replace '\s\u003Cmod((?:.|\n)).*%directory_name: =_%_v((?:.|\n))*?\u003C/mod\u003E', ''; [IO.File]::WriteAllText('file.txt', $content, [System.Text.Encoding]::UTF8)" 
endlocal
pause

Он работал нормально и делал все, что я хотел. Пока я не получил имя папки blabla_-_blabla's_bla. Итак, апостроф теперь является нарушением синтаксиса PowerShell и не может быть завершен. Но... если я пытаюсь изменить символ апострофа на Юникод \u0027, все работает нормально.

любая идея, как преобразовать через BATCH любые конкретные символы в Юникод (исключая латиницу, «_» и «-») перед вводом в код PowerShell через %directory_name: =_% ?

Что позволяет вам думать, что PowerShell, являющийся гораздо более мощным преемником командного процессора Windows, не способен получить имя текущего рабочего каталога? Абсолютно нет необходимости обрабатывать пакетный файл cmd.exe просто для того, чтобы получить имя текущего рабочего каталога со всеми ограничениями cmd и передать это имя каталога в PowerShell в командной строке. Посмотрите результаты поиска по переполнению стека [powershell] получите текущее имя каталога.

Mofi 21.08.2024 13:38

Мы называем это XY-проблемой. Отложенное расширение переменной вообще не требуется, но приводит к неправильной интерпретации имени каталога с одним или несколькими !. Опция ЗА /D вообще не нужна. Ссылка на динамическую переменную CD также не нужна, поскольку . тоже можно использовать. Текущим каталогом может быть любой каталог при запуске cmd.exe для обработки пакетного файла, как его определяет родительский процесс. Текущий каталог не должен быть каталогом, содержащим пакетный файл. Пакетный файл вообще не нужен, поскольку Powershell имеет все необходимые функции.

Mofi 21.08.2024 13:42

Было бы лучше отредактировать ваш вопрос и описать, что следует делать с помощью PowerShell, и эксперт по кодированию PowerShell обязательно напишет единственную командную строку, необходимую для выполнения работы, используя только PowerShell (и файл ярлыка с этой единственной командной строкой) .

Mofi 21.08.2024 13:46

это не XY. У меня есть большой BATCH-скрипт, который делает много вещей. Поэтому я отделил место кода проблемы. Я не думаю, что у кого-то есть время прочитать весь код, понять его и провести рефакторинг. У меня есть конкретная проблема, которую, я надеюсь, кто-нибудь знает, как решить. Вы написали стену текста и ничего полезного. Пустая трата времени

Aleksandr Podaruev 21.08.2024 16:36

Применить стандарт. Правило цитирования: используйте $content -replace ""\s\u003Cmod((?:.|\n)).*%directory_name: =_%_v((?:.|\n))*?\u003C/mod\u003E"", ''; вместо $content -replace '\s\u003Cmod((?:.|\n)).*%directory_name: =_%_v((?:.|\n))*?\u003C/mod\u003E', '';. Протестировано с использованием powershell -noprofile -Command "write-output ""\s\u003Cmod((?:.|\n)).*%directory_name: =_%_v((?:.|\n))*?\u003C/mod\u003E""" (хорошо) и (неправильно) powershell -noprofile -Command "write-output '\s\u003Cmod((?:.|\n)).*%directory_name: =_%_v((?:.|\n))*?\u003C/mod\u003E'", что приводит к сообщению об ошибке The string is missing the terminator: '.

JosefZ 21.08.2024 17:03

Кстати, в целях тестирования использовался жестко запрограммированный set "directory_name=blabla - blabla's bla"

JosefZ 21.08.2024 17:04

Просто используйте powershell.

js2010 21.08.2024 19:18

@JosefZ спасибо Перед вашим комментарием я нашел решение для двойных кавычек. Я попробовал ваш пример и получил ошибку: The string is missing the terminator: ". Чтобы исправить проблему, это должно быть так $content -replace ""\s\u003Cmod((?:.|\n)).*%directory_name: =_%_v((?:.|\n))*?\u003C/mod\u003E"", """"; Но это не решает проблему с правилом регулярного выражения. Одинарная кавычка нарушает правило регулярного выражения, а «-replace» не делает того, что должно. Я имею в виду, что это не замена, потому что имя «blabla's bla» содержит эту цитату.

Aleksandr Podaruev 22.08.2024 11:55
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
8
63
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Предисловие:

  • Показанный ниже метод надежно передает значение, хранящееся в переменной пакетного файла, вызову PowerShell CLI (с использованием либо powershell.exe (Windows PowerShell), либо pwsh.exe для PowerShell (Core) 7). , какие бы символы он ни содержал, поэтому не требуется никаких предварительных знаний об этом (но см. предостережение о кодировке символов в нижнем разделе).

  • Ситуативно, если вы знаете, что значение никогда не содержит ", но может содержать ', как в рассматриваемом случае, переключение с использования встроенного '...' кавычек (одинарных кавычек) на использование встроенного "..." кавычек (двойных кавычек) в сочетании с предварительным как отмечает JosefZ, возможна интерполяция строк с помощью cmd.exe; однако:

    • Вам нужно не только помнить о том, как на литеральные части такой встроенной строки может влиять строковая интерполяция PowerShell в таких расширяемых (интерполирующих) строках ("..."), предварительно развернутом значении сам по себе может стать объектом нежелательной интерпретации, такой как удаление символов ` и непреднамеренное расширение токенов с префиксом $.

    • Надежная передача встроенной строки "..." внутри общей строки "...", содержащей код PowerShell, переданный в -Command из пакетного файла (cmd.exe), является громоздким, поскольку потенциально возможно нарушение правил синтаксического анализа cmd.exe. См. этот ответ для объяснения и обходных путей.


Чтобы избежать головной боли при цитировании и экранировании, сделайте ссылочные переменные PowerShell, установленные в пакетных файлах, в качестве переменных среды (все переменные, установленные в пакетных файлах, неизменно также являются переменными среды и, следовательно, видны дочерним процессам).
То есть вместо того, чтобы использовать предварительную интерполяцию строк cmd.exe через ссылки, такие как %directory_name%, встроенные в команду, передаваемую -Command, сделайте так, чтобы команда ссылалась на значение переменной пакетного файла directory_name следующим образом, используя синтаксис PowerShell для доступа к переменные среды:

$env:directory_name

В вашем случае это также упрощает применение [regex]::Escape() к значению переменной, что необходимо для обеспечения того, чтобы значение обрабатывалось буквально внутри регулярного выражения , которое вы передаете -replace (обратите внимание, что $env:directory_name -replace ' ', '_' является отложенным эквивалентом замена пробелов на _ посредством предварительной интерполяции строки на cmd.exe, %directory_name: =_%):

[regex]::Escape(($env:directory_name -replace ' ', '_'))
  • В рассматриваемом случае, когда интересующим значением является имя текущего рабочего каталога, вы также можете позволить PowerShell определить его:

    [regex]::Escape(((Split-Path -Leaf $PWD) -replace ' ', '_'))
    

Поскольку вы используете '...' (одинарные кавычки) для регулярного выражения, передаваемого операции $content -replace ..., вы должны объединить результат приведенного выше выражения в эту строку посредством конкатенации строк (+) и заключить операцию в (...), чтобы уточнить приоритет операторов:

$content -replace ('\s\u003Cmod((?:.|\n)).*' + [regex]::Escape(($env:directory_name -replace ' ', '_')) + '_v((?:.|\n))*?\u003C/mod\u003E'), ''

Чтобы собрать все это вместе, используя упрощенную версию командного файла:

@echo off
setlocal

:: Get the name of the current directory.
for %%a in (.) do set "directory_name=%%~nxa"

:: Invoke PowerShell and make it obtain the value of %directory_name% 
:: via $env:directory_name
powershell -Command "$content = [IO.File]::ReadAllText('file.txt'); $content = $content -replace ('\s\u003Cmod((?:.|\n)).*' + [regex]::Escape(($env:directory_name -replace ' ', '_')) + '_v((?:.|\n))*?\u003C/mod\u003E'), ''; [IO.File]::WriteAllText('file.txt', $content, [System.Text.Encoding]::UTF8)" 

pause

Общее предостережение относительно кодировки символов пакетного файла (поскольку в заголовке упоминается Unicode):

  • Чтобы также поддерживать символы, отличные от ASCII, убедитесь, что кодировка символов вашего командного файла соответствует кодовой странице активной консоли, как сообщает chcp.com

  • Для полной поддержки Unicode через UTF-8:

    • Сохраните командные файлы в формате UTF-8 без спецификации.

    • Переключите активную кодовую страницу окна консоли на 65001 (UTF-8):

      • Примечание:

        • В Windows 10 и выше есть возможность переключиться на UTF-8 для всей системы на постоянной основе, но это имеет далеко идущие последствия — см. этот ответ.

        • Специальное переключение на UTF-8 влияет не только на данный пакетный файл, но и на другие процессы, которые могут запускаться позже в том же окне консоли. Если это нежелательно, необходимо сохранить исходную кодовую страницу и восстановить ее позже.

      • В сеансах оболочки cmd.exe либо запустите chcp 65001 перед вызовом командного файла, либо поместите его сразу после @echo off в командном файле (как уже отмечалось, это также влияет на процессы, запускаемые позже в том же окне консоли).

        • Если вы хотите, чтобы все будущие cmd.exe сеансы по умолчанию использовали UTF-8 (без вышеупомянутого общесистемного переключения), вы можете использовать команду configure для запуска chcp 65001 каждый раз при создании cmd.exe процесса:

          reg.exe add "HKCU\Software\Microsoft\Command Processor" /v AutoRun /d "chcp 65001 >NUL"
          
          • Обратите внимание на использование >NUL для отключения вывода chcp.com; удалите его, если предпочитаете напоминание о том, что команда запускалась при запуске.

          • Конфигурация специфична для текущего пользователя. Если у вас есть права администратора, вы можете альтернативно выбрать куст HKLM вместо HKCU из сеанса с повышенными правами, чтобы настроить поведение для всех пользователей.

      • В сеансах PowerShell использование chcp 65001 невозможно, поскольку .NET кэширует кодировки и не уведомляется об изменении.
        Вместо этого используйте следующее магическое заклинание (оно неявно устанавливает кодовую страницу на 65001, одновременно информируя .NET об изменении; подробности см. в этом ответе):

        $OutputEncoding = [Console]::InputEncoding = [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()
        
        • Если вы хотите, чтобы для всех будущих сеансов PowerShell по умолчанию использовалась UTF-8 (без вышеупомянутого общесистемного переключения), вы можете добавить указанное выше в свой файл $PROFILE.

        • Файл $PROFILE относится к текущему пользователю и хост-программе (обычно это окно консоли). Альтернативно, вы можете сохранить команду в файле $PROFILE.CurrentUserAllHosts, чтобы настроить таргетинг на все хосты текущего пользователя, и — при условии, что у вас есть права администратора — в аналогичных файлах $PROFILE.AllUsersCurrentHost и $PROFILE.AllUsersAllHosts, предназначенных для всех пользователей, из сеанса с повышенными правами.

$content -replace ('\s\u003Cmod((?:.|\n)).' + [regex]::Escape(($env:directory_name -replace ' ', '_')) + '_v((?:.|\n))*?\u003C/mod\u003E'), '' это тоже работает, но ты забыл * характер, так и должно быть $content -replace ('\s\u003Cmod((?:.|\n)).*' + [regex]::Escape(($env:directory_name -replace ' ', '_')) + '_v((?:.|\n))*?\u003C/mod\u003E'), ''
Aleksandr Podaruev 22.08.2024 11:53

Спасибо за указание на это, @AleksandrPodaruev - я исправил ответ.

mklement0 22.08.2024 12:25

Другие вопросы по теме