![파일의 첨부 경로를 전체 경로로 바꾸는 Bash 스크립트](https://linux55.com/image/199146/%ED%8C%8C%EC%9D%BC%EC%9D%98%20%EC%B2%A8%EB%B6%80%20%EA%B2%BD%EB%A1%9C%EB%A5%BC%20%EC%A0%84%EC%B2%B4%20%EA%B2%BD%EB%A1%9C%EB%A1%9C%20%EB%B0%94%EA%BE%B8%EB%8A%94%20Bash%20%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8.png)
~/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 스크립트의 세 가지 버전입니다. 세 가지 모두 첫 번째 매개변수는 검색할 디렉터리(예: ./app
또는 ./
)여야 합니다. 나머지 매개변수는 수정할 Markdown 파일의 이름입니다(예: ./app/file1.md
또는 ./app/*.md
).
둘 다 검색 파일에만 기록 되지만 .png
사용되는 정규식과 전역 변수를 변경하여 쉽게 변경할 수 있습니다.
#!
참고: 세 스크립트 모두 표준 출력으로 인쇄하는 대신 스크립트가 Markdown 파일을 수정하도록 하려면 첫 번째 줄을 제거하십시오. 첫 번째#! 요구 사항을 충족하는지 확인하기 위한 테스트에 사용됩니다. 두 번째는 실제로 마크다운 파일을 수정합니다(그리고 원본 파일을 .bak에 복사합니다. -i.bak
백업 복사본을 만들지 않으려면 변경하면 됩니다). 이 옵션의 작동 방식에 대한 세부정보를 -i
보고 man perlrun
검색하세요 .-i
또한 참고하시기 바랍니다파일::기본 이름그리고파일::찾기사용되는 모듈은 Perl에 포함된 Perl 코어 라이브러리 모듈입니다. File::Basename
기본적으로 명령의 작업을 수행 basename
하고 File::Find
명령과 같이 반복적으로 디렉터리를 검색합니다 find
.
왜 sh나 bash 대신 Perl을 사용하나요? 쉘은 텍스트나 데이터 처리에 있어서 끔찍한 언어이기 때문입니다. 바라보다쉘 루프를 사용하여 텍스트를 처리하는 것이 왜 나쁜 습관으로 간주됩니까?몇 가지 이유. Shell의 임무는 조정하는 것입니다.기타 프로그램데이터 처리 자체가 아닌 데이터 처리 작업을 수행합니다. 데이터 처리를 위해 셸을 사용하는 것은 드라이버가 필요할 때 삽을 사용하고, 국자가 필요할 때 포크를 사용하는 것과 같습니다.
세 가지 버전 모두 다음 파일 및 디렉터리 구조를 사용하여 테스트되었습니다.
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]]
두번째 버전
두 번째 버전은 파일이 ./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의 경로를 찾아 수정합니다.
세 번째 버전
세 번째 버전은 첨부 파일이 ./app/
디렉토리 트리의 깊이에 관계없이 의 하위 디렉토리에서 찾을 수 있는 경우 유용합니다. 이 버전과 두 번째 버전의 유일한 차이점은 배열을 채우는 방식입니다 @png
. 두 번째 버전에서는 이 glob()
기능을 사용하고 세 번째 버전에서는 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.png
있지만attachments/more/
other/
실수
$file
첨부 파일 이름에 초과 공백이 있는지 여부와 Markdown 인터프리터가 초과 공백을 얼마나 엄격하게 처리하는지에 따라 선행 및 후행 공백을 제거하는 것이 좋습니다 . 뒤에 다음 줄을 추가합니다$file = $1;
.$file =~ s/^\s*|\s*$//g;
마크다운 파일에 표시된 위치에서 .png 파일을 찾을 수 없는 경우 두 번째 및 세 번째 버전이 반환됩니다.첫 번째동일한 이름을 가진 파일이 여러 개 있더라도 파일을 일치시킵니다(예, 이는 실제 버그라기보다는 디자인 결정에 가깝습니다. 저는 이 방식으로 작성하기로 결정했습니다). 때로는 예상한 파일이 아닐 수도 있습니다. 이는 다음의 자연스러운 결과입니다.지고 규칙.
이것은 일치하는 수를 세어 "수정"할 수 있으며(힌트: Perl의 내장
grep
함수는 배열을 반환합니다. 위의 스크립트는 첫 번째 결과를 제외한 모든 결과를 삭제합니다. 해당$found
변수는 배열 변수로 대체될 수 있습니다@found
) 오류가 있으면 인쇄합니다. 더 많거나 특정 디렉터리의 첨부 파일을 다른 디렉터리보다 선호하는(또는 이전 파일보다 최신 파일을 선호하거나 새 파일보다 오래된 파일을 선호하는 등) 경험적 방법이 있습니다. 실제 해결책은 모호함을 피하기 위해 입력 Markdown 파일을 편집하는 것입니다.perldoc -f grep
Perl 함수에 대한 자세한 내용은 참고자료를 참조하세요grep
.
답변2
한 줄에 하나의 파일 링크만 있고 한 줄씩 수행한다고 가정하면 sed
괜찮을 것입니다. 그래서 우리는 다음과 같은 것을 가지고 있습니다:
file=$(sed -n 's/!\[\[\(.*\.png\)]]/\1/gp')
name=$(basename "$file")
fullFile=$(find . -name "$name" -print -quit)
-quit
여기서는 첫 번째 일치 항목이 발견되자마자 검색이 중지되도록 사용합니다 . 이것이 당신이 올바른 방향으로 갈 수 있기를 바랍니다.