2010-08-10 1 views
8
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» из вышеизложенного, нет?Почему «set -P» не работает после трубы?

Я нахожусь в Windows XP Pro, SP3, если это имеет значение.

ответ

11

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

+0

По совпадению, мне нужно было взять номер из одной программы, выполнить некоторую математику, а затем передать ее другому для использования. Это, похоже, делает трюк: echo 5 | (set/P TmpVar = && set/a TmpVar/2)> C: \ test.txt – TonyM

+1

+1; В действительности проблема связана с тем, как Windows реализует трубы. См. [Почему задержка с расширением заканчивается, если внутри блок кода кода?] (Http://stackoverflow.com/q/8192318/1012053), чтобы получить полное объяснение механизма, а также множество интересных побочных эффектов. – dbenham

3

Я не знаю, что 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 

Там интересный фрагмент кода на this page, который предполагает, что это связано с контекстом:

Хорошо, я узнал, почему я сам. Это связано с тем, что | создает новый контекст, поэтому переменная никогда не выводит ее на остальную часть текущего контекста. Доказательство:
> set bar=

bar=aaa
> set bar
Environment variable bar not defined

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

+0

Извините, я не делал никакого пакетного кодирования более чем за 10 лет, и поэтому моя голова загрязнена понятиями Unix, что STDIN является STDIN ...Я думаю, вы правы, что «по пользователю» по умолчанию не является синонимом «STDIN» в DOS-мире (да, я знаю про вонючие средства обнаружения TTY, некоторые программы Unix тянут, но это редкость и трудно добиться исключения :) Мне нужно будет больше узнать об этих контекстах (на самом деле видел эту страницу вчера вечером, но слишком устал, чтобы разобрать ее полностью). – DVK

+1

Ну, я думаю, вы на самом деле правы, @DVK, несмотря на мои первоначальные абзацы в приведенном выше ответе. _some_ тип STDIN, так как 'set/p V1 = paxdiablo

+1

Проблема не имеет ничего общего с командой SET и всем, что нужно сделать с тем, как Windows реализует трубы. См. ответ TonyM, а также ссылку, которую я вложил в нее. – dbenham

1

Это не прямой ответ на исходный вопрос, который просил Why doesn't [..] work?, но этот ответ может быть полезным для людей, следящих к ̲ с ̲ ч ̲ я ̲ х ̲ v ̲ х ̲ что будет логическим результатом что-то вроде этого:

echo:somevalue|set /p somevar= 

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

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

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

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

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

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

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

Примечание: Хотя можно было бы использовать %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 [email protected],-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 создать символическую ссылку на один альтернативный поток данных, который будет предоставлять нам с стандартное имя файла, которое будет использоваться с обычными командами управления файлами.
    • В качестве альтернативы можно использовать один из многих инструментов, доступных для управления чередующимися потоками данных, например Streams, LADS или AlternateStreamView.
  3. Полезные в список обычных файлов включая альтернативные потоки данных из командной строки dir /a-d /r. Затем вы можете использовать любую программу для доступа к потоку данных имен (альтернативных) файла, например. notepad.exe myscript.cmd:data

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