GNU sed의 이상한 정규식 처리
제목과 관련된 내용이 15% 정도만 있는 이상한 페이지
1. 발단
- 생각의 속도로 위키를 작성하다보니 파일명이 제각각이다.
- 주로 vim으로 문서 작업을 하고
nerdtree
를 쓰는데_wiki
아래 md파일중에서..- Category문서와 일반 콘텐츠 문서를 한 눈에 구분하고 싶었다.
- 카테고리문서는 Camelcase-category.md , 콘텐츠 문서는 소문자로 시작하는 contents-document.md로 하기로 생각
- 주로 vim으로 문서 작업을 하고
-
johngrib님이 알려주신 방법대로 :VimwikiRenameLink 를 이용하여 열심히 파일명을 수정하다보니…
- 갑자기 문서 맨 위쪽에 잘 나오던
상위 문서
항목이 안보이는 것이다. - 이유는 아래와 같다.
-
상위 문서
항목은 각 문서 위쪽 메타데이터의 parent 항목을 참조하는데… - :VimwikiRenameLink는 vimwiki상의 링크만 수정하기 때문에 …
- vimwiki링크 형식이 아닌 parent 항목은 해당명령과는 상관이 없고 …
- 결국 실제 파일명과 parent 항목의 파일명이 틀려져 발생하는 문제였다.
-
- 갑자기 문서 맨 위쪽에 잘 나오던
2. 전개
처음엔 … git for windows 의 bash
갑작스런 급전개
- johngrib님께 문의를 드렸더니, 답변과 함께 질문을 본문에 넣어주셨다.
- Windows 플랫폼을 주로 사용하는 내겐 이제 까마득한 쉘 스크립트의 세계로 다시 발을 들여놓게되었다.
- 답변을 주셨으니 테스트 해봐야 한다.
처음엔 git이라는게 참쉽게 배워질거라 그렇게 믿었었는데 그렇게 믿어…- 처음엔 최근 쉘스크립트를 쓰던 환경이 git for windows 에서 제공하는 간단한 bash 쉘뿐이여서 여기서 시작했다.
- 어디선가 읽은 내용인데 git for windows가 bash 쉘상에서 실행되는 이유는..
- git이 단일 프로그램이 아닌 여러가지 UNIX 상의 툴이 조합되어 만들어진 시스템이라서 …
- windows에서 command line으로 실행하기 위해선 bash 환경을 만들고 그 위에서 지원하는 것이 제일 간단한 방법이라…
- 이미 그런 환경을 구현한 MSYS2를 채용해 구현했다고 본것 같다. (원본 링크는 잊어서 확인은 불가…)
git for windows의 bash 쉘에서 시작
- 어차피 vim에서 작성하고 서버에 올릴때 bash git으로 올리는 지라 여기서 해결가능하다면 금상첨화
- johngrib님 답변중에 있는 ‘ag’는 지원되지 않는다. 리눅스상이라면 설치해서 해볼수 있을텐데
- sudo install이나 apt-get 명령도 없어서 안됨
- find 와 sed는 지원이 되어 해볼수 있었다. 여러가지 이슈가 발생하여 아래에 기록한다.
-
sed 명령중 -i 옵션 뒤에 ‘’ 이 있고 없고의 차이
- 그대로 따라했더니 에러나옴,
sed: can't read s,.....: No such file or directory
- -i 뒤에 문자열이 두개 있는데 첫번째 문자열이 이상해 지워봤더니 정상동작
- 이게 typo인줄 알고 johngrib님께 문의해봤는데 그건 아님
- 알고보니 GNU sed와 BSD sed의 차이
The GNU version of sed allows you to use “-i” without an argument. The FreeBSD/Mac OS X does not. You must provide an extension for the FreeBSD/Mac OS X version. IF you want to do in-palce editing without creating a backup, you can use
1
sed -i '' 's/^/\t/' *.txt
- 결론적으로 내가 쓰는 GNU sed는
-i
만 쓰면 BSD sed의-i ''
와 같은 효과를 가진다. - 참고로.. -i 옵션은
in-place argument
라는 명칭을 가지는데, 여러개의 파일간에 유사한 simple 편집사항을 한방에 편집하는 옵션이다. 때문에 원본손실이 발생할수 있는데 이 -i 옵션에 suffix를 지정해 백업파일을 작성하도록 할수도 있다. (위 차이점 링크에 간단한 예제와 함께 설명이 되어있음)
- 그대로 따라했더니 에러나옴,
-
메타 데이터
parent
작성시 일관성 문제- 이런 건
parent : this-category $
이렇게, 저런건parent : that-category$
저렇게.. ( 잘보면 trailing whitespace 의 차이가 있다. 차이를 부각시키려고 문장끝에 평소에는 안보이는 정규식의 문장끝 표시$
를 추가했다.) - ..위와 같이 막 쓰다 보니 johngrib님이 제시해주신 정규식으로 category 이름을 정확히 포착하지 못하는 경우가 발생
-
trailing whtitespace
로 구글링해서 이 사이트를 찾음 - 사이트가 괜찮아보여 정규식 카테고리 만들고 [[regex-category]] 페이지에 등록
- 해당 페이지
Trimming Whtitespace
절에trailing whitespace
처리 방법대로 해봄1 2
# 현재 \[[ ]] 로 작성했지만 안보이는 상태임.. find . -name '*.md' | xargs sed -E -i 's,(^parent *: *)([^\[]*)[ \t]+$,\1[[\2]],'
- 여전히 문제 발생
- 이제는
category+공백+$
인 녀석들만 바뀌고category+$
인 녀석들은 안바뀜 - 두번에 나눠 작업하면 되긴 하지만 마음에 안듬
- 이런 건
-
sed 명령중 -i 옵션 뒤에 ‘’ 이 있고 없고의 차이
johngrib님의 두번째 답변
- 기본적으로 trailing whitespace 만으로 처리하려는 것이 잘못된 생각임
- 정규식에서 category title을 캡춰하는 두번째 캡춰에서 whitespace를 배제하는 것도 생각해야 했음
1 2 3
# 현재 \[[ ]] 로 작성했지만 안보이는 상태임.. # johngrib님의 2번째 답변 내용 find . -name '*.md' | xargs sed -E -i 's,(^parent *: *)([^ \t\[]*) *$, \1\[[\2]],'
- 따라 해보니 한방에 잘되긴 한다.
- 하지만 이것 저것 변형해 보고 싶은게 생겼고, 한계가 명확한 git for windwos의 bash를 떠나 자유를 꿈꾸며 WSL로 가기로 했다.
WSL (Windows Subsystem for Linux)에 정착하다
WSL을 처음 사용하기
- 예전에 호기심으로 WSL을 설치해놓긴 했는데, 설치한후 한 번 돌려보고 이제 두번째정도 된다
- WSL 사용해보기 전에는 막연하게 windwos와 linux가 가상환경으로 완전 분리되지 않았을까 생각 했는데..
- 윈도우에서 작성한 파일들을 linux의 강력한 도구들로 다룰수 있다 !!!!
- 윈도우와 리눅스는 file system 자체가 틀렸는데 파일 시스템은 윈도우를 쓰면서 linux의 도구를 사용할수 있게 한건가?
- 이러한 의문들은 일단 요기를 참고해보자
- 리눅스로 윈도우에서 작성한 파일들을 손대는것은 되지만 역으로 가능하지는 않은듯하다.
- 일단 현재 사용목적으로는 git for windows의 bash 처럼 linux 도구를 사용하여 윈도우 파일들을 관리하는것으로 충분하다.
sed의 위험성에 대한 대비
- 필자 말로 Google rating이 상당히 좋고, 역사도 오래된 (1994년~) 이 문서에 따르면…
- sed 를 사용하는 대부분의 사용자는
s
명령어만 쓰며s
명령어를 익히는데 필요한 90% 노력은 regex(Regular Expression)을 익히는데 든다고 한다 - 가끔씩 쓸 때마다 다시 배우는 정규식이 주는 위협을 조금이라도 덜기 위해…수정하기전 이전 내용 백업이 필요한데..
- 위에서 봤던 이 링크의 -i 옵션에 딸린 백업 기능을 쓸 필요가 있다.
- 위 링크에서도 조심하기 위해 -i 옵션에 extension을 지정하기를 권장하고 있다.
- sed 를 사용하는 대부분의 사용자는
- 백업 기능을 추가한
parent : category
요소 변경하기- *.md 파일을 수정하고 백업 생성 (각 *.md별로 *.md.origin 생성하여 원본 백업)
1 2
# GUN version sed를 사용한다고 가정함 find . -name '*.md' | xargs sed -E -i.origin 's,(^parent *: *)([^ \t\[]*) *$,\1\[[\2]],'
- *.md 파일과 *.md.origin 파일을 비교후 오류가 있으면 전부 원복 (원복시에 *.md.origin은 제거됨)
1 2
# rename 없으면 for 루프와 mv 명령으로 만들수도 있지만 rename이 훨씬 간단함. rename -v -f -e 's/.md.origin/.md/' *.md.origin
- *.md 파일이 모두 정상적으로 변경되었으면 *.md.origin 제거
1
rm *.md.origin
- *.md 파일을 수정하고 백업 생성 (각 *.md별로 *.md.origin 생성하여 원본 백업)
위 방법을 구글링 하다가 얻어걸린 내용들 정리
- for 루프와 mv 명령 == rename
- 누가 원조인지는 잘 모르겠지만 다음과 같이 비슷한 형식으로 linux rename 명령을 소개하는 글이 많다.
- 우선 mv로 파일명을 변경 하는 방법 소개
1
mv oldfile.txt newfile.txt
- for와 mv를 써서 여러개의 파일명을 한번에 변경하는 방법 소개
1 2
# 대충 이런식? f와 *.prog는 다를수 있는데 형식은 모두 동일 for f in *.prog; do mv -- "$f" "${f%.prog}.prg"
- 위의 2번으로 했던 작업을 rename을 써서 간단하게 해결
1 2
# rename 사용법을 간단하게 소개하며 마무으리 rename 's/.prog/.prg/' *.prog
- 우선 mv로 파일명을 변경 하는 방법 소개
- 관련 자료들 링크 (핵심 내용은 서로 90% 정도 동일)
- rename 사용법은 대충 알겠는데 위에서 tricky한 방법으로 소개된 2번째 방식중…
- mv 다음에 오는
--
의 의미를 도저히 알수가 없었다. - 알아 내기 위해 mv manual이나 for manual을 뒤졌는데 위와 같은 옵션은 적혀있질 않았다.
- shell 확장 을 보다가…이곳에 가봐도 없고..
-
요기서는 “${f%.prog}.prg”의 의미는 알아낼수 있었다.
-
${f%.prog}
의 의미는 f로 지정된 파일명의 뒷부분에서 가장 짧게 일치하는.prog
를 삭제하고 나머지를 반환.. - 만약에 f가
something.prog
였다면something
만 남겠네. 바로 뒤에.prg
를 붙여서.. -
something.prog
–>something.prg
가 되는 기법..
-
- 해석방법, 핵심요약 모두 좋은 내용이지만 찾는 내용은 없고…
- 전반적으로 소개하는 내용 같아서 많이 뒤져봤는데 역시 못찾고..
-
아래는 알아내는데 시간이 좀 많이 걸린
--
내용을 설명한다.-
BASH Shell Script중 2.Basics 뒷부분 Misc 에서 드디어 발견!!!!
-
--
를 사용하여이 뒤로부터는 옵션이 아니다
라는 선언임 - 위 옵션이 필요한 경우는 grep으로 ‘-n’ 표현을 검색하고자 할 때 발생한다.
- grep -r ‘-n’ 이라고 치면 ‘‘으로 둘러 쌌지만 -n을 옵션으로 인식하여 에러가 발생한다.
1 2 3
$ grep -r '-n' Usage: grep [OPTION]... PATTERN [FILE]... Try 'grep --help' for more information.
- grep -r – ‘-n’ 으로 다시치면 ‘-n’을 검색 대상으로 인식한다.
1 2
$ grep -r -- '-n' ...검색결과를 보여줌...
- 그러므로,
mv -- "$f" "${f%.prog}.prg"
는 단순히mv oldfile newfile
인데 파일명에서 옵션으로 오인할 여지를 없앤 내용임.
- grep -r ‘-n’ 이라고 치면 ‘‘으로 둘러 쌌지만 -n을 옵션으로 인식하여 에러가 발생한다.
-
- 결국, StackExchange에서도 정확히 이 내용을 문의하는 질문 발견 !!
- 질문의 제목은
What does "--" (double-dash) mean?
- 영어 명칭은
end of command option
인듯.
- 질문의 제목은
-
BASH Shell Script중 2.Basics 뒷부분 Misc 에서 드디어 발견!!!!
- mv 다음에 오는
- 누가 원조인지는 잘 모르겠지만 다음과 같이 비슷한 형식으로 linux rename 명령을 소개하는 글이 많다.
드디어 제목과 관련된 내용으로 …
본래 문제는 풀렸지만 … 풀리지 않은 의문 하나.
- regex101.com 사이트를 보다보니
\s
가 whitespace를 의미한다고 해서 [ \t]를 [\s]로 바꾸어 보았다. - 그러니까 find + sed 명령을 다음과 같이 바꿔보았다.
1 2 3 4
# 원래는 이랬던 명령어 조합을 ... find . -name '*.md' | xargs sed -E -i.origin 's,(^parent *: *)([^ \t\[]*) *$,\1[[\2]],' # 이렇게 변경해 봤다 ... find . -name '*.md' | xargs sed -E -i.origin 's,(^parent *: *)([^\s\[]*)\s*$,\1[[\2]],'
- 그랬더니
parent : \[[category ]]
문제가 다시 발생했다. 아니 더 나빠졌다. - 어떤문서는 제대로 되고 어떤문서는 인식을 못한다 .. 되고, 안되는 규칙을 발견할 수 없었다.
- 이번에는 johngrib님 에게서도 마땅한 답변을 얻지 못했다.
- 믿을건 .. 언제나 답을 줬던 stack overflow 뿐.. 뒤져봤다
3. 결론
갑작스런 결론이지만 johngrib님 댓글창에 적은 답변을 이 페이지의 결론으로 하는게 좋을것 같았다.
아래는 댓글 내용이다.
위 내용으로 stack overflow를 뒤지다가 해당 의문사항이 거의 풀려서 공유드립니다.
-
의문사항 : GNU sed 정규식 에서 \s를 인식할때도 있고 인식하지 못할때도 있다. 규칙이 뭔가?
1 2 3 4 5 6
# 아래에서 \s가 세군데 사용되었는데 1,2번째는 인식하고 3번째는 인식못함 $ echo 'a : b ' | sed -r 's,(a\s*:\s*)([^\s]*),\1[\2],' a : [b ] # 아래에서 3번째 \s를 ' '로 바꿀때 제대로 인식함 $ echo 'a : b ' | sed -r 's,(a\s*:\s*)([^ ]*),\1[\2],' a : [b]
- 답변 :
- GNU sed는 POSIX 사양 호환으로 POSIX BRE(Basic Reqular Expression)을 지원 참고한 링크
-
POSIX BRE중 Braket Expression (9.3.5)절에 [list] 일경우 list 내용에 대한 규칙이 나오는데…
- list 중에 있는 ‘.’, ‘*’,’[’,’' 문자는 특수 의미를 상실하고 literal의 의미만 가지게된다고 합니다.
- 이 규칙 때문에 bracket 바깥에 있는 \s는 인식하지만 bracke안의 \s는 인식못함 ‘\’ ‘s’로 각각인식됩니다.
- 따라서 bracket 내에서 특수 의미 문자 클래스를 사용하려면 별도의 규칙을 사용합니다.
- \s 대신 [:space:]를 사용해야하며 braket 내에서 사용할경우 [[:space:]] 혹은 [^[:space:]] 처럼 사용하게 됩니다.
1 2 3
# 규칙대로 사용한 의문사항 해결책 $ echo 'a : b ' | sed -r 's,(a\s*:\s*)([^[:space:]]*),\1[\2],' a : [b]
-
2번째 의문사항 : 위의 규칙이 적용되었다면 [^\t]도 인식하지 못해야 하는데 왜 인식하나?
참고로 command line 상에서 \t문자를 입력하기 위해서는
Ctrl + 대문자 v
를 누르고tab
을 누르면 됩니다. 참고링크
1
2
3
# [] 안의 \t 은 잘 인식함
$ echo 'a : b[탭문자]' | sed -E 's,(a\s*:\s*)([^\t]*)(\s*),\1[\2]_(\3),'
a : [b]_( )
- 답변
-
GNU sed manaul의 5.5절 Character Classes and Bracket Expressions의 맨 마지막 부분에 이런 내용이 있습니다.
The characters $, , ., [, and \ are normally not special within list. For example, [*] matches either ‘\’ or ‘’, because the \ is not special here. However, strings like [.ch.], [=a=], and [:space:] are special within list and represent collating symbols, equivalence classes, and character classes, respectively, and [ is therefore special within list when it is followed by ., =, or :. Also, when not in POSIXLY_CORRECT mode, special escapes like \n and \t are recognized within list. See Escapes.
- 편의성을 위해 POSIX 표준을 위반한 부분이 있는데 별도 옵션으로 엄격모드로 하지 않는한 \t는 bracket 내에서도 인식이 된다는 설명입니다. 이상입니다.
-
GNU sed manaul의 5.5절 Character Classes and Bracket Expressions의 맨 마지막 부분에 이런 내용이 있습니다.
뭐가 이상한 sed 정규식이냐?
- 문서를 보면 알겠지만, 그냥 결론 내용을 찾다가 알게된 많은 내용을 적고 싶었는데…
- 마땅한 제목을 찾을수 없었다.
- 그래서 그냥 최종적으로 정리한 내용을 제목으로 하기로 하고..
- 어그로성 낚시성 제목을 달았다.
- 어차피 나 혼자 보는 컨셉이므로 ..
- 나만 잘 기억나면 된다.