Как выровнять текст в файле, чтобы он выглядел как таблица в bash на основе текста шаблона?

#bash #awk #sed #grep

#bash #awk #sed #grep

Вопрос:

У меня есть следующий текст

 '   14411.7647 e0       - 2647.0588 e3         7352.9412 e12        14411.7647 e123       21828.2063'
' - 2647.0588 e3         7352.9412 e12        7814.9002'
'   14411.7647 e0         14411.7647 e123       20381.3131'
'   14411.7647 e0         14411.7647 e123       20381.3131'
'   0.0000 e0         0.0000 e123       1.9293e-12'
'   14411.7647'
  

и я хотел бы выровнять, чтобы он выглядел как таблица, основанная на терминах eXXX. Это может быть примером вывода:

 ' 14411.7647 e0     - 2647.0588 e3        7352.9412 e12       14411.7647 e123   21828.2063'                 
'                   - 2647.0588 e3        7352.9412 e12                          7814.9002'                 
' 14411.7647 e0                                               14411.7647 e123   20381.3131'                 
' 14411.7647 e0                                               14411.7647 e123   20381.3131'                 
'     0.0000 e0                                                   0.0000 e123   1.9293e-12'                 
'                                                                               14411.7647'                                                                                                                                                                         
  

Самая важная часть — выровнять термины eXXX вместе с его коэффициентами.

ОБНОВЛЕНИЕ: столбцы изначально разделены пробелами. Выходные данные могут быть разделены табуляциями, например.

UPDATE2: Первая строка указывает общее количество столбцов. Столбцов не больше, чем в первой строке. Exxx во второй и следующих строках может быть таким же или отличаться от первого, но вы никогда не найдете больше терминов, чем в первой строке, и они не будут неупорядоченными (т. Е. e12 всегда будет после e3)

Можно ли этого добиться с помощью awk или аналогичного?

Комментарии:

1. Какой символ разделяет столбцы? Одна табуляция или несколько пробелов?

2. @Cyrus увеличивает количество пробелов.

3. Добро пожаловать в SO. Stack Overflow — это страница вопросов и ответов для профессиональных программистов-энтузиастов. Добавьте свой собственный код к вашему вопросу. Ожидается, что вы продемонстрируете, по крайней мере, объем исследований, которые вы провели для самостоятельного решения этого вопроса.

4. несколько пробелов? Как я узнаю, должно ли во второй строке - 2647.0588 e3 быть во втором столбце? Почему не в первом столбце? Почему не четвертый или третий столбец? Что является ключом к сортировке, какое поле переходит в какой столбец? Являются exx постоянными в файле? Ожидается ли, что мы будем искать по всему файлу все возможные eXX поля, а затем решать, сколько столбцов там должно быть? Что, если e3 поле находится после e12 в одной строке? Должны ли мы тогда изменить порядок?

5. Я обновил (UPDATE2) свой вопрос, чтобы прояснить ваш комментарий @KamilCuk

Ответ №1:

 $ cat tst.awk
BEGIN { OFS="t" }
{
    # Get rid of all single quotes at the start/end of lines
    gsub(/^47|47$/,"")

    # Attach the  /- sign when present to the number to its right
    # to normalize how the fields are presented on each line.
    gsub(/  /," ")
    gsub(/- /,"-")
}
NR==1 {
    # Consider each pair like "14411.7647 e0" to be one field with
    # "e0" as the key that determines the output order for that field
    # and "14411.7647" as the value associated with that key. Here
    # we create an array that remembers the order of the keys.
    for (i=1; i<=NF; i =2) {
        key = $(i 1)
        fldNr2key[  numFlds] = key
    }
}
{
    # Populate an array that maps the key to its value
    delete key2val
    for (i=1; i<=NF; i =2) {
        key = $(i 1)
        val = $i
        key2val[key] = val
    }

    # Print the values by the order of the keys
    out = ""
    for (fldNr=1; fldNr<=numFlds; fldNr  ) {
        key = fldNr2key[fldNr]
        fld = ""
        if (key in key2val) {
            val = key2val[key]
            fld = val (key ~ /./ ? " " key : "")
            sub(/^[- ]/,"amp; ",fld) # restore the blank after a leading  /-
        }
        out = out fld (fldNr<numFlds ? OFS : "")
    }
    print "47 " out "47"
}
  

Вывод с разделением табуляцией:

 $ awk -f tst.awk file
' 14411.7647 e0 - 2647.0588 e3    7352.9412 e12   14411.7647 e123       21828.2063'
'       - 2647.0588 e3    7352.9412 e12         7814.9002'
' 14411.7647 e0                   14411.7647 e123       20381.3131'
' 14411.7647 e0                   14411.7647 e123       20381.3131'
' 0.0000 e0                       0.0000 e123   1.9293e-12'
'                               14411.7647'
  

Визуально табличный вывод (или используйте printfs с соответствующей шириной для каждого поля в скрипте):

 $ awk -f tst.awk file | column -s$'t' -t
' 14411.7647 e0  - 2647.0588 e3    7352.9412 e12    14411.7647 e123  21828.2063'
'                - 2647.0588 e3    7352.9412 e12                     7814.9002'
' 14411.7647 e0                                     14411.7647 e123  20381.3131'
' 14411.7647 e0                                     14411.7647 e123  20381.3131'
' 0.0000 e0                                         0.0000 e123      1.9293e-12'
'                                                                    14411.7647'
  

Комментарии:

1. очень приятное и элегантное решение.

2. спасибо, это также работало в Linux. В любом случае, не тот результат в mac os : (

3. Всегда пожалуйста. Да, / / поведение не определено для POSIX, поэтому оно будет работать только в некоторых awks. Я исправил это сейчас, чтобы / /

Ответ №2:

Похоже, поля могут быть разделены несколькими пробелами, тогда вы можете попробовать использовать FS = « *47 *| «, таким образом, ваши конечные ожидаемые строки (на основе NR==1 ) могут быть разделены на столбцы eXXX (от $2 до $(NF-2) ), обычный столбец, если он существует в $(NF-1) . и $ 1, и $ NF всегда ПУСТЫ.

 $ cat t17.1.awk
BEGIN{ FS = " *47 *|   "; OFS = "t"; }

# on the first line, set up the total N = NF
# the keys and value lengths for the 'eXXX' cols 
# to sort and format fields for all rows
NR == 1 {
    N = NF
    for (i=2; i < N-1; i  ) {
        n1 = split($i, a, " ")
        e_cols[i] = a[n1]
        e_lens[i] = length($i)
    }
    # the field-length of the regular column which is non eXXX-cols
    len_last = length($(NF-1))
}

{
    printf "47 "
    # hash the e-key for field from '2' to 'NF-1'
    # include NF-1 in case the last regular column is missing
    for (i=2; i < NF; i  ) {
        n1 = split($i, a, " ")
        hash[a[n1]] = $i
    }

    # print the eXXX-cols based on the order as in NR==1
    for (i=2; i < N-1; i  ) {
        printf("%*s%s", e_lens[i], hash[e_cols[i]], OFS)
    }

    # print the regular column at $(NF-1) or EMPTY if it is an eXXX-cols
    printf("%*s47n", len_last, match($(NF-1),/ e[0-9] $/)?"":$(NF-1))

    # reset the hash
    delete hash
}
  

Запустите приведенный выше скрипт, и вы получите следующий результат: (Обратите внимание, я добавил одну дополнительную строку, так что eXXX-cols 14411.7647 e123 находится в конце строки перед завершающей ' )

 $ awk -f t17.1.awk file.txt 
' 14411.7647 e0 - 2647.0588 e3    7352.9412 e12   14411.7647 e123       21828.2063'
'               - 2647.0588 e3    7352.9412 e12                          7814.9002'
' 14411.7647 e0                                   14411.7647 e123       20381.3131'
' 14411.7647 e0                                   14411.7647 e123       20381.3131'
'     0.0000 e0                                       0.0000 e123       1.9293e-12'
'                                                                       14411.7647'
'                                                 14411.7647 e123                 '
  

Примечание:

  • возможно, вам понадобится gawk для "%*s" работы с printf() , если это не работает, попробуйте использовать фиксированное число, например: printf("s%s", hash[e_cols[i]], OFS)

  • некоторые значения в электронных столбцах могут иметь больший размер, чем соответствующее значение при NR ==1, чтобы исправить это, вы можете вручную указать массив длин или просто использовать фиксированное число