2010-08-10 9 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" 

¿Qué pasó? De acuerdo con toda la documentación para SET que he visto,% V1% debería tener asignado un valor de "abcd" en el de arriba, ¿no?¿Por qué no funciona "set -P" después de una tubería?

Estoy en Windows XP Pro, SP3 si es importante.

Respuesta

11

La tubería parece crear una nueva instancia de CMD para llevar a cabo el siguiente comando que recibe los datos de la tubería. Entonces, cuando la tubería ha concluido, esa instancia de CMD se cierra y la variable se pierde.

+0

Casualmente, necesitaba tomar un número del estándar de un programa, hacer algunos cálculos sobre él y luego canalizarlo a otro para el uso. Esto parece hacer el truco: echo 5 | (set/P TmpVar = && set/a TmpVar/2)> C: \ test.txt – TonyM

+1

+1; De hecho, el problema es el resultado de cómo Windows implementa tuberías. Ver [¿Por qué falla la expansión retrasada cuando está dentro de un bloque de código procesado?] (Http://stackoverflow.com/q/8192318/1012053) para obtener una explicación completa del mecanismo, junto con muchos efectos secundarios interesantes. – dbenham

3

No sé lo que mana que has visto para el comando set pero la salida de set /? establece claramente:

El modificador/P le permite establecer el valor de una variable a una línea de entrada ingresada por el usuario.

(mi cursiva). Creo que set /p está obteniendo su entrada desde la consola, independientemente de lo que esté intentando canalizar a través de la entrada estándar. Por qué no está esperando, no estoy seguro. echo xxx | set /p xx= tampoco establece la variable.

Pero, si desea establecer una variable de un archivo de una sola línea, sólo puede utilizar uno de estos:

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

Ese segundo es el más simple pero no ayuda mucho si quieres para obtener el resultado de un comando arbitrario, pero es posible que primero tenga que dirigirlo a un archivo.

El primero le permite ejecutar comandos arbitrarios sin archivos temporales:

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

Hay un fragmento interesante sobre this page lo que sugiere que tiene que ver con contextos:

Ok, descubrí por qué yo mismo. Es porque el | crea un nuevo contexto por lo que la variable nunca llega al resto del contexto actual. Prueba:
> set bar=

bar=aaa
> set bar
Environment variable bar not defined

aunque declino hacer comentarios sobre la veracidad de esa conclusión. No sé cuáles son los contextos en este sentido, solo lo llamo la atención para que esté completo.

+0

Lo siento, no he hecho ninguna codificación por lotes en más de 10 años y por lo tanto mi cabeza está contaminada por las nociones de Unix de que STDIN es STDIN ...Supongo que tienes razón de que "por el usuario" no es sinónimo de "STDIN" en el mundo de DOS de manera predeterminada (sí, sé algo de detección de TTY wanky que algunos programas de Unix extraen, pero esa es una excepción rara y difícil de lograr :) Tendré que leer más sobre estos contextos (en realidad vi esa página anoche pero estaba demasiado cansada para analizarla por completo. – DVK

+1

Bueno, creo que tienes razón, @DVK, a pesar de mis párrafos originales en la respuesta anterior. Está claro _some_ tipo de STDIN ya que 'set/p V1 = paxdiablo

+1

El problema no tiene nada que ver con el comando SET y todo que hacer con la forma en que Windows implementa las tuberías. Consulte la respuesta de TonyM, así como el enlace donde puse un comentario allí. – dbenham

1

Esta no es una respuesta directa a la pregunta original, que pidió Why doesn't [..] work?, pero esta respuesta puede ser útil para las personas que buscan un ̲ c ̲ h ̲ i ̲ e ̲ v ̲ e ̲ lo que sería el resultado lógico de algo como esto:

echo:somevalue|set /p somevar= 

la solución normal, por ejemplo para el código anterior, sería la siguiente:

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

que hace el trabajo a la perfección, excepto que crea un nuevo archivo que debe tratarse posteriormente (además del hecho de que debemos tomar medidas adicionales para asegurarnos de que este nuevo archivo no sobrescriba uno existente) no creamos o nunca intentamos reemplazar).

Si bien es cierto que es ninguna manera alrededor de usar una secuencia de archivo para cerrar la brecha a través de ambos lados del operador tubería | en lo que se refiere a las variables de entorno, estar bajo un entorno Windows NT y proporcionan el volumen del guión reside en que se utiliza el sistema de archivos NTFS, se puede usar algo mucho más elegante que los archivos temporales: salto alternate data streams

Vamos a la derecha en un ejemplo que ilustra cómo se podría usarlos:

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

Aquí %~nx0 significa, y se amplía a, el nombre de archivo (nombre base + extensión) de nuestro archivo de proceso por lotes, entre comillas en caso de que contenga espacios.

Nota: Si bien se podría utilizar %0 directamente (sin comillas) en lugar de referirse a el script actual, .\%~nx0 es más manejable si alguna vez tiene que mirada en el valor expandido de la orden durante la depuración su script, especialmente si la ruta completa de su script es bastante larga.

Como tal, ".\%~nx0":temp se refiere a una secuencia de datos alternativa (ADS) de nuestro script propia llamado temp, por ejemplo myscript.cmd:temp, que creamos (o sobrescribir) con la salida del comando echo. Como los ADS se comportan de la misma manera que la transmisión predeterminada de un archivo, los comandos echo y set no ven ninguna diferencia.

Un guión completo utilizando el método podría tener este aspecto:

@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 

Aquí, al final de la secuencia de comandos, la línea type nul> %__self%:%__adsname, que podría ampliarse a algo así como type nul> ".\myscript.cmd":test.dat, se utiliza para borrar el contenido de la secuencia de datos alternativa que acabamos de utilizar. Aunque esto establece el tamaño de la secuencia de datos alternativa en cero, lo hace no borrarlo (ver notas a continuación).

Algunas notas finales:

  1. mayoría de los comandos no pueden entender o trabajar con flujos de datos alternativos cuando se proporcionan como archivos de origen . Esto significa que no se pueden usar los siguientes:

    • type, p. type myscript.cmd:data > myscript_data.txt
    • copy, p. Ej. copy myscript.cmd:data myscript_data.txt
    • move, p. Ej. move myscript.cmd:data myscript_data.txt
    • del/erase, p. del myscript.cmd:data
    • ren/rename, p. ren myscript.cmd:data myscript.cmd:data.txt
    • y así sucesivamente

    De hecho, es realmente sólo la redirección de archivos que soporta flujos de datos alternativos con una excepción (que se me ocurre): el comando start.

  2. Aunque podemos con bastante facilidad claro/borrar el contenido de una secuencia de datos alternativa, eliminando uno es bastante complicado ya que la copia de un archivo o el cambio de nombre no implica ningún corrientes (así :$DATA unos), y la edición/cambio del el contenido de un archivo solo afecta la secuencia de datos predeterminada. Sin embargo, tenemos algunas opciones dependiendo de lo que estamos tratando de lograr:
    • Si sólo tenemos un único flujo de datos alternativo o no les importa la eliminación de todas de ellos a la vez, y el contenido del archivo es solo texto, entonces podemos crear un nuevo archivo manteniendo solo la secuencia de datos predeterminada del archivo original usando el siguiente comando type myscript.cmd > myscript_clean.cmd, luego de lo cual se puede eliminar el archivo original.
    • Si tenemos derechos administrativos en el volumen y funcionan bajo Windows Vista o una versión posterior del sistema operativo, podemos utilizar MKLINK para crear un enlace simbólico a uno secuencia de datos alternativa, que a su vez nos proporcionará una nombre de archivo estándar para ser utilizado con los comandos habituales de gestión de archivos.
    • Alternativamente, uno puede usar una de las muchas herramientas disponibles para manipular flujos de datos alternativos, como Streams, LADS o AlternateStreamView.
  3. Un comando útil para listar los archivos normales incluyendo secuencias de datos alternativas desde la línea de comandos es dir /a-d /r. A continuación, puede usar cualquier programa para acceder a la secuencia de datos de nombre (alternativa) de un archivo, p. notepad.exe myscript.cmd:data

Espero que esto ayude!

Cuestiones relacionadas