Di vueltas y vueltas con esto. Estaba frustrado con la portabilidad de bytes nulos. No me sentó bien que no hubiera una forma confiable de manejarlos en un caparazón. Así que seguí buscando. La verdad es que encontré varias formas de hacer esto, solo algunas de las cuales se mencionan en mi otra respuesta. Pero los resultados fueron al menos dos funciones de shell que funcionan así:
_pidenv ${psrc=$$} ; _zedlmt <$near_any_type_of_file
Primero hablaré sobre la \0
delimitación. En realidad es bastante fácil de hacer. Aquí está la función:
_zedlmt() { od -t x1 -w1 -v | sed -n '
/.* \(..\)$/s//\1/
/00/!{H;b};s///
x;s/\n/\\x/gp;x;h'
}
Básicamente od
toma stdin
y escribe en stdout
cada byte que recibe en hexadecimal uno por línea.
printf 'This\0is\0a\0lot\0\of\0\nulls.' |
od -t x1 -w1 -v
#output
0000000 54
0000001 68
0000002 69
0000003 73
0000004 00
0000005 69
0000006 73
#and so on
Apuesto a que puedes adivinar cuál es el \0null
, ¿verdad? Escrito así es fácil de manejar con cualquiera sed
. sed
solo guarda los dos últimos caracteres en cada línea hasta que encuentra un valor nulo en el que reemplaza las nuevas líneas intermedias con printf
un código de formato amigable e imprime la cadena. El resultado es una \0null
matriz delimitada de cadenas de bytes hexadecimales. Mira:
printf %b\\n $(printf 'Fewer\0nulls\0here\0.' |
_zedlmt | tee /dev/stderr)
#output
\x46\x65\x77\x65\x72
\x6e\x75\x6c\x6c\x73
\x68\x65\x72\x65
\x2e
Fewer
nulls
here
.
Lo canalicé arriba para tee
que pueda ver tanto la salida de la suspensión del comando como el resultado del printf
procesamiento. Espero que se dé cuenta de que la subshell en realidad tampoco se cita pero se printf
divide solo en el \0null
delimitador. Mira:
printf %b\\n $(printf \
"Fe\n\"w\"er\0'nu\t'll\\'s\0h ere\0." |
_zedlmt | tee /dev/stderr)
#output
\x46\x65\x0a\x22\x77\x22\x65\x72
\x27\x6e\x75\x09\x27\x6c\x6c\x27\x73
\x68\x20\x20\x20\x20\x65\x72\x65
\x2e
Fe
"w"er
'nu 'll's
h ere
.
Tampoco hay citas sobre esa expansión, no importa si la citas o no. Esto se debe a que los valores de mordida no se separan, excepto la \n
línea de línea generada por cada vez que sed
imprime una cadena. La división de palabras no se aplica. Y eso es lo que hace esto posible:
_pidenv() { ps -p $1 >/dev/null 2>&1 &&
[ -z "${1#"$psrc"}" ] && . /dev/fd/3 ||
cat <&3 ; unset psrc pcat
} 3<<STATE
$( [ -z "${1#${pcat=$psrc}}" ] &&
pcat='$(printf %%b "%s")' || pcat="%b"
xeq="$(printf '\\x%x' "'=")"
for x in $( _zedlmt </proc/$1/environ ) ; do
printf "%b=$pcat\n" "${x%%"$xeq"*}" "${x#*"$xeq"}"
done)
#END
STATE
La función anterior se utiliza _zedlmt
para ${pcat}
una secuencia preparada de código de bytes para el abastecimiento del entorno de cualquier proceso que se pueda encontrar en /proc
, o directamente .dot
${psrc}
al mismo en el shell actual, o sin un parámetro, para mostrar una salida procesada de la misma al terminal como set
o lo printenv
haré. Todo lo que necesita es un $pid
: cualquier/proc/$pid/environ
archivo legible servirá.
Lo usas así:
#output like printenv for any running process
_pidenv $pid
#save human friendly env file
_pidenv $pid >/preparsed/env/file
#save unparsed file for sourcing at any time
_pidenv ${pcat=$pid} >/sourcable/env.save
#.dot source any pid's $env from any file stream
_pidenv ${pcat=$pid} | sh -c '. /dev/stdin'
#feed any pid's env in on a heredoc filedescriptor
su -c '. /dev/fd/4' 4<<ENV
$( _pidenv ${pcat=$pid} )
ENV
#.dot sources any $pid's $env in the current shell
_pidenv ${psrc=$pid}
Pero, ¿cuál es la diferencia entre ser amigable con los humanos y el sourcable ? Bueno, la diferencia es lo que hace que esta respuesta sea diferente a todas las demás aquí, incluida la otra. Cualquier otra respuesta depende de las citas de shell de una forma u otra para manejar todos los casos límite. Simplemente no funciona tan bien. Por favor créeme, lo he intentado. Mira:
_pidenv ${pcat=$$}
#output
LC_COLLATE=$(printf %b "\x43")
GREP_COLOR=$(printf %b "\x33\x37\x3b\x34\x35")
GREP_OPTIONS=$(printf %b "\x2d\x2d\x63\x6f\x6c\x6f\x72\x3d\x61\x75\x74\x6f")
LESS_TERMCAP_mb=$(printf %b "\x1b\x5b\x30\x31\x3b\x33\x31\x6d")
LESS_TERMCAP_md=$(printf %b "\x1b\x5b\x30\x31\x3b\x33\x31\x6d")
LESS_TERMCAP_me=$(printf %b "\x1b\x5b\x30\x6d")
LESS_TERMCAP_se=$(printf %b "\x1b\x5b\x30\x6d")
LESS_TERMCAP_so=$(printf %b "\x1b\x5b\x30\x30\x3b\x34\x37\x3b\x33\x30\x6d")
LESS_TERMCAP_ue=$(printf %b "\x1b\x5b\x30\x6d")
NINGUNA cantidad de caracteres extravagantes o comillas contenidas puede romper esto porque los bytes de cada valor no se evalúan hasta el momento en que se obtiene el contenido. Y ya sabemos que funcionó como un valor al menos una vez: no es necesario analizar o proteger la cotización aquí porque se trata de una copia byte por byte del valor original.
La función primero evalúa los $var
nombres y espera a que se completen las comprobaciones antes de .dot
obtener el here-doc alimentado en el descriptor de archivo 3. Antes de obtenerlo, así es como se ve. Es infalible. Y POSIX portátil. Bueno, al menos el manejo de \ 0null es POSIX portable: el sistema de archivos / process es obviamente específico de Linux. Y es por eso que hay dos funciones.
. <(xargs -0 bash -c 'printf "export %q\n" "$@"' -- < /proc/nnn/environ)
, que también manejará las variables con comillas en ellas correctamente.