我希望将多段 shell 结果拆分为不同的变量,
这是传感器的示例输出来说明
acpitz-virtual-0
Adapter: Virtual device
temp1: +41.0°C (crit = +95.0°C)
coretemp-isa-0000
Adapter: ISA adapter
Core 0: +36.0°C (high = +90.0°C, crit = +90.0°C)
Core 1: +36.0°C (high = +90.0°C, crit = +90.0°C)
在这种情况下,我希望有一个sensors
包含整个输出的变量(命令名称), sensors_acpitz_virtual_0
包含整个输出的第一段和一个sensors_coretemp_isa_0000
包含第二段内容的变量。
sensors_acpitz_virtual_0
acpitz-virtual-0
Adapter: Virtual device
temp1: +41.0°C (crit = +95.0°C)
Sensors_coretemp_isa_0000
coretemp-isa-0000
Adapter: ISA adapter
Core 0: +36.0°C (high = +90.0°C, crit = +90.0°C)
Core 1: +36.0°C (high = +90.0°C, crit = +90.0°C)
sensors_acpitz_virtual_0__Adapter__Virtual_device
然后是一个仅包含第一段这部分内容的变量
Adapter: Virtual device
temp1: +41.0°C (crit = +95.0°C)
sensors_acpitz_virtual_0__Adapter__Virtual_device__temp1
仅包含来自 acpitz-virtual-0 虚拟设备适配器的 temp1 温度的变量,依此类推。
+41.0°C (crit = +95.0°C)
我应该使用什么工具(最好是发行版中预安装的常用工具)以及如何获得这种结果?
答案1
我会做类似的事情:
eval "$(#"
perl -MString::ShellQuote -00 -lne '
if (/^(.+)\n(.+)/) {
($v1, $v2, $rest) = ("sensors_$1", "$2", $'\'');
# $v1, $v2 contain the first 2 lines, $rest the rest
s/\W/_/g for $v1, $v2;
# replace non-word characters with _ in the variables
print "$v1=" . shell_quote("$1\n$2$rest");
print "${v1}__$v2=" . shell_quote("$2$rest");
# output the variable definition taking care to quote the value
while ($rest =~ /^(.*?):\s*(.*)/gm) {
# process the "foo: bar" lines in the rest
($v3,$val) = ("$1", $2);
$v3 =~ s/\W/_/g;
print "${v1}__${v2}__$v3=" . shell_quote($val)
}
}' < that-file)"
-00
对于段落模式。-l
删除记录末尾的段落分隔符并print
在输出中添加一个。
-n
一次处理一条记录的输入。
在您的示例中,该 perl 命令将输出以下 shell 代码:
sensors_acpitz_virtual_0='acpitz-virtual-0
Adapter: Virtual device
temp1: +41.0°C (crit = +95.0°C)'
sensors_acpitz_virtual_0__Adapter__Virtual_device='Adapter: Virtual device
temp1: +41.0°C (crit = +95.0°C)'
sensors_acpitz_virtual_0__Adapter__Virtual_device__temp1='+41.0°C (crit = +95.0°C)'
sensors_coretemp_isa_0000='coretemp-isa-0000
Adapter: ISA adapter
Core 0: +36.0°C (high = +90.0°C, crit = +90.0°C)
Core 1: +36.0°C (high = +90.0°C, crit = +90.0°C)'
sensors_coretemp_isa_0000__Adapter__ISA_adapter='Adapter: ISA adapter
Core 0: +36.0°C (high = +90.0°C, crit = +90.0°C)
Core 1: +36.0°C (high = +90.0°C, crit = +90.0°C)'
sensors_coretemp_isa_0000__Adapter__ISA_adapter__Core_0='+36.0°C (high = +90.0°C, crit = +90.0°C)'
sensors_coretemp_isa_0000__Adapter__ISA_adapter__Core_1='+36.0°C (high = +90.0°C, crit = +90.0°C)'
我们用来eval "$(that-perl-command)"
告诉 shell 评估该命令输出的代码。
答案2
好吧,我不太确定这个名字是从哪里来的acpitz_virtual_0__Adapter__Virtual_device__temp1
应该会来,所以这对此没有任何作用,但是:
sed -e '/./{H;$!d;}' -e'x;s///' \
-e 's/\(\n.*\)*-/_\1/g' \
-e "s/\n\(.*\)/='\1'/" <your_input
...输出如下:
acpitz_virtual_0='Adapter: Virtual device
temp1: +41.0°C (crit = +95.0°C)'
coretemp_isa_0000='Adapter: ISA adapter
Core 0: +36.0°C (high = +90.0°C, crit = +90.0°C)
Core 1: +36.0°C (high = +90.0°C, crit = +90.0°C)'
我不允许'
在输入中使用单引号,并且我认为与可移植 shell 名称唯一不兼容的是每个名称中的破折号。处理报价(万一)做:
sed -e '/./{H;$!d;}' -ex\;s/// \
-e "s/\(\n.*\)*['-]/_\1/g" \
-e "s/'/'"'\\&&/g' \
-e "s/\n\(.*\)/='\1'/" <your_input
...这将始终转义输入中的任何硬引号。
要按照您指定的方式使用此输出,有多个选项。您可以将其包装在eval
:
eval "$(sed ... <your_input)"
...或者将其读入文件...
sed ... <your_input >out; . ./out
...或者将其流式传输到子shell...
sed ... <your_input | sh
...以及许多其他事物。
这是另一个sed
可以完成您询问的奇特事情的方法:
sed -netDel -e'/./{H;g;s///' \
-e'# grow' -e's/[0-9]*: .*\n[^:0-9]*//' \
-e'# loop' -e's/: .*//' \
-e'# ends' -e's/[^_[:alnum:]]/_/g;}' \
-e'# here' -e's/\(.\)\(.*\)/& \\/p' \
-e$\!t -ex -e's//\2\1. \\/' \
-e'# swap' -e"s/'/'"'\\&&/g;tDel' \
-ed\;:Del -e"s/\(.*\). /'\1' /" \
-e'# loop' -e'/^[^:]*: /!{p;D;}' \
-e'# ends' -e"s/\n/' \\\&/;P;D" <infile >outfile
这是它的工作原理:sed
从某种意义上说,有点蜡烛两头烧。sed
替补状态从左到右的迭代增长和在每个段落末尾朝同一方向迭代减少之间。
就像:
1
1:2 # grow
1:2:3 # swap
2:3 # del
3
#if that makes any sense at all...
这生长状态在 之间进行处理/./{H;g;...p;};$!t
。
对于至少匹配一个
.
字符的每个输入行,sed
将一个副本附加到H
旧空间,然后立即g
通过覆盖模式空间来设置所有保留空间。- 通过这种方式,
sed
可以对不断增长的堆栈执行编辑并打印每次迭代的结果,同时始终将原始输入保存在保留空间中。
- 通过这种方式,
在里面生长state,
sed
将为每个输入行打印有效的 shell 名称。sed
删除第一次出现和最后一次出现之间的所有内容"[0-9]*: "
以及最后一次出现的所有内容,但以其他方式用该字符替换类中不": "
存在的任何字符- 因此名称会递归增长^
[_[:alnum:]]
_
(直到他们不这样做为止)。
sed
t
ests 为任何在一个过程中成功编辑过的行生长在尝试处理逻辑之前,状态和分支远离脚本交换状态。- 作为一种特殊情况,这个
t
est!
不会对$
最后一个输入行执行,因此它会下降到交换无论如何声明。
- 作为一种特殊情况,这个
这交换状态处理之间x;...;d
sed
ex
更改保留空间缓冲区的最后一个或空白模式空间缓冲区,从而清除下一个输入段落的保存空间(如果有的话)。- 输入中多个空白行的序列不会对该模式产生负面影响 - 将空白保持空间替换为空白模式空间可以根据需要经常发生,以达到相同的结果。
sed
将段落的前导空白行交换到其尾部,附加一个空格,后跟一个反斜杠,并立即转义所有单引号(如果有的话)在非空白保持缓冲区失败之前的段落中德尔状态,或者d
删除一个空白状态。
这德尔tDel -> :Del
状态在和 之间有效:Del;...;D
。
sed
将其所有递归减少模式缓冲区包装在一对单引号中。- 其中第一个被删除,每个的第一行都被删除德尔迭代,但最后一个总是被自身替换。
对于段落缓冲区匹配,将在缓冲区中
"^[^:]*: "
sed
第一个出现的嵌入 ewline 前面添加另一个单引号,后跟空格和反斜杠\n
(如果有的话)并P
rint 仅输出缓冲区的第一行。- 否则,
sed
p
完全打印一个非空缓冲区。
- 否则,
无论哪种方式,
sed
然后D
删除\n
缓冲区中第一个出现的 ewline,然后再循环回脚本顶部(如果有任何剩余)。- 如果此处未清空缓冲区,则在脚本顶部(第一行被删除后),初始
t
est 将证明刚刚执行的引号替换为 true,因此sed
将直接跳过其大部分编辑脚本到:Del
标签对于每次迭代德尔状态直到缓冲区完全清空。
- 如果此处未清空缓冲区,则在脚本顶部(第一行被删除后),初始
所以sed
大多可以做你问的那些奇特的事情。sed
这里也不例外的流式传输性质,因此它的输出如下:
acpitz_virtual_0 \
acpitz_virtual_0_Adapter__Virtual_device \
acpitz_virtual_0_Adapter__Virtual_device_temp1 \
'acpitz-virtual-0
Adapter: Virtual device
temp1: +41.0°C (crit = +95.0°C)
' \
'Adapter: Virtual device
temp1: +41.0°C (crit = +95.0°C)
' \
'temp1: +41.0°C (crit = +95.0°C)' \
'' \
coretemp_isa_0000 \
coretemp_isa_0000_Adapter__ISA_adapter \
coretemp_isa_0000_Adapter__ISA_adapter_Core_0 \
coretemp_isa_0000_Adapter__ISA_adapter_Core_1 \
'coretemp-isa-0000
Adapter: ISA adapter
Core 0: +36.0°C (high = +90.0°C, crit = +90.0°C)
Core 1: +36.0°C (high = +90.0°C, crit = +90.0°C)
' \
'Adapter: ISA adapter
Core 0: +36.0°C (high = +90.0°C, crit = +90.0°C)
Core 1: +36.0°C (high = +90.0°C, crit = +90.0°C)
' \
'Core 0: +36.0°C (high = +90.0°C, crit = +90.0°C)' \
'Core 1: +36.0°C (high = +90.0°C, crit = +90.0°C)' \
'' \
###backslashes for set or for assignment
...在每个段落的堆栈中:首先是名称,然后是值。您可以循环它,每个段落将以空参数结束。
这里还进行了更多的验证,因此无论其输入如何,都应该不存在无效 shell 名称或任何类型的未加引号的输出值的危险。
此函数适用于您的输入:
ineval(){
. /dev/fd/0
for v
do case ${#v}:${s:-$v} in
(0*) until [ 0 -eq "${#1}" ] &&
s= && ${1+"shift"}
do shift; done;;
(*:*[-:]*) eval "s=- $1=\$v;shift"
esac; done
} <<SET
set '' $( sed -netDel -e'/./{H;g;s///' \
-e'# grow' -e's/[0-9]*: .*\n[^:0-9]*//' \
-e'# loop' -e's/: .*//' \
-e'# ends' -e's/[^_[:alnum:]]/_/g;}' \
-e'# here' -e's/\(.\)\(.*\)/& \\/p' \
-e$\!t -ex -e's//\2\1. \\/' \
-e'# swap' -e"s/'/'"'\\&&/g;tDel' \
-ed\;:Del -e"s/\(.*\). /'\1' /" \
-e'# loop' -e'/^[^:]*: /!{p;D;}' \
-e'# ends' -e"s/\n/' \\\&/;P;D" "$@" )
SET
以下是set -x
您的输入:
(set -x; ineval /tmp/sens)
+ sed -netDel -e/./{H;g;s/// -e# grow -es/[0-9]*: .*\n[^:0-9]*// -e# loop -es/: .*// -e# ends -es/[^_[:alnum:]]/_/g;} -e# here -es/\(.\)\(.*\)/& \\/p -e$!t -ex -es//\2\1. \\/ -e# swap -es/'/'\\&&/g;tDel -ed;:Del -es/\(.*\). /'\1' / -e# loop -e/^[^:]*: /!{p;D;} -e# ends -es/\n/' \\&/;P;D /tmp/sens
+ . /dev/fd/0
+ set acpitz_virtual_0 acpitz_virtual_0_Adapter__Virtual_device acpitz_virtual_0_Adapter__Virtual_device_temp1 acpitz-virtual-0
Adapter: Virtual device
temp1: +41.0°C (crit = +95.0°C)
Adapter: Virtual device
temp1: +41.0°C (crit = +95.0°C)
temp1: +41.0°C (crit = +95.0°C) coretemp_isa_0000 coretemp_isa_0000_Adapter__ISA_adapter coretemp_isa_0000_Adapter__ISA_adapter_Core_0 coretemp_isa_0000_Adapter__ISA_adapter_Core_1 coretemp-isa-0000
Adapter: ISA adapter
Core 0: +36.0°C (high = +90.0°C, crit = +90.0°C)
Core 1: +36.0°C (high = +90.0°C, crit = +90.0°C)
Adapter: ISA adapter
Core 0: +36.0°C (high = +90.0°C, crit = +90.0°C)
Core 1: +36.0°C (high = +90.0°C, crit = +90.0°C)
Core 0: +36.0°C (high = +90.0°C, crit = +90.0°C) Core 1: +36.0°C (high = +90.0°C, crit = +90.0°C)
+ [ 0 -eq 0 ]
+ s=
+ shift
+ eval s=- acpitz_virtual_0=$v;shift
+ s=- acpitz_virtual_0=acpitz-virtual-0
Adapter: Virtual device
temp1: +41.0°C (crit = +95.0°C)
+ shift
+ eval s=- acpitz_virtual_0_Adapter__Virtual_device=$v;shift
+ s=- acpitz_virtual_0_Adapter__Virtual_device=Adapter: Virtual device
temp1: +41.0°C (crit = +95.0°C)
+ shift
+ eval s=- acpitz_virtual_0_Adapter__Virtual_device_temp1=$v;shift
+ s=- acpitz_virtual_0_Adapter__Virtual_device_temp1=temp1: +41.0°C (crit = +95.0°C)
+ shift
+ [ 0 -eq 83 ]
+ shift
+ [ 0 -eq 66 ]
+ shift
+ [ 0 -eq 41 ]
+ shift
+ [ 0 -eq 0 ]
+ s=
+ shift
+ eval s=- coretemp_isa_0000=$v;shift
+ s=- coretemp_isa_0000=coretemp-isa-0000
Adapter: ISA adapter
Core 0: +36.0°C (high = +90.0°C, crit = +90.0°C)
Core 1: +36.0°C (high = +90.0°C, crit = +90.0°C)
+ shift
+ eval s=- coretemp_isa_0000_Adapter__ISA_adapter=$v;shift
+ s=- coretemp_isa_0000_Adapter__ISA_adapter=Adapter: ISA adapter
Core 0: +36.0°C (high = +90.0°C, crit = +90.0°C)
Core 1: +36.0°C (high = +90.0°C, crit = +90.0°C)
+ shift
+ eval s=- coretemp_isa_0000_Adapter__ISA_adapter_Core_0=$v;shift
+ s=- coretemp_isa_0000_Adapter__ISA_adapter_Core_0=Core 0: +36.0°C (high = +90.0°C, crit = +90.0°C)
+ shift
+ eval s=- coretemp_isa_0000_Adapter__ISA_adapter_Core_1=$v;shift
+ s=- coretemp_isa_0000_Adapter__ISA_adapter_Core_1=Core 1: +36.0°C (high = +90.0°C, crit = +90.0°C)
+ shift
+ [ 0 -eq 157 ]
+ shift
+ [ 0 -eq 139 ]
+ shift
+ [ 0 -eq 58 ]
+ shift
+ [ 0 -eq 58 ]
+ shift
+ [ 0 -eq 0 ]
+ s=
+ shift
答案3
Perl 有一种特殊的段落模式,它将“行”定义为段落。这意味着一条线是由两个连续的\n
,而不是一个定义的。从man perlrun
:
-0[octal/hexadecimal]
specifies the input record separator ($/) as an octal or
hexadecimal number. [. . .]
The special value 00 will cause Perl to slurp files in paragraph
mode.
因此,您可以编写一个 perl 脚本来打印变量:
$ perl -00lne '/.*/; $v=$&; $v=~s/-/_/g; print "$v=\"$_\""' file
acpitz_virtual_0="acpitz-virtual-0
Adapter: Virtual device
temp1: +41.0°C (crit = +95.0°C)"
coretemp_isa_0000="coretemp-isa-0000
Adapter: ISA adapter
Core 0: +36.0°C (high = +90.0°C, crit = +90.0°C)
Core 1: +36.0°C (high = +90.0°C, crit = +90.0°C)"
然后使用eval
告诉你的 shell 读取它们:
$ eval "$(perl -00lne '/.*/; $v=$&; $v=~s/-/_/g; print "$v=\"$_\""' file )"
$ echo "$coretemp_isa_0000"
coretemp-isa-0000
Adapter: ISA adapter
Core 0: +36.0°C (high = +90.0°C, crit = +90.0°C)
Core 1: +36.0°C (high = +90.0°C, crit = +90.0°C)
Perl 脚本可能需要一些解释。这与注释脚本相同:
## The . doesn't match newlines by default, so this is just a
## quick way of getting the text before the first \n, this will be
## you variable's name.
/.*/;
## $& is whatever was matched my the last match (//) operator. We set
## $v to that.
$v=$&;
## Bash doesn't like dashes in variable names, this will replace
## them with underscores.
$v=~s/-/_/g;
## Print the variable name and its value ($foo="bar")
print "$v=\"$_\""