Нежелательная пустая строка в файле CSV

У меня есть команда, которую я смог собрать с помощью сообщества Stackoverflow. Теперь у меня есть небольшая проблема со сценарием, это очень небольшая проблема, но она меня беспокоит.

Ниже приведен сценарий, который я использую:

#!/bin/bash

  read -p "Enter /DIR/PATH/FILENAME where you wish to copy the data: " FILENAME
  echo "Enter the JOB_NAME or %SEARCHSTRING%"


 while read -r i;
   do

  awk '
    BEGIN {
    print "\"insert_job\",\"job_type\",\"box_name\",\"command\",\"machine\",\"owner\",\"date_conditions\",\"condition\",\"run_calendar\",\"exclude_calendar\",\"days_of_week\",\"run_window\",\"start_times\",\"start_mins\",\"resources\",\"profile\",\"term_run_time\",\"watch_file\",\"watch_interval\"" }

/job_type/ {
    if (NR>1){printf "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n", jn, jt, box, cmd, mcn, own, dc, c, rc, ec, dow, ruw, st, sm, res, prof, trt, wf, wi} jn = "\""$2"\""; jt = "\""$4"\""; box = "\" \""; cmd = "\" \""; mcn = "\" \""; own = "\" \""; dc = "\" \""; c = "\" \""; rc = "\" \""; ec = "\" \""; dow = "\" \""; ruw = "\" \""; st = "\" \""; sm = "\" \""; res = "\" \""; prof = "\" \""; trt = "\" \""; wf = "\" \""; wi = "\" \""}
    /box_name/ {box = "\""$2"\""}
    /command/ {$0=substr($0,index($0,$2)); cmd = "\""$0"\""}
    /machine/ {mcn = "\""$2"\""}
    /owner/   {own = "\""$2"\""}
    /date_conditions/ {dc = "\""$2"\""}
    /condition/ {$0=substr($0,index($0,$2)); c = "\""$0"\""}
    /run_calendar/ {rc = "\""$2"\""}
    /exclude_calendar/ {ec = "\""$2"\""}
    /days_of_week/ {dow = "\""$2"\""}
    /run_window/ {ruw = "\""$2"\""}
    /start_times/ {gsub("\"",""); st = "\""$2"\""}
    /^start_mins/ {sm = "\""$2"\""}
    /profile/ {prof = "\""$2"\""}
    /term_run_time/ {trt = "\""$2"\""}
    /watch_file/ {wf = "\""$2"\""}
    /watch_interval/ {wi = "\""$2"\""}
    /resources/ {res = "\""$2"\""}
    END{printf "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n", jn, jt, box, cmd, mcn, own, dc, c, rc, ec, dow, ruw, st, sm, res, prof, trt, wf, wi}
' < <(autorep -j $i -q) > $FILENAME.csv

break
done

$i принимает запись wildcard и выдает результат в соответствии с ней.

например: следующие 4 задания имеют в своем названии Test, поэтому я укажу Test% в качестве значения подстановочного знака, и скрипт выдаст результат для всех 4 заданий.

Это тестовые задания, которые я использую:

/* ----------------- Test_A ----------------- */

insert_job: Test_A  job_type: CMD
command: sleep 3000
machine: machine1
owner: user1
permission:
date_conditions: 0
term_run_time: 1
alarm_if_fail: 1
alarm_if_terminated: 1


/* ----------------- Test_B ----------------- */

insert_job: Test_B    job_type: CMD
command: echo
machine: machine2
owner: user2
permission:
date_conditions: 0
description: "Test"
std_out_file: "/tmp/$AUTO_JOB_NAME.$AUTORUN.out"
std_err_file: "/tmp/$AUTO_JOB_NAME.$AUTORUN.err"
max_run_alarm: 1
alarm_if_fail: 0
alarm_if_terminated: 0
send_notification: 1



/* ----------------- Test_c ----------------- */

insert_job: Test_c   job_type: CMD
command: sleep 10
machine: machine3
owner: user3
permission:
date_conditions: 0
alarm_if_fail: 0
alarm_if_terminated: 0


/* ----------------- Test_d ----------------- */

insert_job: Test_d   job_type: CMD
command: ls
machine: machine4
owner: user4
permission:
date_conditions: 0
alarm_if_fail: 1
alarm_if_terminated: 1

Но вот проблема: вывод файла csv имеет пустую строку между именами столбцов и данными, как показано ниже:

"insert_job","job_type","box_name","command","machine","owner","date_conditions","condition","run_calendar","exclude_calendar","days_of_week","run_window","start_times","start_mins","resources","profile","term_run_time","watch_file","watch_interval"
,,,,,,,,,,,,,,,,,,
"Test_A","CMD"," ","sleep 3000","machine1","user1","0","0"," "," "," "," "," "," "," "," ","1"," "," "
"Test_B","CMD"," ","echo","machine2","user2","0","0"," "," "," "," "," "," "," "," "," "," "," "
"Test_c","CMD"," ","sleep 10","machine3","user3","0","0"," "," "," "," "," "," "," "," "," "," "," "
"Test_d","CMD"," ","ls","machine4","user4","0","0"," "," "," "," "," "," "," "," "," "," "," "

Требуемый вывод:

"insert_job","job_type","box_name","command","machine","owner","date_conditions","condition","run_calendar","exclude_calendar","days_of_week","run_window","start_times","start_mins","resources","profile","term_run_time","watch_file","watch_interval"
"Test_A","CMD"," ","sleep 3000","machine1","user1","0","0"," "," "," "," "," "," "," "," ","1"," "," "
"Test_B","CMD"," ","echo","machine2","user2","0","0"," "," "," "," "," "," "," "," "," "," "," "
"Test_c","CMD"," ","sleep 10","machine3","user3","0","0"," "," "," "," "," "," "," "," "," "," "," "
"Test_d","CMD"," ","ls","machine4","user4","0","0"," "," "," "," "," "," "," "," "," "," "," "

Я пытался использовать (NR>=1), но это не работает. Я знаю, что это очень тривиально, но я не могу понять это, может кто-нибудь мне помочь?

Вы смотрели, помогает ли этот ответ вашей проблеме: stackoverflow.com/questions/21489237/…

Zyfella 25.01.2023 14:35

if (NR>1) может быть там, где ваша проблема. В первый раз, когда это утверждение истинно, все ваши переменные для следующего printf будут пустыми, поэтому на выходе будет пустая запись csv. Изменение проверки на if (NR>3) должно избежать вывода этой пустой записи csv.

j_b 25.01.2023 15:58

@j_b if (NR>1) был вашим кодом, который я взял из одного из моих предыдущих постов stackoverflow.com/questions/74808194/…

NecroCoder 25.01.2023 16:49

Не добавляйте дополнительный вопрос в ЭТОТ вопрос, верните его в исходное состояние, когда вы получили ответы, а затем опубликуйте новый вопрос. Вопросы-хамелеоны категорически не приветствуются.

Ed Morton 25.01.2023 20:47
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
88
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вот как надежно (ваш существующий скрипт не будет работать для различных возможных входных значений) и обслуживаемо (если вы хотите добавить/удалить поля для печати или изменить их порядок, просто обновите первый split() аргумент) сделать то, что вы пытаетесь сделать, используя cat file вместо autorep -j $i -q, которого нет в моей системе:

$ cat tst.sh
#!/usr/bin/env bash

cat file |
awk '
    BEGIN {
        OFS = ","
        numTags = split("insert_job job_type box_name command machine owner date_conditions condition run_calendar exclude_calendar days_of_week run_window start_times start_mins resources profile term_run_time watch_file watch_interval",tags)
        for ( tagNr=1; tagNr<=numTags; tagNr++ ) {
            tag = tags[tagNr]
            printf "\"%s\"%s", tag, (tagNr<numTags ? OFS : ORS)
        }
    }

    !NF || /^/\*/ { next }
    { gsub(/^[[:space:]]+|[[:space:]]+$/,"") }

    match($0,/[[:space:]]job_type:/) {
        if ( jobNr++ ) {
            prt()
            delete tag2val
        }

        # save "insert_job" value
        tag = substr($1,1,length($1)-1)
        val = substr($0,length($1)+1,RSTART-(length($1)+2))
        gsub(/^[[:space:]]+|[[:space:]]+$/,"",val)
        tag2val[tag] = val

        # update $0 to start with "job_type" to look like all other input
        $0 = substr($0,RSTART+1)
    }

    {
        tag = val = $0
        sub(/:.*/,"",tag)
        sub(/[^:]+:[[:space:]]*/,"",val)
        tag2val[tag] = val
    }

    END { prt() }

    function prt(    tagNr,tag,val) {
        for ( tagNr=1; tagNr<=numTags; tagNr++ ) {
            tag = tags[tagNr]
            val = tag2val[tag]
            printf "\"%s\"%s", val, (tagNr<numTags ? OFS : ORS)
        }
    }
'

$ ./tst.sh
"insert_job","job_type","box_name","command","machine","owner","date_conditions","condition","run_calendar","exclude_calendar","days_of_week","run_window","start_times","start_mins","resources","profile","term_run_time","watch_file","watch_interval"
"Test_A","CMD","","sleep 3000","machine1","user1","0","","","","","","","","","","1","",""
"Test_B","CMD","","echo","machine2","user2","0","","","","","","","","","","","",""
"Test_c","CMD","","sleep 10","machine3","user3","0","","","","","","","","","","","",""
"Test_d","CMD","","ls","machine4","user4","0","","","","","","","","","","","",""

Вышеупомянутое является упрощенной версией моего ответа на ваш предыдущий вопрос, где я предоставил скрипт, который выводит CSV всех полей вместо приведенного выше, который выводит только определенный список полей.

Непонятно, почему у вас есть цикл, вызывающий autorep в вашем существующем скрипте, но если он вам по какой-то причине нужен, скорее всего, вы должны вызывать autorep в цикле и передавать вывод цикла в awk.

while IFS= read -r i; do
    autorep -j "$i" -q
done |
awk '...'

вместо вызова autorep и передачи его вывода в awk внутри цикла:

while IFS= read -r i; do
    autorep -j "$i" -q |
    awk '...'
done

Вышеприведенное было запущено на этой входной копии/вставленной из вашего вопроса:

$ cat file
/* ----------------- Test_A ----------------- */

insert_job: Test_A  job_type: CMD
command: sleep 3000
machine: machine1
owner: user1
permission:
date_conditions: 0
term_run_time: 1
alarm_if_fail: 1
alarm_if_terminated: 1


/* ----------------- Test_B ----------------- */

insert_job: Test_B    job_type: CMD
command: echo
machine: machine2
owner: user2
permission:
date_conditions: 0
description: "Test"
std_out_file: "/tmp/$AUTO_JOB_NAME.$AUTORUN.out"
std_err_file: "/tmp/$AUTO_JOB_NAME.$AUTORUN.err"
max_run_alarm: 1
alarm_if_fail: 0
alarm_if_terminated: 0
send_notification: 1



/* ----------------- Test_c ----------------- */

insert_job: Test_c   job_type: CMD
command: sleep 10
machine: machine3
owner: user3
permission:
date_conditions: 0
alarm_if_fail: 0
alarm_if_terminated: 0


/* ----------------- Test_d ----------------- */

insert_job: Test_d   job_type: CMD
command: ls
machine: machine4
owner: user4
permission:
date_conditions: 0
alarm_if_fail: 1
alarm_if_terminated: 1

Эд Мортон, ваш скрипт работает очень хорошо, для первого split( ) аргумента, если в тегах есть место, то как их разделить. я имею в виду, что если вместо insert_job я хочу использовать insert job, то как заставить скрипт воспринимать его как одну строку?

NecroCoder 25.01.2023 19:12

Есть несколько разных вещей, которые могут означать, и несколько разных способов реализации решения, но в целом, если вы хотите использовать теги в строке заголовка, которые отличаются от тегов в ваших данных (например, insert job в заголовке, но insert_job в данных ), затем вам нужно предоставить алгоритм или сопоставление между двумя, поэтому примите ответ на вопрос, который вы задали здесь, когда будете готовы это сделать, а затем задайте дополнительный вопрос с образцом ввода/вывода, который демонстрирует ваши требования к этому новому проблема.

Ed Morton 25.01.2023 19:20

Вот рубин для этого:

ruby -e '

scan1=/^/\*[ \ta-zA-Z_-]+\*/\R+([\s\S]*?)(?=(?:^/\*[ \t-]+)|\z)/
scan2=/([^ \t\n\r:]+): ([^ \t\n\r:]+)/

headers=  "insert_job   job_type    box_name    command machine owner   
        date_conditions condition   run_calendar    exclude_calendar    
        days_of_week    run_window  start_times start_mins  resources   
        profile term_run_time   watch_file  watch_interval".
        split.map{|e| "\"#{e}\""}

data=$<.read.
    scan(scan1).
    map{|block| block[0].scan(scan2)}

hsh=Hash.new {|h,k| h[k] = [""]*data.length}

data.each_with_index{|r,i|
    r.to_h.each{|k,v| hsh[k][i]=v}
}

puts headers.join(",")
hsh.values.transpose.
   each{|r| puts r.map{|e| e.match(/^".*"$/) ? e : "\"#{e}\""}.join(",")}

' file 

Отпечатки:

"insert_job","job_type","box_name","command","machine","owner","date_conditions","condition","run_calendar","exclude_calendar","days_of_week","run_window","start_times","start_mins","resources","profile","term_run_time","watch_file","watch_interval"
"Test_A","CMD","sleep","machine1","user1","0","1","1","1","","","","",""
"Test_B","CMD","echo","machine2","user2","0","","0","0","Test","/tmp/$AUTO_JOB_NAME.$AUTORUN.out","/tmp/$AUTO_JOB_NAME.$AUTORUN.err","1","1"
"Test_c","CMD","sleep","machine3","user3","0","","0","0","","","","",""
"Test_d","CMD","ls","machine4","user4","0","","1","1","","","","",""

И вот рубин, чтобы сделать второй:

ruby -e '
scan1=/(\S[\s\S]*?)(?=(?:^$)|\z)/
scan2=/(^[^:]+):[ \t]+(.*)/

data=$<.read.
    scan(scan1).
    map{|block| block[0].scan(scan2)}.
    map{|s| s.to_h}

hsh=Hash.new{|h,k| h[k] = [""]*data.length}
data.each_with_index{|h, i| h.each{|k,v| hsh[k][i]<<v}}

puts hsh.keys.map{|e| "\"#{e}\""}.join(",")
hsh.values.transpose.
    each{|r| puts r.map{|e| e.match(/^".*"$/) ? e : "\"#{e}\""}.join(",")}
' file 

Отпечатки:

"Machine Name","Type","Node Name","Agent Name","Operating System","Agent Release","Agent Build"
"machine1machine2","aa","machine1.testmachine2.test","AGENTAGENT","Windows Server 2012 Windows Server 2012 for amd64","12.012.0","6181, Service Pack 00, Maintenance Level 006181, Service Pack 00, Maintenance Level 00"
"machine1machine2","aa","machine1.testmachine2.test","AGENTAGENT","Windows Server 2012 Windows Server 2012 for amd64","12.012.0","6181, Service Pack 00, Maintenance Level 006181, Service Pack 00, Maintenance Level 00"

Другие вопросы по теме