쉘 출력의 여러 세그먼트를 여러 변수로 분할

쉘 출력의 여러 세그먼트를 여러 변수로 분할

여러 개의 쉘 결과를 서로 다른 변수로 분할하고 싶습니다.

다음은 설명하기 위한 센서의 샘플 출력입니다.

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두 번째 세그먼트의 내용을 포함하는 변수를 갖고 싶습니다 .

센서_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__temp1acpitz-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 명령은 다음 쉘 코드를 출력합니다.

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)"이 명령의 출력을 평가하도록 쉘에 지시하는 데 사용하는 코드입니다.

답변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)'

'입력에 작은 따옴표를 허용하지 않으며 이식 가능한 쉘 이름과의 유일한 비호환성은 각 이름의 대시라고 생각합니다. 견적 처리(만약에 대비해서)하다:

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

...또는 서브셸로 스트리밍합니다...

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증가하는 스택에서 편집을 수행하고 각 반복의 결과를 인쇄하는 동시에 원본 입력을 항상 예약된 공간에 유지하는 것이 가능합니다.
  • 내부에자라다상태에서는 sed각 입력 행에 대해 유효한 쉘 이름을 인쇄합니다.

    • sed첫 번째 항목과 마지막 항목 사이의 모든 항목 "[0-9]*: "과 마지막 항목 이후의 모든 항목을 제거하지만 그렇지 않으면 ": "클래스에 없는 모든 문자를 해당 문자로 대체하므로 이름이 재귀적으로 커집니다.^[_[:alnum:]]_(그렇지 않을 때까지).
  • sed tests는 프로세스 중에 성공적으로 편집된 행입니다.자라다로직을 처리하기 전에 스크립트에서 상태를 지정하고 분기합니다.교환상태.

    • 특별한 경우로, 이 test는 마지막 입력 라인 !에서 실행되지 않으므로 다음으로 떨어집니다.$교환진술이 무엇이든 상관 없습니다.

이것교환상태 처리 간x;...;d


  • sede x예약된 공간 버퍼의 마지막 또는 빈 모드 공간 버퍼를 변경하여 다음 입력 단락을 위해 저장된 공간을 지웁니다.(그렇다면).

    • 입력에서 여러 개의 빈 줄이 연속적으로 나타나는 것은 패턴에 부정적인 영향을 미치지 않습니다. 동일한 결과를 얻기 위해 필요한 만큼 공백 유지 공간을 빈 패턴 공간으로 바꾸는 일이 발생할 수 있습니다.
  • sed단락 앞의 빈 줄을 끝으로 바꾸고 공백과 백슬래시를 추가하고 작은따옴표를 즉시 이스케이프합니다.(그렇다면)공백이 아닌 보유 버퍼가 실패하기 전 단락에서상태를 삭제하거나 d빈 상태를 삭제하세요.


이것tDel -> :Del상태는 과 사이에서 유효합니다 :Del;...;D.


  • sed모든 재귀 감소 모드 버퍼를 작은따옴표 쌍으로 묶습니다.

    • 첫 번째 행이 삭제되고, 각 행의 첫 번째 행이 삭제됩니다.반복되지만 마지막 항목은 항상 자체적으로 대체됩니다.
  • 단락 버퍼 일치의 경우 버퍼에 "^[^:]*: " sed포함된 ewline이 처음 발생하기 전에 또 다른 작은따옴표가 추가되고 그 뒤에 공백과 백슬래시가 추가됩니다.\n(그렇다면)print 는 P버퍼의 첫 번째 줄만 출력합니다.

    • 그렇지 않으면 sed p비어 있지 않은 버퍼 전체를 인쇄합니다.
  • 어느 쪽이든, 남아 있는 경우 스크립트의 맨 위로 다시 루프하기 전에 버퍼에서 처음으로 나타나는 ewline을 제거하십시오 sed.D\n

    • 여기에서 버퍼가 비어 있지 않으면 스크립트 상단(첫 번째 줄이 제거된 후)에서 초기 test는 방금 수행한 인용문이 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

...각 단락의 스택에: 먼저 이름, 그 다음 값입니다. 반복할 수 있으며 각 단락은 빈 매개변수로 끝납니다.

여기에는 더 많은 검증이 진행되므로 입력에 관계없이 잘못된 쉘 이름이나 따옴표가 없는 출력 값이 발생할 위험이 없어야 합니다.

이 기능은 입력에 대해 작동합니다.

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다음을 알려주는 셸을 사용하여 읽어보세요.

$ 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=\"$_\""

관련 정보