Answers:
下面的代码会将每个环境变量转换为一条export
语句,并用引号将其正确读取以读取到shell中(LS_COLORS
例如,因为其中可能包含分号),然后将其获取。
[ 不幸的是,printf
in /usr/bin
通常不支持该功能%q
,因此我们需要调用内置的一个bash
。]
. <(xargs -0 bash -c 'printf "export %q\n" "$@"' -- < /proc/nnn/environ)
"$@"
代替'{}'
。对于那些对--
他改进的答案中的参数感到疑惑的人:将位置参数bash -c command_string
指定为$0
,从开始,而"$@"
扩展为包括以开头的参数$1
。参数--
将分配给$0
。
在这个答案中,我假设一个系统/proc/$pid/environ
以指定的PID返回进程的环境,变量定义之间为空字节。(因此是Linux,Cygwin或Solaris(?))。
export "${(@ps:\000:)$(</proc/$pid/environ)}"
(非常简单,就像zsh一样:没有命令 的输入重定向<FILE
等效于cat FILE
。命令替换的输出经过参数扩展,其标志 ps:\000:
表示“拆分为空字节”,并且@
含义是“如果整个内容都用双引号引起来,则将每个数组元素都作为一个单独的字段”(概括"$@"
)。
while IFS= read -r -d "" PWD; do export "$PWD"; done </proc/$pid/environ
PWD=$(pwd)
(在这些shell中,传递给空的定界符会read
导致将空字节PWD
用作分隔符。我将其用作临时变量名,以避免破坏另一个最终可能会被导入的变量。尽管从技术上讲PWD
,您也可以导入,但它只会保留直到下一个cd
。)
POSIX可移植性对于这个问题并不是很有趣,因为它仅适用于具有 /proc/PID/environ
。所以问题是Solaris sed支持什么?或者Solaris是否拥有/proc/PID/environ
,它不习惯使用,但是我在Solaris功能方面落后了很多,所以今天可能会。在Linux上,GNU实用程序和BusyBox都是null安全的,但带有警告。
如果我们确实坚持POSIX可移植性,则不需要POSIX文本实用程序来处理空字节,因此这很困难。这是一个假设awk支持空字节作为记录定界符的解决方案(nawk和gawk都支持,而BusyBox awk则支持,但mawk不支持)。
eval $(</proc/$pid/environ awk -v RS='\0' '{gsub("\047", "\047\\\047\047"); print "export \047" $0 "\047"}')
BusyBox的AWK(这是在嵌入式Linux系统中常见的版本)不支持的空字节而不是设置RS
于"\0"
在一个BEGIN
块以及上面没有命令行语法; 但是它确实支持-v 'RS="\0"'
。我没有调查原因,这看起来像我版本中的错误(Debian wheezy)。
(将所有行以空分隔的记录括在单引号中"\047"
在转义值内的单引号后,在单引号中。)
请注意,其中任何一个都可能尝试设置只读变量(如果您的外壳程序具有只读变量)。
我为此转了一圈。我对空字节的可移植性感到沮丧。没有一种可靠的方法可以在外壳中处理它们,这让我感到不舒服。所以我一直在寻找。事实是,我找到了几种方法来做到这一点,而我的其他答案中只提到了其中几种。但是结果至少是两个这样的shell函数:
_pidenv ${psrc=$$} ; _zedlmt <$near_any_type_of_file
首先,我将讨论\0
定界。实际上,这很容易做到。功能如下:
_zedlmt() { od -t x1 -w1 -v | sed -n '
/.* \(..\)$/s//\1/
/00/!{H;b};s///
x;s/\n/\\x/gp;x;h'
}
基本上od
采用stdin
与写入到其stdout
它接收在每行一个十六进制的每个字节。
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
我敢打赌,您可以猜测是哪个\0null
,对吗?像这样写出来,很容易用any 处理sed
。sed
只需保存每行中的最后两个字符,直到遇到空值为止,此时它将以printf
友好的格式代码替换中间的换行符并打印字符串。结果是\0null
十六进制字节字符串的定界数组。看:
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
.
我将上述内容传递给,tee
以便您可以同时看到命令怀疑的输出和printf
的处理结果。我希望您会注意到,该子外壳实际上也没有被引用,但printf
仍仅在\0null
定界符处分割。看:
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
.
该扩展也没有引号-引号与否无关紧要。这是因为除了\n
每次生成的一条举绳外,咬合值都是无分隔的sed
打印一个字符串。分词不适用。这就是使这成为可能的原因:
_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
上述功能用途_zedlmt
要么${pcat}
为可以在被发现的任何过程的环境采购的字节码准备的流/proc
,或者直接.dot
${psrc}
在当前外壳相同,或不带参数,以显示相同的处理后的输出到终端等set
或printenv
会。您需要的是$pid
- 任何可读的/proc/$pid/environ
文件都可以。
您可以这样使用它:
#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}
但是,人类友好和友善之间有什么区别?嗯,不同之处在于,这个答案与这里的其他答案(包括我的其他答案)不同。其他所有答案都取决于以某种方式引用外壳程序来处理所有边缘情况。它根本不能很好地工作。请相信我- 我已经尝试过。看:
_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")
没有数量的时髦字符或包含的引号可以解决此问题,因为直到获取内容的那一刻才评估每个值的字节。而且我们已经知道它至少可以作为一个值工作-此处没有解析或引用保护的必要,因为这是原始值的逐字节复制。
该函数首先评估$var
名称,并等待检查完成,然后.dot
再在文件描述符3上获取提交给它的here-doc。这很简单。和POSIX便携式。好吧,至少\ 0null处理是POSIX可移植的-/ process文件系统显然是Linux特定的。这就是为什么有两个功能的原因。
使用source
和过程替换:
source <(sed -r -e 's/([^\x00]*)\x00/export \1\n/g' /proc/1/environ)
不久:
. <(sed -r -e 's/([^\x00]*)\x00/export \1\n/g' /proc/1/environ)
使用eval
和命令替换:
eval `sed -r -e 's/([^\x00]*)\x00/export \1\n/g' /proc/1/environ`
该sed
呼叫可以替换为awk
呼叫:
awk -vRS='\x00' '{ print "export", $0 }' /proc/1/environ
但是请不要忘记,它不会清除pid 1中没有的任何环境变量。
值得注意的是,进程可以具有不是有效的Bash / Sh / * sh变量的环境变量-POSIX建议但不要求环境变量具有匹配的名称^[a-zA-Z0-9_][a-zA-Z0-9_]*$
。
要在Bash中生成与其他进程的环境外壳兼容的变量列表,请执行以下操作:
function env_from_proc {
local pid="$1" skipped=( )
cat /proc/"$pid"/environ | while read -r -d "" record
do
if [[ $record =~ ^[a-zA-Z_][a-zA-Z0-9_]*= ]]
then printf "export %q\n" "$record"
else skipped+=( "$record" )
fi
done
echo "Skipped non-shell-compatible vars: ${skipped[@]%%=*}" >&2
}
同样,要加载它们:
function env_from_proc {
local pid="$1" skipped=( )
while read -r -d "" record
do
if [[ $record =~ ^[a-zA-Z_][a-zA-Z0-9_]*= ]]
then export "$(printf %q "$record")"
else skipped+=( "$record" )
fi
done < /proc/"$pid"/environ
echo "Skipped non-shell-compatible vars: ${skipped[@]%%=*}" >&2
}
这个问题只是偶尔出现,但是当它出现时...
我认为这是POSIX可移植的:
. <<ENV /dev/stdin
$(sed -n 'H;${x;s/\(^\|\x00\)\([^=]*.\)\([^\x00]*\)/\2\x27\3\x27\n/gp}' \
/proc/$pid/environ)
ENV
但是@Gilles有一个很好的意义- sed
可能会处理null,但可能不会。因此,实际上也是这个(我真的这么认为) POSIX可移植方法:
s=$$SED$$
sed 's/'\''/'$s'/;1s/^./'\''&/' </proc/"$$"/environ |
tr '\0' "'" |
sed 's/'\''/&\n&/g' |
sed '1d;$d;s/^\('\''\)\([^=]*.\)/\2\1/;s/'$s'/'\\\''/g'
不过,如果您拥有GNU,则sed
只需执行以下操作:
sed -z 's/^[^=]*./&'\''/;s/$/'\''\n/' </proc/"$$"/environ
#BOTH METHODS OUTPUT:
好吧,除了 /dev/...
未指定它是但您可以非常期望该语法在大多数Unices上具有相同的行为。
现在,如果这与您的其他问题有关,则您可能希望像这样使用它:
nsenter -m -u -i -n -p -t $PID /bin/bash 5<<ENV --rcfile=/dev/fd/5
$(sed -z 's/^[^=]*./&'\''/;s/$/'\''\n/' </proc/"$$"/environ)
ENV
此处文档非常有帮助,因为它可以防止外壳与我们在子外壳中难以处理的任何引用产生.dot
冲突,并且还为我们提供了一个可获取文件的可靠路径,而不是子外壳或外壳变量。这里的其他人使用的<(process substitution)
bashism的工作方式几乎相同-只是绝对是匿名的,|pipe
而POSIX只iohere
为here-docs 指定一个,因此它可以是任何类型的文件,尽管实际上,它通常是temp
文件。(dash,
另一方面,|pipes
对于here-docs 使用匿名)。不过,关于进程替换的不幸的事情是,它也依赖于外壳程序-如果使用,这可能是一个特别烦人的问题init
。
|pipes
当然这也适用,但是当|pipe's
状态随着其子外壳消失时,最终您又会失去环境。然后,再次起作用:
sed '...;a\exec <>/dev/tty' /proc/$pid/environ | sh -i
的 sed
语句本身的工作原理是保持每行内存,直到它到达最后的,那时它执行全局替换操作报价和插入新行,其中通过锚固在空为宜。真的很简单。
在dash
图片中,您会看到我选择避开\ mess并将GNU
特定-r
选项添加到sed
。但这只是因为它的键入较少。如您所见,它可以任一种方式工作zsh
图像中。
这里是zsh
:
这是在dash
做同样的事情:
即使终端逃生也毫发无损地通过:
/proc/PID/environ
所以,问题是什么的Solaris sed的支持-或Solaris是否有/proc/PID/environ
,它并没有使用,但我的方式落后于Solaris功能的曲线,所以现在可能如此。)
sed
必须处理十六进制的ascii,其中空字节为1。此外,我实际上只是在想是否还有更简单的方法可以做到这一点。
\xNN
在POSIX中甚至不需要\OOO
八进制语法(在C字符串和awk中是,但是在sed正则表达式中不需要)语法。
/proc/PID/environ
毕竟没有(它在中有其他几个类似Linux的条目/proc/PID
,但没有environ
)。因此,便携式解决方案根本不需要超越Linux工具,这意味着GNU sed或BusyBox sed。两者都支持正则\x00
表达式,因此您的代码可根据需要进行移植(但不包含POSIX)。但是它太复杂了。
. <(xargs -0 bash -c 'printf "export %q\n" "$@"' -- < /proc/nnn/environ)
,它也将正确处理带引号的变量。