ファイルの添付パスをフルパスに置き換えるBashスクリプト

ファイルの添付パスをフルパスに置き換えるBashスクリプト

~/app/Markdownリーダー間の相互運用性のために、相対パス全体が挿入されるようにリファクタリングしたいMarkdownファイルにファイル名と相対パスが混在しています。

~/app/私のファイルには添付ファイルを参照するファイルがあります。~/app/file1.md~/app/attachments/

例:file1.md以下を含みます。

Here is an image:
![[img1.png]]

Here is another image:
![[attachments/img2.png]]

(両方の写真に位置があります~/app/attachments/

![[]]との間で参照を取得できます。

sed -n 's/!\[\[\(.*\.png\)]]/\1/gp'

findしかし、これをインラインを完全な相対パスに置き換えるコマンドと組み合わせるにはどうすればよいですか? - 以下ではなく画像があるところどこでも動作したいです。attachments/

結果file1.mdは次のとおりです。

Here is an image:
![[attachments/img1.png]]

Here is another image:
![[attachments/img2.png]]

答え1

これはこれを行うPerlスクリプトの3つのバージョンです。 3 つすべての最初のパラメーターは、検索するディレクトリー (./appまたは./) でなければなりません。残りのパラメータは、変更するMarkdownファイルの名前です(たとえば、./app/file1.mdまたは./app/*.md)。

どちらも検索ファイルにのみ記録されますが、.png使用される正規表現とグローバル変数を変更することで簡単に変更できます。

#!注:3つのスクリプトすべてが標準出力として印刷されるのではなく、スクリプトがMarkdownファイルを変更できるようにするには、最初の行を削除します。最初の#!要件を満たしていることを確認するためのテストに使用されます。 2番目は実際にマークダウンファイルを変更します(元のファイルを.bakにコピーします。-i.bakバックアップコピーを作成したくない場合は変更できます)。このオプションの仕組みに関する詳細を表示し-iman perlrun検索してください。-i

また参考にしてくださいファイル::デフォルト名そしてファイル::検索使用されるモジュールは、Perlに含まれるPerlコアライブラリモジュールです。 File::Basenameデフォルトでは、コマンドの操作を実行し、basenameコマンドFile::Findなどの繰り返しディレクトリを検索しますfind

なぜshやbashの代わりにPerlを使うのですか?シェルはテキストやデータ処理においてひどい言語だからです。バラよりシェルループを使用してテキストを処理するのはなぜ悪い習慣と見なされますか?いくつかの理由。 Shellの使命は調整することです。その他のプログラムデータ処理自体ではなくデータ処理操作を実行します。データ処理にシェルを使用することは、ドライバーが必要な場合はシャベルを使用し、スクープが必要な場合はフォークを使用するのと同じです。

3つのバージョンはすべて、次のファイルとディレクトリ構造を使用してテストされています。

app/attachments/img1.png
app/attachments/img2.png
app/attachments/more/img5.png
app/file1.md
app/file2.md
app/file3.md
app/img4.png
app/other/img3.png
最初のバージョン

最初のバージョンは、添付ファイルが./appまたは./app/attachmentsでのみ見つかる場合に便利です。

添付ファイルが表示として指定された場所に見つかった場合は、![[filename]]そのまま残ります。見つからない場合、スクリプトは最初に最上位ディレクトリを探し、次にAttachments /サブディレクトリを探します。

$ cat fix-paths1.pl 
#!/usr/bin/perl -p
#!/usr/bin/perl -p -i.bak

BEGIN { $dir = shift };

use File::Basename;

if (/!\[\[([^]]*\.png)\]\]/i) {
  $file = $1;
  next if -f $file1;
  $bn = fileparse($file);

  if (-f "$dir/$bn") {
    s/$file/$bn/
  } elsif (-f "$dir/attachments/$bn") {
    s/$file/attachments\/$bn/
  } else {
    print STDERR "WARNING: Attachment '$file' does not exist. $ARGV:$.\n"
  };
}

実行例 - file1.md あなたの質問と同じです。

$ ./fix-paths1.pl ./app/ app/file1.md 
Here is an image:
![[attachments/img1.png]]

Here is another image:
![[attachments/img2.png]]
セカンドバージョン

2番目のバージョンは、ファイルが./app/の直接サブディレクトリにある場合に便利です。つまり、app/attachments/はありますが、app/attachments/more/は見つかりません。

これはPerlの機能を使用して、指定されたディレクトリ()とすべての直系サブディレクトリにglobファイル配列を作成します。この配列は、一致するすべてのファイルのキャッシュとして機能します。ディレクトリ検索はかなり「コストがかかる」作業だからです。確かにループで繰り返し実行したくない作業です。.png./app/

$ cat fix-paths2.pl
#!/usr/bin/perl -p
#!/usr/bin/perl -p -i.bak

use File::Basename;

BEGIN {
  $dir = shift;
  $dir =~ s:/+$::;

  @png = glob("$dir/*.png");
  push @png, glob("$dir/*/*.png");
  @png = map { s:^$dir/:: ? $_ : $_ } @png;
};

if (/!\[\[([^]]*\.png)\]\]/i) {
  $file = $1;
  next if -f $file1;

  $bn = fileparse($file);

  ($found) = grep { m:(^|/)$bn$: } @png;

  if ($found) {
    s/$file/$found/;
  } else {
    print STDERR "WARNING: Attachment '$file' does not exist. $ARGV:$.\n"
  };
}

実行例:

$ cat app/file2.md 
Here is an image:
![[img1.png]]

Here is another image:
![[attachments/img2.png]]

and another:
![[img3.png]]



$ ./fix-paths2.pl ./app/ ./app/file2.md 
Here is an image:
![[attachments/img1.png]]

Here is another image:
![[attachments/img2.png]]

and another:
![[other/img3.png]]

このリリースでは、img1.pngとimg3.pngのパスを見つけて修正します。

3番目のバージョン

3番目のバージョンは、添付ファイルが./app/ディレクトリツリーの深さに関係なくのサブディレクトリにある場合に便利です。このバージョンと2番目のバージョンの唯一の違いは、配列を埋める方法です@png。 2番目のバージョンではこのglob()機能を使用し、3番目のバージョンではFile::Find

検索結果をキャッシュするために使用される@png配列は、ここで実際にその値を示しています。再帰ディレクトリ検索は、「簡単な」グローバル検索よりもコストがかかる作業です。

$ cat fix-paths3.pl
#!/usr/bin/perl -p
#!/usr/bin/perl -p -i.bak

use File::Basename;
use File::Find;

BEGIN {
  $dir = shift;
  $dir =~ s:/+$::;

  sub wanted {
    if (m/\.png$/) {
      ($f = $File::Find::name) =~ s:^$dir/::;
      push @png, "$f";
    };
  };

  find(\&wanted, $dir);
};

if (/!\[\[([^]]*\.png)\]\]/i) {
  $file = $1;
  next if -f $file1;

  $bn = fileparse($file);

  ($found) = grep { m:(^|/)$bn$: } @png;

  if ($found) {
    s/$file/$found/;
  } else {
    print STDERR "WARNING: Attachment '$file' does not exist. $ARGV:$.\n"
  };
}

実行例:

$ cat app/file3.md 
Here is an image:
![[img1.png]]

Here is another image:
![[attachments/img2.png]]

and another:
![[img3.png]]

and another:
![[attachments/img4.png]]

and another:
![[other/img5.png]]



$ ./fix-paths3.pl ./app/ ./app/file3.md 
Here is an image:
![[attachments/img1.png]]

Here is another image:
![[attachments/img2.png]]

and another:
![[other/img3.png]]

and another:
![[img4.png]]

and another:

![[attachments/more/img5.png]]

このバージョンでは、file3.mdファイルが次の場所にあると記載されていますがimg5.pngattachments/more/other/

間違い

  1. $file添付ファイル名に余分な空白があるかどうか、およびMarkdownインタプリタが余分な空白をどのくらい厳密に処理するかに応じて、先行および末尾の空白を削除することをお勧めします。後ろに次の行を追加します$file = $1;

    $file =~ s/^\s*|\s*$//g;
    
  2. マークダウンファイルに表示されている場所に.pngファイルが見つからない場合は、2番目と3番目のバージョンが返されます。最初同じ名前のファイルが複数ある場合でも、ファイルが一致します(たとえば、実際のバグではなくデザインの決定に近いです。私はこのように書くことにしました)。時々、あなたが期待したファイルではないかもしれません。これは次の自然な結果です。負担ルール

    これは一致する数を数えることによって「修正」することができます(ヒント:Perlの組み込みgrep関数は配列を返します。上記のスクリプトは最初の結果を除くすべての結果を削除します。変数は配列変数$foundで置き換えることができます@found)エラーがある場合は印刷します。より多くの、または特定のディレクトリの添付ファイルを他のディレクトリより優先する(または古いファイルよりも最新のファイルを好む、または新しいファイルより古いファイルを好むなど)、経験的な方法があります。実際の解決策は、あいまいさを避けるために入力Markdownファイルを編集することです。

    perldoc -f grepPerl関数の詳細については、参考資料を参照してくださいgrep

答え2

1行に1つのファイルリンクだけがあり、1行ずつ実行すると仮定すると問題sedありません。だから私たちは次のようなものを持っています:

file=$(sed -n 's/!\[\[\(.*\.png\)]]/\1/gp')
name=$(basename "$file")
fullFile=$(find . -name "$name" -print -quit)

-quitここでは、最初の一致が見つかるとすぐに検索を停止するために使用します。これが正しい方向に行くことを願っています。

関連情報