이 추악한 결과를 어떻게 아름답고 유용한 데이터로 바꾸나요?
산출:
/* ---------- TA#box#AbC_p ---------- */
insert_job: TA#box#AbC_p job_type: a
#owner: bob
permission: gx
date_conditions: 1
days_of_week: su
start_times: "16:15"
run_window: "16:15-17:30"
description: "Job AbC that runs at 4:15PM on Sundays, and should end before 5:30PM"
/* ---------- TA#cmd#EfGJob_p ---------- */
insert_job: TA#cmd#EfGJob_p job_type: b
box_name: TA#box#AbC_p
command: /path/to/shell/script.sh
machine: vm_machine1
#owner: alex
permission: gx
date_conditions: 2
run_window: "16:20-16:30"
description: "job EfG that runs within box AbC"
term_run_time: 60
std_out: /path/to/log.log
std_err: /path/to/err.log
alarm_if_fail: 1
profile: /path/to/profile
잠깐만요. #cmd# 작업은 때때로 #box# 아래에 위치합니다. #cmd# 섹션은 #box# 아래에 있으면 들여쓰기됩니다.
내 이상적인 출력은 다음과 같습니다.
"Job Name", "Time", "Schedule", "Machine", "Description", "Command"
"TA#box#AbC_p", "16:15", "su", "", "Job AbC that runs at 4:15PM on Sundays, and should end before 5:30PM", ""
"TA#cmd#EfGJob_p", "16:15", "su", "vm_machine1", "job EfG that runs within box AbC", "/path/to/shell/script.sh"
awk, perl 및 grep을 시도하고 있지만 CSV 행을 인쇄하기 전에 "섹션"에 대한 모든 정보를 함께 얻을 수 없습니다.
답변1
조금 무서운 sed oneliner :
sed -n \
# we divide out incoming text to small parts,
# each one as you mentioned from /---.*box.*/ to /profile/
'/---.*box.*/,/profile/{
# inside of each part we do following things:
# if string matches our pattern we extract
# the value and give it some identifier (which you
# can see is "ij", "st" and so on)
# and we copy that value with identifier to hold buffer,
# but we don't replace the content of hold buffer
# we just append (capital H) new var to it
/insert_job/{s/[^:]*: /ij"/;s/ .*/",/;H};
/start_times/{s/[^:]*: /st/;s/$/,/;H};
/days_of_week/{s/[^:]*: /dw"/;s/$/",/;H};
/machine/{s/[^:]*: /ma"/;s/$/",/;H};
/description/{s/[^:]*: /de/;s/$/,/;H};
/command/{s/[^:]*: /co"/;s/$/",/;H};
# when line matches next pattern (profile)
# we think that it is the end of our part,
# therefore we delete the whole line (s/.*//;)
# and exchange the pattern and hold buffers (x;)
# so now in pattern buffer we have several strings with all needed variables
# but all of them are in pattern space, therefore we can remove
# all newlines symbols (s/\n//g;). so it is just one string
# with a list of variables
# and we just need to move to the order we want,
# so in this section we do it with several s commands.
# after that we print the result (p)
/profile/{s/.*//;x;s/\n//g;s/ij\("[^"]*box[^"]*",\)/\1/;
s/,\(.*\)st\("[^"]*",\)\(.*ij"[^"]*",\)/,\2\1\3\2/;
s/\([^,]*,[^,]*,\)\(.*\)dw\("[^"]*",\)\(.*ij"[^"]*",[^,]*,\)/\1\3\2\4\3/;
s/de/"",/;s/ij/""\n/;
s/\(\n[^,]*,[^,]*,[^,]*,\)\(.*\)ma\("[^"]*",\)/\1\3\2/;
s/co\("[^"]*"\),\(.*\)/\2\1/;s/de//;p}
};
# the last command just adds table caption and nothing more.
# note: if you want to add some new commands,
# add them before this one
1i"Job Name", "Time", "Schedule", "Machine", "Description", "Command"'
필드 순서는 상자마다 다를 수 있기 때문에 작성했지만 프로필은 항상 마지막입니다. 순서가 항상 같으면 더 쉬울 것입니다.
답변2
저는 Perl을 사용하거나 적어도 awk를 사용하겠습니다.
perl -ne '
BEGIN {
print "\"Job Name\", \"Time\", \"Schedule\", \"Machine\", \"Description\", \"Command\", \"\n";
}
chomp; s/^\s+//; s/\s+$//;
if (($_ eq "" || eof) && exists $fields{insert_job}) {
print "\"", join("\", \"", @fields{qw(insert_job start_times days_of_week machine description command)}), "\"\n";
delete @fields{qw(insert_job)};
}
if (/^([^ :]+): *(.*)/) {$fields{$1} = $2}
'
설명하다:
- 이
BEGIN
블록은 스크립트 시작 부분에서 한 번 실행되고 나머지는 각 입력 줄에 대해 실행됩니다. - 로 시작하는 줄에서
chomp
선행 및 후행 공백을 제거합니다 . - 필드가 있으면 첫 번째
if
줄은 빈 줄에서 실행됩니다(단락 나누기).insert_job
- 이
delete
줄은insert_job
필드를 삭제합니다. 한 단락에서 다음 단락으로 유출하고 싶지 않은 추가 필드 이름을 추가합니다. - 마지막
if
줄에는 필드가 저장됩니다.
답변3
TXR 언어 사용:
@(bind inherit-time nil)
@(bind inherit-sched nil)
@(collect)
@ (all)
@indent/* ---------- @jobname ---------- */
@ (and)
@/ *//* ---------- @nil#@type#@nil ---------- */
@ (end)
@ (bind is-indented @(> (length indent) 0))
@ (gather :vars ((time "") (sched "") (mach "") (descr "") (cmd "")))
@/ */start_times: "@*time"
@/ */days_of_week: @sched
@/ */machine: @mach
@/ */description: "@*descr"
@/ */command: @cmd
@ (until)
@ (end)
@ (cases)
@ (bind type "box")
@ (set (inherit-time inherit-sched) (time sched))
@ (or)
@ (bind type "cmd")
@ (bind is-indented t)
@ (set (time sched) (inherit-time inherit-sched))
@ (end)
@(end)
@(output)
"Job Name", "Time", "Schedule", "Machine", "Description", "Command"
@ (repeat)
"@jobname", "@time", "@sched", "@mach", "@descr", "@cmd"
@ (end)
@(end)
이것은 매우 유치한 접근 방식입니다. 각 레코드에서 관심 있는 모든 필드를 추출하고 존재하지 않는 필드를 공백(매개변수의 기본값 :vars
) 으로 바꿉니다 @(gather)
. 우리는 작업 유형( box
또는 cmd
)과 들여쓰기에 중점을 둡니다. 상자를 볼 때 일부 상자 속성을 전역 변수에 복사합니다. 들여쓰기된 cmd를 보면 해당 속성이 복사됩니다. (우리는 이전에 누군가가 설정했다고 맹목적으로 가정합니다 box
.)
달리기:
$ txr jobs.txr jobs
"Job Name", "Time", "Schedule", "Machine", "Description", "Command"
"TA#box#AbC_p", "16:15", "su", "", "Job AbC that runs at 4:15PM on Sundays, and should end before 5:30PM", ""
"TA#cmd#EfGJob_p", "16:15", "su", "vm_machine1", "job EfG that runs within box AbC", "/path/to/shell/script.sh"
출력은 쉼표로 구분된 따옴표 필드 목록이지만 데이터에 따옴표가 포함될 가능성에 대해 아무런 조치도 취하지 않습니다. 따옴표가 어떤 방식으로든 이스케이프되면 description:
당연히 보존됩니다. 이 @*descr
표현은 탐욕적 일치이므로 description: "a b"c\"d"
출력에서 그대로 재현될 문자를 사용하게 됩니다 descr
.a b"c\"d
이 솔루션의 이점은 데이터 예제가 없는 경우 전체 파일에 걸쳐 순서가 지정된 패턴 일치를 표현하므로 코드 구조에서 대부분의 데이터를 추측할 수 있다는 것입니다. 수집되는 섹션은 작업 이름이 포함된 줄 /* --- ... --- */
과 작업 이름 중간에 있는 두 해시 태그 사이에 유형 필드로 시작하는 것을 볼 수 있습니다. 그런 다음 필수 빈 줄이 있고 그 후에는 다른 빈 줄까지 속성이 수집됩니다.