¿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 f
a 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
$a
y$b
son variables locales en suf
función. Podríasexport
, pero eso parece incompleto.…; f; echo $a; …
resultados3
se repiten, por lo quef
se modifica una variable de shell (y no solo su propia variable local).Respuestas:
Si está usando Bash 4 o posterior, puede usar coprocesos :
saldrá
coproc
crea un nuevo proceso que ejecuta un comando dado (aquí,cat
). Guarda el PIDCOPROC_PID
y 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 luegoread
desde él. Comocat
solo 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 quecat
no siga esperando para siempre.Tenga en cuenta que
read
solo toma una línea a la vez. Puede usarmapfile
para 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 el1
si está leyendo más de una línea.)Si ya cuenta con el cuerpo de
f
ejecución en el mismo shell que la persona que llama y, por lo tanto, puede modificar variables comoa
yb
, ¿por qué no hacer que la función simplemente se establezcac
tambié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_TEMP
etc.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íec
antes de intentar asignarlo, la expansión solo devolverá el valor anterior.Solo para demostrar:
newval
no está asignado a en$c
línea porqueoldval
se 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 ...=
))
$c
oldval
... luego
newval
se 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 nombradaname
en un punto yvar
en 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
$ENV
o 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
export
en lugar deeval
es mucho más seguro.En el ejemplo anterior,
f()
primero asigna$fout
la''
cadena nula y luego establece los parámetros posicionales para probar nombres de variables válidos. Si ambas pruebas no pasan, se emite un mensajestderr
yfout
se le asigna el valor predeterminado de$fout_name
. Sin embargo, independientemente de las pruebas,$fout_name
siempre se asigna a unofout
o al nombre que especifique$fout
y, opcionalmente, a su nombre especificado siempre se le asigna el valor de salida de la función. Para demostrar esto escribí este pequeñofor
bucle: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
$a
y se$b
comportan de manera diferente dependiendo de si están definidas en la invocación o si ya están configuradas. Aún así, elfor
hace casi nada más que formatear el conjunto de datos y proporcionado porf()
. Echar un vistazo:fuente