¿Cómo guardar la salida de un comando que modifica el entorno en una variable?
Estoy usando bash shell.
Supongamos que tengo:
function f () { a=3; b=4 ; echo "`date`: $a $b"; }
Y ahora, puedo usar comandos para ejecutar f:
$ a=0; b=0; f; echo $a; echo $b; echo $c
Sat Jun 28 21:27:08 CEST 2014: 3 4
3
4
pero me gustaría guardar la salida de fa variable c, así que intenté:
a=0; b=0; c=""; c=$(f); echo $a; echo $b; echo $c
pero desafortunadamente, tengo:
0
0
Sat Jun 28 21:28:03 CEST 2014: 3 4
así que no tengo ningún cambio de entorno aquí.
¿Cómo guardar la salida del comando (no solo la función) en variable y guardar los cambios ambientales?
Sé que $(...)abre una nueva subshell y ese es el problema, pero ¿es posible hacer alguna solución?
bash
environment-variables
output
faramir
fuente
fuente

$ay$bson variables locales en suffunción. Podríasexport, pero eso parece incompleto.…; f; echo $a; …resultados3se repiten, por lo quefse modifica una variable de shell (y no solo su propia variable local).Respuestas:
Si está usando Bash 4 o posterior, puede usar coprocesos :
saldrá
coproccrea un nuevo proceso que ejecuta un comando dado (aquí,cat). Guarda el PIDCOPROC_PIDy los descriptores de archivos de entrada / salida estándar en una matrizCOPROC(comopipe(2), o vea aquí o aquí ).Aquí ejecutamos la función con salida estándar apuntando a nuestro coproceso en ejecución
cat, y luegoreaddesde él. Comocatsolo escupe su entrada de vuelta, obtenemos la salida de la función en nuestra variable.exec {COPROC[1]}>&-solo cierra el descriptor de archivo para quecatno siga esperando para siempre.Tenga en cuenta que
readsolo toma una línea a la vez. Puede usarmapfilepara obtener una matriz de líneas, o simplemente usar el descriptor de archivo, sin embargo, desea usarlo de una manera diferente.exec {COPROC[1]}>&-obras en las versiones actuales de Bash, pero las versiones anteriores de la serie 4 requieren que guarde el descriptor de archivo en una variable simple primero:fd=${COPROC[1]}; exec {fd}>&-. Si su variable no está configurada, cerrará la salida estándar.Si está utilizando una versión de Bash de la serie 3, puede obtener el mismo efecto
mkfifo, pero no es mucho mejor que usar un archivo real en ese punto.fuente
exec {COPROC[1]}>&-comando (al menos a veces) termina el coproceso de inmediato , por lo que su salida ya no está disponible para ser leída.coproc { cat; sleep 1; }Parece funcionar mejor. (Es posible que necesite aumentar el1si está leyendo más de una línea.)Si ya cuenta con el cuerpo de
fejecución en el mismo shell que la persona que llama y, por lo tanto, puede modificar variables comoayb, ¿por qué no hacer que la función simplemente se establezcactambién? En otras palabras:Una posible razón puede ser que la variable de salida necesita tener diferentes nombres (c1, c2, etc.) cuando se llama en diferentes lugares, pero puede abordar eso haciendo que la función establezca c_TEMP y que los llamadores lo hagan,
c1=$c_TEMPetc.fuente
Es un Kluge, pero intenta
(y, opcionalmente
rm c.file).fuente
mktemp, pero me gustaría no crear archivos adicionales.Simplemente asigne todas las variables y escriba la salida al mismo tiempo.
Ahora si lo haces:
Su salida es:
Tenga en cuenta que este es un código portátil POSIX completo. Inicialmente configuré
c=la''cadena nula porque la expansión de parámetros limita la asignación de variables concurrentes + evaluación a valores numéricos (similares$((var=num))) o nulos o inexistentes, o, en otras palabras, no puede establecer y evaluar una variable a una cadena arbitraria al mismo tiempo si esa variable ya tiene asignado un valor. Así que solo me aseguro de que esté vacío antes de intentarlo. Si no vacíecantes de intentar asignarlo, la expansión solo devolverá el valor anterior.Solo para demostrar:
newvalno está asignado a en$clínea porqueoldvalse expande en${word}, mientras que la asignación$((aritmética en línea siempre ocurre. Pero si no tiene y está vacío o desarmado ...=))$coldval... luego
newvalse asigna y se expande a la vez$c.Todas las demás formas de hacerlo implican alguna forma de evaluación secundaria. Por ejemplo, supongamos que deseo asignar la salida de
f()una variable nombradanameen un punto yvaren otro. Como está escrito actualmente, esto no funcionará sin establecer la var en el alcance de la persona que llama. Sin embargo, una forma diferente podría verse así:He proporcionado un ejemplo mejor formateado a continuación, pero, llamado como arriba, el resultado es:
O con diferentes
$ENVo argumentos:Probablemente, lo más difícil de hacer bien cuando se trata de evaluar dos veces es asegurarse de que las variables no rompan las comillas y ejecuten código aleatorio. Cuantas más veces se evalúa una variable, más difícil se vuelve. La expansión de parámetros ayuda mucho aquí, y el uso
exporten lugar deevales mucho más seguro.En el ejemplo anterior,
f()primero asigna$foutla''cadena nula y luego establece los parámetros posicionales para probar nombres de variables válidos. Si ambas pruebas no pasan, se emite un mensajestderryfoutse le asigna el valor predeterminado de$fout_name. Sin embargo, independientemente de las pruebas,$fout_namesiempre se asigna a unofouto al nombre que especifique$fouty, opcionalmente, a su nombre especificado siempre se le asigna el valor de salida de la función. Para demostrar esto escribí este pequeñoforbucle:Juega alrededor de algunos con nombres de variables y expansiones de parámetros. Si tiene alguna pregunta, solo pregunte. Eso solo ejecuta las mismas pocas líneas en la función ya representada aquí. Vale la pena mencionar al menos que las variables
$ay se$bcomportan de manera diferente dependiendo de si están definidas en la invocación o si ya están configuradas. Aún así, elforhace casi nada más que formatear el conjunto de datos y proporcionado porf(). Echar un vistazo:fuente