完全なキーと値のペア

完全なキーと値のペア

背景:

特定の文字列に関連する値を挿入するbashスクリプト(MacOS 10.9.5)を作成したいと思います。スクリプトで可能な各関連値を定義します。

たとえば、リンクテキストに関連するテキストを変数yahooとして定義できます。www.yahoo.com

XX_yahoo="www.yahoo.com"

XX_既存の変数との名前の競合を避けるためにプレフィックスが追加されます。それから私のスクリプトは

\MakeLink[yahoo]{}

そして

\MakeLink[yahoo]{www.yahoo.com}

マクロ角かっこ内のリンクテキストを既存の変数と\MakeLink一致させます。一部のテキストに変数が指定されていない場合は、リンクテキストのタイトルケースを使用します。したがって、

\MakeLink[foo bar]{}

しなければならない

\MakeLink[foo bar]{Foo Bar}

以下のスクリプトは、次の状況を処理します。

  • リンクテキストはいいえスペースがあり、
  • リンクテキスト変数はまだ定義されていません。

質問:

リンクテキストに可能な値の数は数千になり、その中にスペースがある可能性があるため、私の質問は次のとおりです。

  1. これが最善の方法ですか?配列を変数として使用する方が良いでしょうか?
  2. リンクテキストにスペースがある場合はどうすればよいですか?たとえば、私は

    \MakeLink[the google]{}
    

    に置き換えられます

    \MakeLink[the google]{www.google.com}.
    

ノート

  • あると推測できる。ただ\MakeLink1行に1回表示されます。
  • MakeTitleCaseタイトルなど、大文字と小文字が変更されない単語のリストを持つようにマクロを改善する必要がありますが、後で変更できます。

既存のソリューションの既知の問題:

  • \MakeLink前にバックスラッシュを省略しても一致が引き続き発生するため、一致する方法に問題があります。テストケースの最初の段落の最後の行を参照してください。
  • 私のファイル?に1つがあると、何かがsed間違っているようです。
  • リンクテキストにスペースが含まれている場合をどのように処理するのかわかりません。

スクリプト

#!/bin/bash

## Can't have a backslash in the values of these variables, which is ok for my purposes.
XX_yahoo="www.yahoo.com"
XX_google="www.google.com"

function MakeTitleCase {
    echo $(echo "$1" | awk '{for(j=1;j<=NF;j++){ $j=toupper(substr($j,1,1)) substr($j,2) }}1')
}


while read -d $'\n' LINE; do
    ## Extract target which is the text within the square brackets of "\MakeLink[target]{}"
    TARGET=$(echo ${LINE} | sed -e 's?\]{}.*??' -e 's?\MakeLink\[??')
    TEMP=XX_${TARGET}
    if [ -z "${!TEMP}" ]; then
        REPLACEMENT=$(MakeTitleCase "${TARGET}")
    else
        REPLACEMENT=${!TEMP}
    fi

    ## Incorrect handling of leading backslash for the match.
    echo "${LINE}" | sed "s?\MakeLink\[${TARGET}\]{}?\\\MakeLink\[${TARGET}\]{${REPLACEMENT}}?";
done 

exit 0

入力ファイルの例:

A very popular site on the internet was
\MakeLink[yahoo]{} but was surpassed by
\MakeLink[google]{} due to its  
MakeLink[search engine]{}.

Due to its dominance
\MakeLink[the google]{} has had to deal with
\MakeLink[antitrust issues]{}.

現在の出力:

A very popular site on the internet was
\MakeLink[yahoo]{www.yahoo.com} but was surpassed by
\MakeLink[google]{www.google.com} due to its
\MakeLink[search engine]{Search Engine}.

Due to its dominance
\MakeLink[the google]{The Google} has had to deal with
\MakeLink[antitrust issues]{Antitrust Issues}.

希望の出力:

上記の唯一の変更は関連テキストthe googleです。MakeLink[search engine]{}いいえ先行バックスラッシュが欠落して変更されました。

A very popular site on the internet was
\MakeLink[yahoo]{www.yahoo.com} but was surpassed by
\MakeLink[google]{www.google.com} due to its
MakeLink[search engine]{}.

Due to its dominance
\MakeLink[the google]{www.google.com} has had to deal with
\MakeLink[antitrust issues]{Antitrust Issues}.

答え1

Perlが構造に来ます:

#!/usr/bin/perl
use warnings;
use strict;

my %replace = ( yahoo              => 'www.yahoo.com',
                google             => 'www.google.com',
                'search engine'    => 'Search Engine',
                'the google'       => 'The Google',
                'antitrust issues' => 'Antitrust Issues',
              );

while (<>) {
    s/\\MakeLink\[(.*?)\]\{\}/\\MakeLink[$1]{$replace{$1}}/g;
    print;
}

代替ハッシュテーブルを作成し、それを置換に使用します。最新のbashバージョンではハッシュテーブルを作成できますが、sedでは直接使用できないため、直接bash + sed対応エントリはありません。

答え2

nemocaの答えと似ていますが(私はあなたを見ずにこの記事を書きました。誓います!)、ハードコーディングなしでヘッダーシェルを処理します。

#!/usr/bin/perl
use strict;
use warnings;

my %links = (
    yahoo => "www.yahoo.com",
    google => "www.google.com",
);
$links{"the $_"} = $links{$_} for keys %links;

while (<>) {
    s{\\MakeLink\[(.+?)\]\{\}}{
        sprintf "\\MakeLink[%s]{%s}", 
            $1, 
            exists $links{$1} ? $links{$1}
                              : join " ", map {ucfirst lc} split " ", $1;
    }eg;
    print;
}

実行してください:

$ perl link.pl input
A very popular site on the internet was
\MakeLink[yahoo]{www.yahoo.com} but was surpassed by
\MakeLink[google]{www.google.com} due to its  
MakeLink[search engine]{}.

Due to its dominance
\MakeLink[the google]{www.google.com} has had to deal with
\MakeLink[antitrust issues]{Antitrust Issues}.

答え3

スクリプトを確認しませんでしたが、次の2つの場所で引用の問題(表示したくない場合は特別な意味のある文字)が発生することが確認されました。

  • read -d $'\n' LINE(複雑な作成方法read LINE)はバックスラッシュエスケープ文字を解析するので、効果的にバックスラッシュを食べます。完了read -r LINE。このコマンドは、先頭と末尾のスペースも削除します。これを防ぐには、次の手順を実行しますIFS= read -r LINE
  • 変数をsedスクリプトに置き換えます。これらの変数の内容は、ユーザーが意図した検索文字列や代替テキストではなく、sedスクリプトに解析されます。これは?ファイルの問題です。$TARGETsedはに表示されたときにこれを表示します?。この問題を解決するには、sedのすべての特殊文字の前にバックスラッシュ文字を追加します(そして正規表現と代替テキストでは別の文字をエスケープする必要があります!)。

実は…上に書いた通りにしないでください。私は何が間違っているかを説明していますが、ドライバを使って釘を打つので、スクリプトを完全に書き直す必要があります。

連想配列のある bash を使用しています。設定された名前で変数を使用することは、より良い方法を使用できない場合に便利な方法ですが、適切なデータ構造よりも使用するのは難しいです。XX_yahoo変数が実際に環境から取得する必要がない場合は、連想配列を使用してください。

typeset -A targets
targets[yahoo]='www.yahoo.com'

シェルでファイルを1行ずつ解析することは可能ですが、大容量while read …ファイル(遅い)や構文がマイナーでないファイル(わかるように、解析を実行するときにコンテンツを正しく解析するのは難しい)には適していません。次へ) シェルで sed などの外部ツールを前後に切り替えます。あなたの使命は、awkスクリプト(または他の答えと同様にPerl)から主な内容を取得することです。

とにかくawkを使用したい場合は、awkで直接連想配列を定義することもできます。

テストされていないコード。

#!/bin/awk -f
BEGIN {
    targets[yahoo]="www.yahoo.com";
    targets[google]="www.google.com";
}
function MakeTitleCase(text) {
    split(text, words);
    text = "";
    for (w in words) {
        text = text toupper(substr(w,1,1)) substr(w,2)
    }
    return text;
}

/^ *\\MakeLink\[[^][{}]*\]{}/ {
    target_start = index($0, "[") + 1;
    target_end = index($0, "]") - 1;
    target = substr($0, target_start, target_end - target_start);
    if (target in targets) {
        replacement = targets[target];
    } else {
        replacement = MakeTitleCase(target);
    }
    $0 = substr($0, 1, target_start-1) replacement substr($0, target_end);
}

1

関連情報