将Unix`cal`输出转换为乳胶表代码:单线解决方案?

时间:2012-02-05 13:20:17

标签: perl bash shell sed awk

试图实现以下目标让我苦苦思索:

将Unix cal输出转换为乳胶表代码,使用短而甜的单行(或几行)。

E.g cal -h 02 2012 | $magicline应该产生

Mo      &Tu     &We     &Th     &Fr     \\
        &       & 1     & 2     & 3     \\
 6      & 7     & 8     & 9     &10     \\
13      &14     &15     &16     &17     \\
20      &21     &22     &23     &24     \\
27      &28     &       &       &       \\

到目前为止,我能想出的唯一合理解决方案是

cal -h | sed -r -e '1d' -e \
  's/^(..)?(...)?(...)?(...)?(...)?(...)?(...)?$/\2\t\&\3\t\&\4\t\&\5\t\&\6\t\\\\/'

......我真的很努力。关于它的好处是,它简单易懂,易于理解,它是“不灵活”(它无法应付8周的一周)和一点点冗长。我正在寻找可供学习的替代解决方案; - )

编辑:找到另一个似乎可以接受的

cal -h | tail -n +2 |
    perl -ne 'chomp;
        $,="\t&";
        $\="\t\\\\\n";
        $line=$_;
        print map {substr($line,$_*3,3)} (1..5)'

编辑:好的:

cal -h | perl \
    -F'(.{1,3})' -ane \
        'BEGIN{$,="\t&";$\="\t\\\\\n"}
            next if $.==1;
            print @F[3,5,7,9,11]'

7 个答案:

答案 0 :(得分:3)

在OS-X上测试:

cal 02 2012 |grep . |tail +2 |perl -F'/(.{3})/' -ane \
    'chomp(@F=grep $_,@F); $m=$#F if !$m; printf "%s"."\t&%s"x$m."\t\\\\\n", @F;'

其中cal输出有3个字符的列;可以更改{3}以匹配您的cal输出。

答案 1 :(得分:2)

使用awk的GNU版本:

我使用英语cal的{​​{1}}输出。

命令:

LANG

输出:

LANG=en_US cal

February 2012 Su Mo Tu We Th Fr Sa 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 一行:

awk

结果:

LANG=en_US cal | awk '
BEGIN { 
  FIELDWIDTHS = "3 3 3 3 3 3 3"; 
  OFS = "&";
} 
FNR == 1 || $0 ~ /^\s*$/ { next } 
{ 
  for (i=2; i<=6; i++) { 
    printf "%-3s%2s", $i, i < 6 ? OFS : "\\\\";
  } 
  printf "\n";
}'

答案 2 :(得分:1)

cal 02 2012|perl -lnE'$.==1||eof||do{$,="\t&";$\="\t\\\\\n";$l=$_;print map{substr($l,$_*3,3)}(1..5)}'

我的新宠:

cal 02 2012|perl -F'(.{1,3})' -anE'BEGIN{$,="\t&";$\="\t\\\\\n"}$.==1||eof||do{$i//=@F;print@F[map{$_*2-1}(1..$i/2)]}'

答案 3 :(得分:0)

这可能对您有用:

cal | sed '1d;2{h;s/./ /g;x};/^\s*$/b;G;s/\n/ /;s/^...\(.\{15\}\).*/\1/;s/.../ &\t\&/g;s/\&$/\\\\/' 

答案 4 :(得分:-1)

遵循awk 1衬里应该有效:

cal 02 2012 | awk 'NR>1 && NF>0 {
  if (NR==2)
      col=NF;
   j=1;
   if (NR==3 && NF<col) {
      if (NF<col)
         printf(" & ");
      for(j=2; j<=col-NF; j++)
         printf("   &");
   }
   for (i=j; i<=col; i++) {
      if (j==1 && i==j)
         printf("%2s ", $(i-j+1));
      else
         printf(" %3s", "&" $(i-j+1));
   }
   printf(" \\\\\n");
}'

<强>输出:

Su  &Mo &Tu &We &Th &Fr &Sa \\
 &    &   &  &1  &2  &3  &4 \\
 5   &6  &7  &8  &9 &10 &11 \\
12  &13 &14 &15 &16 &17 &18 \\
19  &20 &21 &22 &23 &24 &25 \\
26  &27 &28 &29   &   &   & \\

答案 5 :(得分:-1)

cal -h 02 2012| cut -c4-17 | sed -r 's/(..)\s/\0\t\&/g' | sed 's/$/\t\\\\/' | head -n-1 | tail -n +2

这将产生:

Mo     &Tu     &We     &Th     &Fr    \\
       &       & 1     & 2     & 3    \\
 6     & 7     & 8     & 9     &10    \\
13     &14     &15     &16     &17    \\
20     &21     &22     &23     &24    \\
27     &28     &29     &       &      \\

您可以轻松地将\t替换为您希望的空格数

答案 6 :(得分:-1)

这适用于我cal的实现,它使用四个字符的列,并有一个显示月份和年份的初始标题行

cal | perl -pe "next if $.==1;s/..../$&&/g;s/&$/\\\\/"

看起来您的列可能有8个字符,并且没有标题行,在这种情况下

cal | perl -pe "s/.{8}/$&&/g;s/&$/\\\\/"

应该做的伎俩,但要准备好调整它。