Eu voltei e voltei com isso. Fiquei frustrado com a portabilidade de bytes nulos. Não me ocorreu bem que não havia uma maneira confiável de lidar com eles em uma concha. Então eu continuei olhando. A verdade é que encontrei várias maneiras de fazer isso, das quais apenas duas são mencionadas na minha outra resposta. Mas os resultados foram pelo menos duas funções de shell que funcionam assim:
_pidenv ${psrc=$$} ; _zedlmt <$near_any_type_of_file
Primeiro vou falar sobre a \0
delimitação. Na verdade, é muito fácil de fazer. Aqui está a função:
_zedlmt() { od -t x1 -w1 -v | sed -n '
/.* \(..\)$/s//\1/
/00/!{H;b};s///
x;s/\n/\\x/gp;x;h'
}
Basicamente, od
leva stdin
e grava em stdout
cada byte que recebe em hexadecimal um por linha.
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
Aposto que você pode adivinhar qual é o \0null
certo? Escrito assim, é fácil lidar com qualquer um sed
. sed
apenas salva os dois últimos caracteres em cada linha até encontrar um nulo; nesse ponto, substitui as novas linhas intermediárias pelo printf
código de formato amigável e imprime a string. O resultado é uma \0null
matriz delimitada de cadeias de bytes hexadecimais. Veja:
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
.
Eu canalizei o acima para tee
que você pudesse ver a saída do comando susbstitution e o resultado do printf
processamento. Espero que você observe que o subshell também não é citado, mas printf
ainda é dividido apenas no \0null
delimitador. Veja:
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
.
Também não há aspas nessa expansão - não importa se você citou ou não. Isso ocorre porque os valores da mordida são apresentados sem separação, exceto pela linha de \n
ew gerada para cada vez que sed
uma string é impressa. A divisão de palavras não se aplica. E é isso que torna isso possível:
_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
A função acima usa _zedlmt
para ${pcat}
um fluxo preparado de código de bytes para a origem do ambiente de qualquer processo que possa ser encontrado /proc
ou diretamente para .dot
${psrc}
o mesmo no shell atual, ou sem um parâmetro, para exibir uma saída processada do mesmo para o terminal, como set
ou printenv
vontade. Tudo que você precisa é de $pid
- qualquer/proc/$pid/environ
arquivo legível fará.
Você o usa assim:
#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}
Mas qual é a diferença entre humano amigável e fonte ? Bem, a diferença é que faz com que essa resposta seja diferente de todas as outras aqui - incluindo a minha outra. Qualquer outra resposta depende do shell citar de uma maneira ou de outra para lidar com todos os casos extremos. Simplesmente não funciona tão bem. Por favor, acredite em mim - eu tentei. Veja:
_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")
NENHUMA quantidade de caracteres descolados ou citações contidas pode quebrar isso porque os bytes de cada valor não são avaliados até o instante em que o conteúdo é originado. E já sabemos que funcionou como um valor pelo menos uma vez - não há proteção de análise ou cotação necessária aqui, porque esta é uma cópia de byte a byte do valor original.
A função primeiro avalia os $var
nomes e aguarda a conclusão das verificações antes .dot
de fornecer o documento aqui, alimentado pelo descritor de arquivo 3. Antes de fornecê-lo, é assim que parece. É à prova de idiotas. E POSIX portátil. Bem, pelo menos a manipulação \ 0null é POSIX portátil - o sistema de arquivos / process é obviamente específico do Linux. E é por isso que existem duas funções.
. <(xargs -0 bash -c 'printf "export %q\n" "$@"' -- < /proc/nnn/environ)
, que também manipulará variáveis com aspas.