Почему "set -P" не работает после канала? - PullRequest
9 голосов
/ 10 августа 2010
C:\>type c:\output.txt
abcd
C:\>type c:\output.txt | set /p V1=

C:\>set
... A bunch of junk, NOT seeing "V1"

Что случилось? Согласно всей документации для SET, которую я видел,% V1% должно было быть присвоено значение "abcd" из вышеприведенного, нет?

Я на Windows XP Pro, SP3, если это имеет значение.

Ответы [ 3 ]

12 голосов
/ 29 декабря 2010

Похоже, что канал создает новый экземпляр CMD для выполнения следующей команды, которая получает данные канала.Поэтому, когда канал завершен, этот экземпляр CMD завершается, а переменная теряется.

3 голосов
/ 10 августа 2010

Я не знаю, что doco вы видели для команды set, но вывод set /? ясно говорит:

Переключатель / P позволяет установить значение переменной в строку ввода, введенную пользователем .

(мой курсив). Я думаю, что set /p получает свой ввод с консоли независимо от того, что вы пытаетесь передать через стандартный ввод. Почему это не ждет, я не уверен. echo xxx | set /p xx= также не может установить переменную.

Но, если вы хотите установить переменную из однострочного файла, вы можете просто использовать один из них:

for /f "delims=" %%i in (c:\output.txt) do set V1=%%i
set /p V1=<c:\output.txt

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

Первая позволяет выполнять произвольные команды без временных файлов:

for /f "delims=" %%i in ('echo AAA') do set xx=%%i

На этой странице есть интересный фрагмент, который предполагает, что он имеет отношение к контекстам:

Хорошо, я сам узнал почему. Это потому, что | создает новый контекст, поэтому переменная никогда не выделяет его до остальной части текущего контекста. Доказательство:
> set bar=
> echo aaa | (set /p bar= && set bar)
bar=aaa
> set bar
Environment variable bar not defined

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

2 голосов
/ 17 декабря 2015

Это не прямой ответ на исходный вопрос, который задавался Why doesn't [..] work?, но этот ответ может быть полезен для людей, ищущих & # x332; c & # x332; h & # x332; i & # x332; e & # x332; v & # X332; е & # X332; что будет логический результат примерно так:

echo:somevalue|set /p somevar=

Обычный обходной путь, например, для кода выше, будет:

echo:somevalue>tempfile.txt&set /p somevar=<tempfile.txt

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

Хотя это правда, никак не может использовать поток файлов для преодоления разрыва по обеим сторонам оператора | канала в том, что касается переменных среды, находясь в среде Windows NT. и при условии том, на котором находится скрипт, использует файловую систему NTFS, можно использовать что-то гораздо более элегантное, чем временные файлы: альтернативные потоки данных

Давайте рассмотрим пример, иллюстрирующий, как их можно использовать:

echo:somevalue>".\%~nx0":temp&set /p somevar=<".\%~nx0":temp

Здесь %~nx0 обозначает и расширяется до имени файла (базовое имя + расширение) нашего пакетного файла, заключенного в кавычки, если он содержит пробелы.

Примечание: В то время как можно использовать %0 напрямую (без кавычек) вместо того, чтобы ссылаться на текущий скрипт .\%~nx0 более управляем, если вам когда-либо понадобится посмотрите на расширенное значение команды при отладке вашего скрипта, особенно если полный путь вашего скрипта довольно длинный.

Таким образом, ".\%~nx0":temp относится к альтернативному потоку данных (ADS) нашего собственного сценария с именем temp, например myscript.cmd:temp, который мы создаем (или перезаписать) выводом команды echo. Поскольку ADS ведет себя так же, как поток файла по умолчанию, команды echo и set не видят никакой разницы.

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

@echo off
set __self=".\%~nx0"
set __adsname=test.dat

:: Using some input...
set l_input=@FirewallAPI.dll,-23000
echo:Input: %l_input%
echo:

:: ...we process it...
expand_str_res_id.exe %l_input%>%__self%:%__adsname%&set /p l_output=< %__self%:%__adsname%

:: ...and show the output, e.g. "File and Printer Sharing"
echo:Output: %l_output%
echo:

:cleanup
set l_input=
set l_output=
type nul> %__self%:%__adsname%
set __self=
set __adsname=
pause

Здесь, в конце скрипта, строка type nul> %__self%:%__adsname, которая может быть расширена до чего-то вроде type nul> ".\myscript.cmd":test.dat, используется для очистки содержимого альтернативного потока данных, который мы только что использовали. Хотя при этом размер альтернативного потока данных устанавливается равным нулю, он не удаляет его (см. Примечания ниже).

Некоторые заключительные замечания:

  1. Большинство команд не могут понимать или работать с альтернативными потоками данных, если они предоставлены как исходные файлы . Это означает, что следующие не могут быть использованы:

    • type, например type myscript.cmd:data > myscript_data.txt
    • copy, например copy myscript.cmd:data myscript_data.txt
    • move, например move myscript.cmd:data myscript_data.txt
    • del / erase, например del myscript.cmd:data
    • ren / rename, например ren myscript.cmd:data myscript.cmd:data.txt
    • и т. Д.

    На самом деле, это действительно только перенаправление файлов, которое поддерживает альтернативные потоки данных с одним исключением (о котором я могу думать): команда start.

  2. Хотя мы можем довольно легко очистить / стереть содержимое альтернативного потока данных, удалить один довольно сложно, так как копирование файла или его переименование не требуют каких-либо потоков (ну, в общем, :$DATA) и редактирование / изменение содержимого файла влияет только на поток данных по умолчанию. Тем не менее, у нас есть несколько вариантов в зависимости от того, чего мы пытаемся достичь:
    • Если у нас есть только один альтернативный поток данных или , не против удалить все из них сразу, а содержимое файла - только текст, тогда мы можем создать новый файл, сохранив только поток данных по умолчанию исходного файла, используя следующую команду type myscript.cmd > myscript_clean.cmd, после чего исходный файл можно удалить.
    • Если у нас есть права администратора для тома и , работающего в Windows Vista или более поздней версии ОС, мы можем использовать MKLINK для создания символической ссылки на one альтернативный поток данных, который затем предоставит нам стандартное имя файла, которое будет использоваться с обычными командами управления файлами.
    • В качестве альтернативы можно использовать один из множества доступных инструментов для управления альтернативными потоками данных, например Streams , LADS или AlternateStreamView .
  3. Полезная команда для вывода списка обычных файлов , включая альтернативные потоки данных из командной строки: dir /a-d /r. Затем вы можете использовать любую программу для доступа к потоку имен (альтернатив) данных файла, например, notepad.exe myscript.cmd:data

Надеюсь, это поможет!

...