Есть ли способ предотвратить процентное расширение переменной env в командной строке Windows?

Я использую следующую команду git в git bash в Windows:

git log --format="%C(cyan)%cd%Creset %s" --date=short -5

Отображает дату принятия (%cd) с последующим сообщением фиксации (%s). Дата фиксации оборачивается цветными маркерами: %C(cyan) начать цветной вывод и %Creset остановить цветной вывод.

Хотя он хорошо работает в Git Bash, он не очень хорошо с cmd: %cd% расширяется оболочкой Windows в текущий рабочий каталог (эквивалент $PWD в баш).

Следовательно, когда эта команда запускается через cmd Я вижу текущий рабочий каталог, отображаемый вместо даты фиксации в первом столбце! Git Bash:

2015-10-08 commit msg
2015-10-08 commit msg
2015-10-07 commit msg
2015-10-06 commit msg
2015-10-06 commit msg

CMD:

D:\git\someFolderCreset commit msg
D:\git\someFolderCreset commit msg
D:\git\someFolderCreset commit msg
D:\git\someFolderCreset commit msg
D:\git\someFolderCreset commit msg

На самом деле, я никогда не использую cmd непосредственно я обнаружил такое поведение при написании сценария nodejs (0.12), в котором

require('child_process').execSync('git log --format=...', {stdio: 'inherit'})

который выполняется узлом, используя cmd когда на винде).

Простой обходной путь может заключаться в том, чтобы ввести пространство для предотвращения %cd% быть найденным, т.е. изменить

git log --format="%C(cyan)%cd%Creset %s" --date=short -5

в

git log --format="%C(cyan)%cd %Creset%s" --date=short -5

Однако это ввело избыточное пространство (я удалил еще один пробел перед %s но это все же взлом, и требует ручного вмешательства).

Есть ли способ предотвратить расширение оболочкой Windows?

Я нашел информацию об использовании %% или же ^% чтобы избежать % но они не являются решением здесь:

# produces superfluous ^ characters
git log --format="%C(cyan)^%cd^%Creset %s" --date=short -5

^2015-10-08^ commit msg
^2015-10-08^ commit msg
^2015-10-07^ commit msg
^2015-10-06^ commit msg
^2015-10-06^ commit msg

# seems the expansion is done at command parse time
git log --format="%C(cyan)%%cd%%Creset %s" --date=short -5

%D:\git\someFolder commit msg
%D:\git\someFolder commit msg
%D:\git\someFolder commit msg
%D:\git\someFolder commit msg
%D:\git\someFolder commit msg

Идеальное решение должно быть либо совместимым с bash и cmd, без создания избыточных символов, либо с функцией escape в javascript, чтобы экранировать универсальную команду UNIX-y для Windows, чтобы предотвратить расширения (если такая функция escape может быть создана).

2 ответа

Решение

Чтобы предоставить альтернативу полезному ответу MC ND:

Если вам действительно нужно включить оболочку (что маловероятно, потому что вы заявляете, что хотите, чтобы команда работала как с Windows' cmd.exe и Bash), рассмотрите решение сразу ниже; для альтернативы без оболочки, которая обходит проблему, см. решение внизу.

Наконечник шляпы MC ND для улучшения моего оригинального подхода, предлагая поместить двойные кавычки вокруг % экземпляры, а не имена потенциальных переменных между % случаи, и для предложения разъяснения о execFileSync ,


"Высвобождение" % символы. за cmd.exe

Как указано в ответе MC ND, вы не можете технически избежать % в командной строке Windows (внутри командных файлов вы можете использовать %%, но это не работает при вызове команд оболочки из других сред, таких как Node.js, и обычно не работает на разных платформах).

Тем не менее, обходной путь заключается в размещении двойных кавычек вокруг каждого % экземпляр:

// Input shell command.
var shellCmd = 'git log --format="%C(cyan)%cd%Creset %s" --date=short -5'

// Place a double-quote on either end of each '%'
// This yields (not pretty, but it works):
//   git log --format=""%"C(cyan)"%"cd"%"Creset "%"s" --date=short -5
var escapedShellCmd = shellCmd.replace(/%/g, '"%"')

// Should work on both Windows and Unix-like platforms:
console.log(require('child_process').execSync(escapedShellCmd).toString())

Вставленные двойные кавычки предотвращают cmd.exe от распознавания токенов, таких как %cd% как ссылки на переменные ("%"cd"%" не будет расширен).

Это работает, потому что лишние двойные кавычки в конечном итоге удаляются из строки при обработке целевой программой:

  • Windows: git.exe (предположительно через среду выполнения C), затем заботится об извлечении лишних двойных кавычек из объединенной строки.

  • Unix-подобные (POSIX-подобные оболочки, такие как Bash): сама оболочка заботится об удалении двойных кавычек перед передачей их целевой программе.

    • Предостережение: использование двойных кавычек в вашей команде в целом означает, что вам нужно следить за POSIX-подобными оболочками, выполняющими потенциально нежелательные расширения на $ -префиксированные токены (здесь не проблема); однако, чтобы оставаться Windows-совместимым, вы должны использовать двойные кавычки.

Технически, применение этого метода к строке в двойных кавычках разбивает ее на последовательность подстрок в двойных кавычках, перемежающихся с кавычками % экземпляров. POSIX-подобные оболочки по-прежнему распознают это как одну строку - подстроки заключаются в двойные кавычки и непосредственно примыкают к % экземпляров. (Если вы примените технику к строке без кавычек, логика обратная: вы эффективно объединяете в двойных кавычках % экземпляры.) Двойные кавычки вокруг подстрок, которые считаются синтаксическими элементами, а не частью строки, затем удаляются, когда подстроки соединяются вместе, чтобы сформировать один литерал для передачи целевой программе.


Обойти проблему, избегая оболочки вообще

Примечание: следующее основано на execFile[Sync], который работает только для вызова внешних исполняемых файлов (что верно в случае OP: git.exe) - напротив, для вызова встроенных команд оболочки (внутренних команд) или пакетных файлов Windows нельзя избежать exec[Sync] и, таким образом, интерпретация cmd.exe (на Windows). [1]

Если вы используете execFileSync скорее, чем execSync оболочка ( cmd.exe в Windows) НЕ будут задействованы, поэтому вам не нужно беспокоиться о побеге % символы. или любые другие метасимволы оболочки, в этом отношении:

require('child_process').execFileSync('git', 
   [ 'log', 
     '--format=%C(cyan)%cd%Creset %s',
     '--date=short',
     '-5' ], {stdio: 'inherit'})

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


[1] В Windows файлы сценариев (например, сценарии Python) не могут быть вызваны напрямую execFile[Sync] , но вы можете вместо этого передать исполняемый файл интерпретатора (например, python ) в качестве файла для выполнения и файла сценария в качестве аргумента. На Unix-подобных платформах вы можете вызывать сценарии напрямую, если они имеют строку shebang и помечены как исполняемые.
execFile[Sync] может использоваться для вызова командных файлов Windows, но cmd.exe неизменно интерполирует аргументы, как с exec[Sync] ,

Короче, Вы не можете сделать это или, по крайней мере, нет общего метода

обратите внимание, я был неправ. Ответ mklement0 указал путь для общего решения.

Конечно, вы можете использовать ^не для того, чтобы избежать знака процента (вы не можете избежать знака процента на уровне командной строки), а для отделения знака процента от имени переменной, чтобы он не был обнаружен в качестве раскрываемой переменной. То есть для вашего случая достаточно использовать

git log --format="%C(cyan)%cd^%Creset %s" --date=short -5

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

Таким образом, любые "escape" символы будут включены в вывод как в cmd а также bash, но это может быть решено для этого конкретного (или аналогичного) случая, используя

process.env.cd = '%cd%'
require('child_process').execSync(
    'git log --format="%C(cyan)%cd%Creset %s" --date=short -5'
    , {stdio: 'inherit'}
)

По сути, код выполняет назначение поддельного значения cd переменная окружения, меняя ее на литерал %cd%Итак, когда cmd parser выполняет раскрытие переменной, правильное значение заканчивается в командной строке.

Но, может быть (я не проверял, как / что git код делает) изменение cd переменная может мешать. Таким образом, также для этого случая, вместо изменения значения cd переменная, которую вы можете использовать

process.env['C(cyan)'] = '%C(cyan)%';

Зачем? Когда cmd анализатор обрабатывает командную строку, он сравнивает начало строки формата с существующей переменной, выполняет расширение и в том же процессе использует начальный знак процента в %cd% переменная, поэтому она не расширяется.

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