代わりに出力にセパレータを保持する「column -t」のようなコマンド


17

単純なテーブルを編集しています。きれいにフォーマットしてもらいたいです。、、または同様のものを使用できますがtbllatexこれはやり過ぎのようです。プレーンテキストで十分です。単純なので、ソースを出力にすることもできます。したがって、ソースも見栄えが良いはずです。これは、完璧な仕事のように思えますcolumn -s '|' -t-セパレータを見つけ、各列の最大幅に従って整列するスペースを自動的に挿入します。残念ながら、セパレータは削除されるため、さらに編集した後は再実行できません。出力を入力として機能させるために、これをべき等で実行できる優れたテキスト処理ツールはありますか?または、自分で書く必要がありますか?

編集:ここに私が欲しいものの例があります:

foo |   bar | baz
abc def | 12 | 23456

になるはずです

foo     | bar | baz
abc def | 12  | 3456

ときに' '、セパレータとスペーサの両方があり、column -tうまく動作します。しかし、私のアイテムにはスペースが含まれているため、それを使用することはできません。スペーサーをセパレーターと区別することは、事態を複雑にします。セパレータの隣でセパレータ文字として扱うのは便利だと思いますが、そうでcolumn -s '|' -tはありません(明らかに現在の動作も有用です)。


emacs org-modeを使用できます。テーブルのサポートは実際には非常に優れており、スプレッドシートのような機能を提供します。
-vschum

私が合理的だと思ったほど一般的ではありませんが、leancrew.com / all-this / 2008/08 / tables-for-markdown-and-textmateにはマークダウンテーブル専用のpythonプログラムがあります。
wnoise

これは、少なくとも2週間ごとに遭遇する問題です。printfホロコーストを毎回バイパスする唯一の実行可能な解決策は、これまでのところ、@データに一意の文字(など)を追加し、... | column -s@ -t後で使用することです。
sjas

回答:


17

私はあなたの問題が何であるかを正しく理解しているかどうかわかりません。しかし、一時的なセパレータを追加することで解決できますか?したがって、2番目の区切りを使用して区切りをマークし、元の区切りに手を加えないようにすることができます。

「|」のそれぞれに「@」を追加するこの例を参照してください したがって、列コマンドの入力は「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éphaneGimenez:そしてsed 's/ |/|/g'column修正後に追加された余分なスペースを追加します。これで、私にとって十分に機能するソリューションができました。(このような余分な文字に依存していなければいいのですが。もしも利用できない場合はどうなりますか?)
wnoise

3
@wnoise:@の代わりに、低ASCII値など、通常はテキストに表示されないものを使用できます。$ '\ x01' ...(ただし$ '\ x00'ではない)
...-Peter.O

6

これは質問をしたときには利用できませんでしたが、v。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スクリプトです。'column -t`を使用せず、セパレータはIFS(または少なくともawkのIFSの内部バージョン)であるため、IFSとまったく同じように処理されます...デフォルトの区切り文字は$' \ t 'です

このスクリプトは、右端のフィールドを完全に埋めます。
「列」はこれを行いません。
すべての列をパディングすることにより、このスクリプトを
簡単に変更して、テーブルフレームも作成できます。

注意。入力ファイルを2回処理する必要があります
(「列」もこれを行う必要があります)
最初のパスは、列の最大幅を取得することです。
2番目のパスは、フィールドを(列ごとに)展開することです

いくつかのオプション追加し、明白なバグを修正しました(変数の名前を変更します:(

  • -lインデントされたフィールドの空白を左にトリムします
  • -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の答えの 2パス調整です。これにより、入力データから区切り文字を推測することで、区切り文字をハードコーディングする必要がなくなります。

  1. スペースで囲まれた単一の英数字以外の文字の入力を解析し、最も一般的な文字でソートし、最も一般的な文字が区切り文字であると想定し$dます。
  2. hmonoliuの答えのように多少なりとも進行しますが、PeterOのコメントに従って、の代わりにASCII NULLをパディングとして使用します@

コードは、ファイル名、または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

これを1年後に見ると、STDINを 2回使用するため、STDINの呼び出しは機能しないように思えます。大きなファイル(約8,000万行)でテストすると、明らかに正常に動作していることがわかります。うーん...
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、中間記号を位置合わせを行うスペースに置き換えます
  • 2番目はコマンドのsed後に冗長スペースをクリーンアップしますcolumn

使用例:

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

a   : bb: cccc
aaaa: b : cc

また、それはent等であるという点でも優れています。何度も適用して同じ結果を得ることができます(たとえば、vimで編集して再配置する場合)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.