Така команда, як `стовпчик -t ', яка замість цього зберігає роздільники у виведенні


17

Я редагую просту таблицю. Мені б хотілося, щоб він був добре відформатований. Хоча я міг би використовувати tbl, latexабо подібне, це здається непосильним - простого тексту насправді достатньо. Як це просто, я б також міг мати джерело. Тож джерело теж повинно виглядати добре. Здається, це має бути ідеальною роботою column -s '|' -t- він знаходить роздільники і автоматично вставляє пробіли, щоб вирівняти відповідно до максимальної ширини в кожному стовпчику. На жаль, він видаляє роздільники, тому я не можу повторити його після подальшого редагування. Чи є якийсь хороший інструмент для обробки тексту, який може зробити це безвідмовно, щоб його вихід служив як вхід? Або мені потрібно написати своє?

EDIT: Ось приклад того, що я хочу:

foo |   bar | baz
abc def | 12 | 23456

повинні стати

foo     | bar | baz
abc def | 12  | 3456

Коли ' 'є і роздільник, і прокладка, column -tдобре працює. Але в моїх предметах є пробіли, тому я не можу цим користуватися. Те, що розпірки відрізняються від роздільників, ускладнює речі. Я думаю, що корисно, щоб вони ставились до символів-розділювачів, коли поруч із роздільниками, але це не так column -s '|' -t(хоча очевидно, що поточна поведінка також корисна).


Ви можете використовувати emacs org-mode. Підтримка таблиць насправді є досить дивовижною, забезпечує функціональність електронних таблиць.
vschum

Не настільки загальне, як те, що я вважав розумним, але є програма python, спеціально для розмітки таблиць на leancrew.com/all-this/2008/08/tables-for-markdown-and-textmate .
wnoise

Це проблема, з якою я стикаюся, як мінімум, кожні два тижні. Єдине життєздатне рішення для обходу printfголокосту кожного разу, яке я знайшов до цього часу, - це додавання унікальних знаків (як @) до даних та використання їх ... | column -s@ -tзгодом.
sjas

Відповіді:


17

Не впевнений, чи правильно я розумію, у чому ваша проблема. Але чи можна це вирішити, додавши додатковий часовий роздільник? отже, ви можете використовувати другий сепаратор для позначення розділів, зберігаючи початковий сепаратор недоторканим.

Дивіться цей приклад, де я додаю "@" до кожного з "|" тому введення команди стовпця буде "xxx @ | yyyy". Стовпець обробляє "@", зберігаючи "|" недоторканий:

~$ echo "foo | this is some text | bar" | sed 's/|/@|/g'  | column -s '@' -t
foo   | this is some text   | bar

Розумний. Майже робить те, що я хочу, і фактично робить те, про що я просив, - залишає роздільники. Я також хочу, щоб пробіли поруч із справжніми роздільниками можна було регулювати вниз, а не просто вгору, як тут.
wnoise

@wnoise: використовуйте sed 's/ *| */@| /g'натомість
Stéphane Gimenez

@ Stéphane Gimenez: І додавання sed 's/ |/|/g'після columnвиправлення доданих додаткових пробілів. Зараз у нас є рішення, яке досить добре працює для мене. (Хоча було б добре, якби це не залежало від зайвого персонажа, як це. Що робити, якщо такого немає в наявності?)
wnoise

3
@wnoise: Замість @ ви можете використовувати щось, що зазвичай не відображається в тексті, наприклад, низьке значення ASCII, наприклад. $ '\ x01' ... (але не $ '\ x00') ...
Peter.O

6

Ця функція була недоступною, коли ви задавали питання, але станом на т. 2.23 column з програми util-linuxви можете вибрати роздільник виводу через

   -o, --output-separator string
          Specify the columns delimiter for table output (default is two spaces).

Тому просто запустіть:

 column -s '|' -o '|' -t infile

Зауважте, що util-linuxверсія не доступна для Ubuntu 18.04 (та, ймовірно, інших дистрибутивів, отриманих Debain) на момент написання. Доступна лише bsdmainutilsверсія. bsdmainutilsВерсія не підтримує вихід форматування.
htaccess

5

Ось сценарій bash. Він не використовує 'стовпчик -t', і сепаратор обробляється точно так само, як і IFS, тому що це IFS (або, принаймні, внутрішня версія IFS IFK) ... Розмежувач за замовчуванням - $ '\ t'

Цей скрипт повністю прошиває найправіше поле.
"стовпець" цього не робить.
Заміщаючи всі стовпці, цей сценарій може бути
легко змінити, щоб створити і рамку таблиці.

Примітка. Вхідний файл потрібно обробити двічі
(«стовпець» також повинен зробити це)
Перший прохід - це отримати максимальну ширину стовпця.
Другий прохід - це розширення полів (на стовпець)

Додано кілька параметрів та виправлено яскраву помилку (перейменування змінних :(

  • -ліва обрізка пробілів будь-яких відступних полів
  • -r Права обрізка білого простору ширша, ніж найширший текст (для стовпця)
  • -b обидва -l і -r
  • -L Додано лівий вихідний роздільник
  • -R Додано правильний роздільник виводу
  • -B обидва -L і -R
  • -S Виберіть вихідний сепаратор

#!/bin/bash
#
#   script [-F sep] [file]
#
#   If file is not specified, stdin is read 
#    
# ARGS ######################################################################
l=;r=;L=;R=;O=;F=' ' # defaults
for ((i=1;i<=${#@};i++)) ;do
  case "$1" in
    -- ) shift 1;((i--));break ;;
    -l ) l="-l";shift 1;((i-=1)) ;;        #  left strip whitespace
    -r ) r="-r";shift 1;((i-=1)) ;;        # right strip whitespace
    -b ) l="-l";r="-r";shift 1;((i-=1)) ;; # strip  both -l and -r whitespace
    -L ) L="-L";shift 1;((i-=1)) ;;        #  Left output delimiter is added
    -R ) R="-R";shift 1;((i-=1)) ;;        # Right output delimiter is added
    -B ) L="-L";R="-R";shift 1;((i-=1)) ;; # output Both -L and -R delimiters
    -F ) F="$2";shift 2;((i-=2)) ;; # source separator
    -O ) O="$2";shift 2;((i-=2)) ;; # output  separator. Default = 1st char of -F 
    -* ) echo "ERROR: invalid option: $1" 1>&2; exit 1 ;;
     * ) break ;;
  esac
done
#
if  [[ -z "$1" ]] ;then # no filename, so read stdin
  f="$(mktemp)"
  ifs="$IFS"; IFS=$'\n'; set -f # Disable pathname expansion (globbing)
  while read -r line; do
    printf "%s\n" "$line" >>"$f"
  done
  IFS="$ifs"; set +f # re-enable pathname expansion (globbing)
else
  f="$1"
fi
[[ -f "$f" ]] || { echo "ERROR: Input file NOT found:" ;echo "$f" ;exit 2 ; }
[[ -z "$F" ]] && F=' '        # input Field Separator string
[[ -z "$O" ]] && O="$F"       # output Field Separator
                 O="${O:0:1}" #   use  single char only

# MAIN ######################################################################
max="$( # get max length of each field/column, and output them
  awk -vl="$l" -vr="$r" -vL="$L" -vR="$R" -vF="$F" -vO="$O" '
    BEGIN { if (F!="") FS=F }
    { for (i=1;i<=NF;i++) { 
        if (l=="-l") { sub("^[ \t]*","",$i) }
        if (r=="-r") { sub("[ \t]*$","",$i) }
        len=length($i); if (len>max[i]) { max[i]=len } 
        if (i>imax) { imax=i } 
      } 
    }
    END { for(i=1;i<=imax;i++) { printf("%s ",max[i]) } }
  ' "$f" 
)"

awk -vl="$l" -vr="$r" -vL="$L" -vR="$R" -vF="$F" -vO="$O" -v_max="$max" '
  BEGIN { if (F!="") FS=F; cols=split(_max,max," ") }
  { # Bring each field up to max len and output with delimiter
    printf("%s",L=="-L"?O:"")
    for(i=1;i<=cols;i++) { if (l=="-l") { sub("^[ \t]*","",$i) } 
                           if (r=="-r") { sub("[ \t]*$","",$i) }
      printf("%s%"(max[i]-length($i))"s%s",$i,"",i==cols?"":O) 
    } 
    printf("%s\n",R=="-R"?O:"")
  }
' "$f"

# END #######################################################################    
if  [[ -z "$1" ]] ;then # no filename, so stdin was used
  rm "$f"   # delete temp file
fi
exit

Чудово зроблено. Звичайно, я сподівався на те, що насправді не вимагатиме написання нової програми.
wnoise


1

Це два проходи Твік на hmontoliu відповіді «s , що дозволяє уникнути потребує жорсткий код роздільник, вгадуючи його з вхідних даних.

  1. введення синтаксичного аналізу для одиночних не буквено-цифрових символів, оточених пробілами, сортування їх за найпоширенішими, і припустимо, що найпоширенішим символом є роздільник, якому призначено $d .
  2. продовжуйте більш-менш, як у відповіді hmonoliu , але використовуйте ASCII NULL як підкладку, а не @як, згідно коментаря PeterO .

Код - це функція, яка приймає ім'я файлу або вводиться від STDIN :

algn() { 
    d="$(grep -ow '[^[:alnum:]]' "${1:-/dev/stdin}"  | \
         sort | uniq -c | sort -rn | sed -n '1s/.*\(.$\)/\1/p')" ;
    sed "s/ *$d */\x01$d /g" "${1:-/dev/stdin}"  | column -s $'\001' -t ;
}

Вихід algn foo(або також algn < foo):

foo      | bar  | baz
abc def  | 12   | 23456

Зважаючи на це через рік, схоже, виклик STDIN не може і не повинен працювати, оскільки він використовує STDIN двічі. Тестування великих файлів (близько 80 мільйонів рядків) свідчить про те, що він, мабуть, працює правильно. Хм ...
agc

0

Використовується ідея hmontoliu для реалізації простої команди:

#! /bin/bash
delim="${1:-,}"
interm="${2:-\~}"
sed "s/$delim/$interm$delim/g" | column -t -s "$interm" | sed "s/  $delim/$delim/g"

Коментар:

  • ${1:-,}- це перший аргумент за ,замовчуванням
  • перший sedвставляє проміжний символ ( $interm2-й аргумент або ~за замовчуванням)
  • потім columnзамінює проміжний символ пробілами, які роблять вирівнювання
  • другий sedочищає зайві пробіли після columnкоманди

Приклад використання:

$ echo "
a: bb: cccc
aaaa: b : cc
" | align :

a   : bb: cccc
aaaa: b : cc

Це також добре тим, що він ідентичний: ви можете застосувати його кілька разів і отримати той самий результат (наприклад, коли ви редагуєте in vim та переставляєте).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.