1

yamlヘッダーの後にマークダウンサブタイトルが続く一連のファイルがあり、次のようになります。

最小限の入力ファイルの例:

---
layout: post
tags: 
  - might 
  - be
  - variable 
  - number 
  - of 
  - these
category: ecology
---



my (h2 size) title
------------------

some text


possible other titles we don't want
-----------------------------------

more text more text

私が示したように、YAMLヘッダーのサイズと最初のサブタイトルが表示される行はさまざまであるため、変更の行番号を事前に知ることは期待できません。最初のタイトルを特定したいと思います(これは、終了後の最初の非空白テキストでもあります---。次に、そのテキストをYAMLヘッダーに書き込み、取得したタイルを本文から削除します。残りのテキストはそのまま残ります:

ターゲット出力ファイル

---
layout: post
tags: 
  - might 
  - be
  - variable 
  - number 
  - of 
  - these
categories: ecology
title: my (h2 size) title
---



some text

possible other titles we don't want
-----------------------------------

more text more text

このような作業はsed/awkなどにとって妥当な作業のようですが、これらのツールの使用法は非常に初歩的なものであり、これを理解することはできませんでした。

単語間を検索できるようですsed 'word1/,/word2/pが、これを変換して、 (ダッシュが3つ以上ある行)の2番目の出現^---$と最初の出現の間を検索する方法がわかりません。^----+-$次に、余分な空白行を削除して、上記のyamlマターに貼り付ける方法。

おそらく、非常に多くのステップがある場合、perlはsedよりも良い選択ですが、私が慣れていないものです。ヒントやアドバイスをありがとう。

4

4 に答える 4

2

2つのパスを実行するだけです。最初のパス(NR == FNRの場合)は前に印刷するタイトルと行番号を検索し、2番目のパスは行番号が適切な場合にそれと他の行を印刷します。

$ cat tst.awk
NR==FNR {
   if (hdrEnd && !title && NF)  {title = $0; titleStart=FNR; titleEnd=FNR+1 }
   if (hdrStart && /^---$/)     {hdrEnd   = FNR }
   if (!hdrStart && /^---$/)    {hdrStart = FNR }
   next
}
FNR == hdrEnd { print "title:", title }
(FNR < titleStart) || (FNR > titleEnd)

$ awk -f tst.awk file file      
---
layout: post
tags: 
  - might 
  - be
  - variable 
  - number 
  - of 
  - these
category: ecology
title: my (h2 size) title
---




some text


possible other titles we don't want
-----------------------------------

more text more text

hdrStartは、ヘッダーが始まる行番号などです。テキストとそれに続くアンダースコアの行だけでなく、タイトルの前後の行をスキップする場合は、titleStartとtitleEndの入力方法をFNR-1とFNR+2などに変更します。 。FNR(File Number of Records)は、現在開いているファイルだけの現在の行番号であり、NR(Number of Records)は、以前に開いたすべてのファイルと現在開いているファイルの合計でこれまでに読み取られた行数です。

コマンドラインでファイル名を2回指定したくない場合は、awksBEGINセクションで複製できます。

$ cat tst.awk             
BEGIN{ ARGV[ARGC++] = ARGV[ARGC-1] }
NR==FNR {
   if (hdrEnd && !title && NF)  {title = $0; titleStart=FNR; titleEnd=FNR+1 }
   if (hdrStart && /^---$/)     {hdrEnd   = FNR }
   if (!hdrStart && /^---$/)    {hdrStart = FNR }
   next
}
FNR == hdrEnd { print "title:", title }
(FNR < titleStart) || (FNR > titleEnd)

次に、スクリプトを次のように呼び出すだけです。

$ awk -f tst.awk file

編集:実際には、2パスアプローチを行わず、ほぼ間違いなく単純な代替手段があります。

$ cat tst.awk
(state == 0) && /^---$/ { state=1; print; next }
(state == 1) && /^---$/ { state=2; next }
(state == 2) && /^./    { state=3; printf "title: %s\n---\n",$0; next }
(state == 3) && /^-+$/  { state=4; next }

state != 2 { print }

$ awk -f tst.awk file
---
layout: post
tags: 
  - might 
  - be
  - variable 
  - number 
  - of 
  - these
category: ecology
title: my (h2 size) title
---

some text


possible other titles we don't want
-----------------------------------

more text more text

ステートマシンに精通している場合は、それが何をしているのかは明らかですが、私に知らせてはいけません。

于 2013-01-16T17:59:45.700 に答える
1

迅速で汚いperlコード:

$/=undef;  # null line delimiter, so that the following reads the full file
my $all=<STDIN>;
my @parts=split(/^(----*)$/m,$all); # split in sections delimited by all-dashes linse
my @head=split("\n",$parts[2]);  # split the header in lines
my @tit=split("\n",$parts[4]);  # split the title section in lines
push @head,pop @tit;            # remove the last line from the title section and append to head
$parts[2]=join("\n",@head)."\n"; # rebuild the header
$parts[4]=join("\n",@tit);       # rebuild the title section
print join("",@parts);           # rebuild all and print to stdout

これは十分に堅牢ではない可能性があります。ダッシュが3つ以上あるかどうかは関係なく、UNIXの改行を想定し、タイトルが空白でないことを確認しません。ただし、開始点として役立つ場合があります。または、これを1回だけ実行する必要がある場合。別のアプローチは、配列内のメモリ内のすべての行を読み取り、区切り文字の行をループして、タイトル行を移動することです。

于 2013-01-13T04:33:15.593 に答える
0

多分このPerlコードはあなたが解決策を見つけるのを助けるでしょう:

#!/usr/bin/env perl

use Modern::Perl;
use File::Slurp;

my @file_content = read_file('test.yml');
my ($start, $stop, $title);
foreach my $line (@file_content) {

    if ($line =~ m{ --- }xms) {
        if (!$start) {
            $start = 1;
        }
        else {
            $stop = 1;
            next;
        }
    }    

    if ($line && $stop && $line = m{\w}xms) {
        $title = $line;
        last;
    }


}

say "Title: $title";

上からのデータを出力: タイトル:my(h2 size)title

于 2013-01-15T22:08:11.680 に答える
0

古き良きPython:

with open("i.yaml") as fp:
    lines = fp.readlines()

c = False
i = 0
target = -1

for line in lines:
    i += 1
    if c:
        if line.strip() != "":
            source = i - 1
            c = False

    if line.strip() == "---":
        if i > 1:
            c = True
            target = i - 1

lines[target:target] = ["title: " + lines[source]]
del lines[source + 1]
del lines[source + 1]

with open("o.yaml", "w") as fp:
    fp.writelines(lines)
于 2013-01-16T18:29:40.587 に答える