Befehl wie `column -t`, der stattdessen Trennzeichen in der Ausgabe beibehält

17

Ich bearbeite eine einfache Tabelle. Ich hätte es gerne schön formatiert. Während ich verwenden könnte tbl, latexoder ähnliche, dies scheint übertrieben - Klartext wirklich ausreichend ist. Da es einfach ist, könnte ich genauso gut die Quelle als Ausgabe haben. Die Quelle sollte also auch gut aussehen. Dies scheint ein perfekter Job zu sein column -s '|' -t- es findet die Trennzeichen und fügt automatisch Leerzeichen ein, um sie entsprechend der maximalen Breite in jeder Spalte auszurichten. Leider werden die Trennzeichen gelöscht, sodass ich sie nach der weiteren Bearbeitung nicht erneut ausführen kann. Gibt es ein gutes Textverarbeitungswerkzeug, das dies idempotent ausführen kann, sodass die Ausgabe als Eingabe dient? Oder muss ich meine eigene schreiben?

BEARBEITEN: Hier ist ein Beispiel für das, was ich will:

foo |   bar | baz
abc def | 12 | 23456

soll werden

foo     | bar | baz
abc def | 12  | 3456

Wann ' 'ist sowohl der Separator und der Spacer, column -tfunktioniert gut. Aber meine Gegenstände haben Leerzeichen, deshalb kann ich das nicht verwenden. Es erschwert die Sache, wenn die Abstandshalter von den Abstandshaltern verschieden sind. Ich denke, es ist nützlich, sie als Trennzeichen neben Trennzeichen zu behandeln, aber das ist nicht das, was es column -s '|' -ttut (obwohl das aktuelle Verhalten natürlich auch nützlich ist).

wnoise
quelle
Du könntest den emacs org-mode benutzen. Die Tabellenunterstützung ist tatsächlich erstaunlich und bietet Funktionen, die denen von Tabellenkalkulationen ähneln.
Vschum
Nicht so allgemein, wie ich es für sinnvoll hielt, aber es gibt ein Python-Programm speziell für Markdown-Tabellen unter leancrew.com/all-this/2008/08/tables-for-markdown-and-textmate .
14.
Das ist ein Problem, auf das ich mindestens alle zwei Wochen stoße. Die einzige praktikable Lösung, um den printfHolocaust jedes Mal zu umgehen , die ich bisher gefunden habe, besteht darin, @den Daten einen eindeutigen Buchstaben ("like" ) hinzuzufügen und ... | column -s@ -tanschließend zu verwenden.
Sjas

Antworten:

17

Ich bin mir nicht sicher, ob ich richtig verstanden habe, was dein Problem ist. Aber kann es durch Hinzufügen eines zusätzlichen zeitlichen Trennzeichens gelöst werden? Daher können Sie das zweite Trennzeichen verwenden, um die Separationen zu markieren, wobei das ursprüngliche Trennzeichen unverändert bleibt.

In diesem Beispiel füge ich jedem "|" ein "@" hinzu. Die Eingabe des Spaltenbefehls wäre also "xxx @ | yyyy". Die Spalte verarbeitet das "@", wobei das "|" unberührt:

~$ echo "foo | this is some text | bar" | sed 's/|/@|/g'  | column -s '@' -t
foo   | this is some text   | bar
hmontoliu
quelle
Klug. Tut fast das, was ich will, und tut tatsächlich das, worum ich gebeten habe - lässt die Trennzeichen an. Ich möchte auch, dass die Leerzeichen neben den echten Trennzeichen nach unten und nicht nur nach oben angepasst werden können, wie hier.
14.
@wnoise: Verwenden Sie sed 's/ *| */@| /g'stattdessen
Stéphane Gimenez
@Stéphane gimenez: und sed 's/ |/|/g'nach den columnreparaturen die zusätzlichen spalte hinzugefügt. Wir haben jetzt eine Lösung, die für mich gut genug funktioniert. (Obwohl es schön wäre, wenn es nicht auf einen zusätzlichen Charakter wie diesen
ankäme
3
@wnoise: Anstelle von @ können Sie etwas verwenden, das normalerweise nicht im Text enthalten ist, z. B. einen niedrigen ASCII-Wert, z. $ '\ x01' ... (aber nicht $ '\ x00') ...
Peter.O
6

Dies war nicht verfügbar, als Sie die Frage stellten, aber ab Version 2.23 column von util-linuxkönnen Sie das Ausgangstrennzeichen über auswählen

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

Also einfach ausführen:

 column -s '|' -o '|' -t infile
don_crissti
quelle
Beachten Sie, dass die util-linuxVersion zum Zeitpunkt des Schreibens nicht auf Ubuntu 18.04 (und wahrscheinlich anderen von Debain abgeleiteten Distributionen) verfügbar ist. Nur die bsdmainutilsVersion ist verfügbar. Die bsdmainutilsVersion unterstützt keine Ausgabeformatierung.
Htaccess
5

Hier ist ein Bash-Skript. "Column -t" wird nicht verwendet, und das Trennzeichen wird genauso behandelt wie das IFS, da es sich um das IFS handelt (oder zumindest um die interne Version des IFS von awk) ... Das Standardtrennzeichen ist $ '\ t'.

Dieses Skript füllt das Feld ganz rechts vollständig aus.
'column' macht das nicht.
Durch Ausfüllen aller Spalten kann dieses Skript
problemlos geändert werden, um auch einen Tabellenrahmen zu erstellen.

Hinweis. Die Eingabedatei muss zweimal verarbeitet werden
(dies müsste auch für 'column' durchgeführt werden).
Der erste Durchgang besteht darin, die maximalen Spaltenbreiten zu ermitteln.
Der zweite Durchgang ist das Erweitern von Feldern (pro Spalte)

Einige Optionen hinzugefügt und ein eklatanter Fehler behoben (Umbenennen von Variablen :(

  • -l Leerzeichen in eingerückten Feldern links abschneiden
  • -r Mit der rechten Maustaste Leerzeichen kürzen, die breiter als der breiteste Text sind (für die Spalte)
  • -b Sowohl -l als auch -r
  • -L Linkes Ausgabetrennzeichen wird hinzugefügt
  • -R Rechter Ausgabetrenner wird hinzugefügt
  • -B Sowohl -L als auch -R
  • -S Wählen Sie den Ausgangstrenner

#!/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
Peter.O
quelle
Schön gemacht. Natürlich hatte ich auf etwas gehofft, das eigentlich kein neues Programm erfordert.
Wnoise
2

Schauen Sie sich das Vim-Plugin namens Tabularize an

:Tabularize /<delim>
Amos Folarin
quelle
1

Dies ist eine zweigängige Änderung der Antwort von hmontoliu , mit der vermieden wird, dass der Begrenzer hart codiert werden muss , indem er aus den Eingabedaten erraten wird.

  1. Analysieren Sie die Eingabe für einzelne nicht-alphanumerische Zeichen, die von Leerzeichen umgeben sind, sortieren Sie sie nach den am häufigsten verwendeten Zeichen und nehmen Sie an, dass das am häufigsten verwendete Zeichen das Trennzeichen ist, das zugewiesen wird $d.
  2. Verfahren Sie mehr oder weniger wie in der Antwort von hmonoliu , verwenden Sie jedoch eine ASCII- NULL als Auffüllung anstelle einer @gemäß PeterOs Kommentar.

Der Code ist eine Funktion, die einen Dateinamen oder eine Eingabe von STDIN akzeptiert :

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 ;
}

Ausgabe von algn foo(oder auch algn < foo):

foo      | bar  | baz
abc def  | 12   | 23456
agc
quelle
Ein Jahr später sieht es so aus, als ob der STDIN- Aufruf nicht funktionieren kann und sollte, da er zweimal STDIN verbraucht . Tests mit großen Dateien (ca. 80 Millionen Zeilen) zeigen an, dass sie anscheinend korrekt funktionieren. Hmm ...
agc
0

Benutzte Idee von hmontoliu , um einfachen Befehl zu implementieren:

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

Kommentar:

  • ${1:-,}- ist ein erstes Argument mit ,der Voreinstellung
  • der erste sedfügt ein Zwischensymbol ein ( $interm2. Argument oder ~standardmäßig)
  • columnErsetzt dann das Zwischensymbol durch Leerzeichen, die ausgerichtet werden
  • Die zweite sedOption bereinigt die redundanten Leerzeichen nach dem columnBefehl

Anwendungsbeispiel:

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

a   : bb: cccc
aaaa: b : cc

Das ist auch insofern gut, als es idempotent ist: Sie können es mehrmals anwenden und das gleiche Ergebnis erzielen (zum Beispiel, wenn Sie in vim bearbeiten und neu ausrichten).

Alexey
quelle