Bunun yerine ayırıcıları çıktıda tutan "sütun -t" gibi komut


17

Basit bir tablo düzenliyorum. Güzel biçimlendirilmiş olmasını istiyorum. Kullanabilseydim tbl, latexya da benzer bir şey olsa da, bu aşırıya kaçmış gibi görünüyor - düz metin gerçekten yeterli. Basit olduğu için ben de kaynağın çıktısı olabilir. Yani kaynak da iyi görünmeli. Bu, mükemmel bir iş olması gerektiği gibi görünüyor column -s '|' -t- ayırıcıları bulur ve her sütundaki maksimum genişliğe göre hizalamak için otomatik olarak boşluklar ekler. Ne yazık ki, ayırıcıları siler, bu yüzden daha fazla düzenleme yaptıktan sonra tekrar çalıştıramazım. Çıktı girdi olarak işlev görmesi için bunu idempotently yapabilen iyi bir metin işleme aracı var mı? Yoksa kendim yazmak zorunda mıyım?

EDIT: İşte ne istediğime bir örnek:

foo |   bar | baz
abc def | 12 | 23456

olmalı

foo     | bar | baz
abc def | 12  | 3456

' 'Hem ayırıcı hem de aralayıcı ne zaman column -tgüzel çalışır. Ama eşyalarımın içinde boşluk var, bu yüzden kullanamam. Ara parçaların ayırıcılardan farklı olması işleri zorlaştırır. Ben ayırıcıların yanında ayırıcı karakter olarak muamele etmek için yararlı olduğunu düşünüyorum, ama ne column -s '|' -tolduğu (açıkçası mevcut davranış da yararlı olsa da).


Emacs org-mode kullanabilirsiniz. Tablo desteği aslında oldukça şaşırtıcı ve elektronik tablo gibi işlevsellik sağlıyor.
vschum

Düşündüğüm kadar genel değil, ama özellikle leancrew.com/all-this/2008/08/tables-for-markdown-and-textmate adresindeki işaretleme tabloları için bir python programı var .
wnoise

Bu en az iki haftada bir karşılaştığım bir problem. printfŞimdiye kadar bulduğum her seferinde soykırımın atlanması için tek geçerli çözüm @, verilere benzersiz bir karakter (beğen ) eklemek ve ... | column -s@ -tdaha sonra kullanmaktır .
sjas

Yanıtlar:


17

Sorunun ne olduğunu doğru anladığımdan emin değilim. Ancak, ek bir zamansal ayırıcı eklenerek çözülebilir mi? bu nedenle, orijinal ayırıcıya dokunmadan, ayırmaları işaretlemek için ikinci ayırıcıyı kullanabilirsiniz.

"|" Öğelerinin her birine "@" eklediğim bu örneğe bakın bu nedenle sütun komutunun girişi "xxx @ | yyyy" olur. Sütun "|" ile "|" bakir:

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

Zeki. Neredeyse istediğimi yapıyor ve aslında istediğimi yapıyor - ayırıcıları içeri bırakıyor. Ayrıca, gerçek ayırıcıların yanındaki boşlukların, burada olduğu gibi, yukarı değil, ayarlanabilmesini istiyorum.
wnoise

@wnoise: sed 's/ *| */@| /g'bunun yerine kullanın
Stéphane Gimenez

Stéphane Gimenez @: Ve ekleyerek sed 's/ |/|/g'sonra columndüzeltmeleri fazladan boşluk ekledi. Artık benim için yeterince iyi çalışan bir çözümümüz var. (Böyle bir ekstra karaktere bağlı olmasaydı güzel olurdu. Ya mevcut değilse?)
wnoise

3
@wnoise: @ yerine, düşük ASCII değeri gibi, genellikle metinde görünmeyen bir şey kullanabilirsiniz, örn. $ '\ x01' ... (ancak $ '\ x00' değil) ...
Peter.O

6

Soruyu sorduğunuzda bu mevcut değildi, ancak v. 2.23'ten column itibaren util-linuxçıkış ayırıcısını

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

Basitçe çalıştırın:

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

Not util-linuxsürümü Ubuntu 18.04 kullanılamaz yazma anda (ve muhtemelen diğer Debain dağıtımlar türetilmiş). Yalnızca bsdmainutilssürüm kullanılabilir. bsdmainutilsVersiyon çıktı biçimlendirme desteklemez.
htaccess

5

İşte bir bash betiği. 'Column -t` kullanmaz ve ayırıcı tam olarak IFS'de olduğu gibi işlenir, çünkü IFS'dir (veya en azından IFS'nin dahili sürümü) ... Varsayılan sınırlayıcı $' \ t '

Bu komut dosyası en sağdaki alanı tamamen doldurur.
'sütun' bunu yapmaz.
Tüm sütunları doldurarak, bu komut dosyası
bir tablo çerçevesi oluşturmak için kolayca değiştirilebilir.

Not. Giriş dosyasının iki kez işlenmesi gerekir
('sütun' da bunu yapmalıdır)
İlk geçiş, sütun maksimum genişliklerini elde etmektir.
İkinci geçiş alanları genişletmek (sütun başına)

Bazı seçenekler eklendi ve göz kamaştırıcı bir hata düzeltildi (değişkenleri yeniden adlandırma :(

  • -l Girintili alanların sol kenar boşlukları
  • -r En geniş metinden daha geniş boşluk kırpma (sütun için)
  • -b Hem -l hem de -r
  • -L Sol çıkış sınırlayıcı eklendi
  • -R Sağ çıkış sınırlayıcı eklendi
  • -B Hem -L ve -R
  • -S Çıkış ayırıcıyı seçin

#!/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

Güzel yapılmış. Tabii ki, aslında yeni bir program yazmayı gerektirmeyecek bir şey umuyordum.
wnoise


1

Bu iki geçişli bir tweak hmontoliu'nun cevabında , sınırlayıcıyı giriş verilerinden tahmin ederek sabit kodlama ihtiyacını ortadan kaldıran.

  1. boşluklarla çevrili tek alfasayısal olmayan karakterler için girdiyi ayrıştırın, bunları en yaygın olanı sıralayın ve en yaygın karakterin atanmış sınırlayıcı olduğunu varsayalım $d.
  2. hmonoliu'nun cevabında olduğu gibi az ya da çok ilerleyin , ancak doldurma olarak bir ASCII NULL kullanın@ uyarınca, Petero un yorumunu.

Kod, bir dosya adını kabul eden veya STDIN'den :

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

Çıktı algn foo(veya ayrıca algn < foo):

foo      | bar  | baz
abc def  | 12   | 23456

Bir yıl sonra buna bakıldığında, STDIN çağrısının çalışmaması ve çalışmaması gerektiği gibi görünüyor çünkü STDIN'i iki kez kullanıyor . Büyük dosyalarla (yaklaşık 80 milyon satır) test yapılması, dosyanın düzgün çalıştığını gösterir. Hmm ...
agc

0

Basit komut uygulamak için hmontoliu fikri kullanıldı :

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

Yorum Yap:

  • ${1:-,}- ,varsayılan olarak ilk argüman
  • ilki sedbir ara sembol ekler ( $interm2. bağımsız değişken veya ~varsayılan olarak)
  • daha sonra columnara sembolü, hizalama yapan boşluklarla değiştirir
  • ikincisi komuttan sedsonra gereksiz alanları temizlercolumn

Kullanım örneği:

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

a   : bb: cccc
aaaa: b : cc

İdempotent olması da iyidir: birkaç kez uygulayabilir ve aynı sonucu alabilirsiniz (örneğin vim'de düzenlediğinizde ve yeniden hizaladığınızda).

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.