인용된 주석 플래그를 무시하면서 파일에서 주석을 제거합니다.

인용된 주석 플래그를 무시하면서 파일에서 주석을 제거합니다.

로 시작하는 댓글을 삭제 하고 싶습니다 #. 나는 설명된 더 간단한 접근 방식을 시도했습니다.파일에서 모든 주석을 제거하는 방법은 무엇입니까?하지만 몇 가지 추가 규칙이 있습니다.

  • A가 #인용된 문자열의 일부로 나타나면 주석이 시작되지 않습니다.
  • 문자열은 작은따옴표나 큰따옴표로 '묶을 수 있습니다 ".
  • 큰따옴표로 묶인 문자열은 앞에 백슬래시가 있으면 따옴표를 포함할 수 있으며 \", 백슬래시는 이와 같이 인용됩니다 \\.
  • 입력의 모든 따옴표가 일치합니다. 그러나 이는 문자열 내용의 일부인 따옴표(즉 "'", 유효한 "\""문자열 )에는 '"'필요하지 않습니다 .
  • 인용된 문자열에는 개행 문자가 포함될 수 없습니다.
  • #설명에는 , 및 를 포함한 모든 문자가 포함될 수 있습니다 '."\
  • 모든 #콘텐츠는 댓글로 시작됩니다(예:스티븐 차제라스대부분의 쉘 코드는 더 복잡한 규칙을 따른다는 점을 지적하세요. $#주석으로 시작하지 않는 Bash 코드를 생각해 보세요.

예를 들어 다음 입력

# comment only
# comments are allowed to contain quotes "' and # number signs
# comments are allowed to contain pairs 'of' "quotes"
some text # with an explanation
some "quoted text # not a comment" # comment
'# not a comment' and '# not a comment either' # comment
"# not a comment containing 'quotes\"" # another comment

다음 출력으로 변환되어야 합니다.




some text
some "quoted text # not a comment"
'# not a comment' and '# not a comment either'
"# not a comment containing 'quotes\""

나는 널리 사용되는 Unix 명령줄 도구(예: , )를 사용하여 최신 Debian/Ubuntu 시스템에서 awk이 작업을 수행하고 싶습니다. POSIX 호환 솔루션이 선호되지만 POSIX에서 설명하는 기능에만 엄격하게 제한되지는 않습니다.grepsed

답변1

POSIX sh 스크립트에서 주석을 제거하는 것이 요점이라면 다음 코드에서 YES로 표시된 주석만 주석이라는 점에 유의하세요.

echo 1 # YES
echo 2 $# NO foo# NO
echo 3;#YES
# YES
cat << E
# NO
E
echo 4 " # NO \" # NO" \" # YES
echo "5
# NO
$(echo 6 # YES
)
`echo 7 \" # NO \"`
"
eval 'echo 8 # NO, then YES'

(대부분의 경우 stackexchange 구문 강조 표시에서 오류가 발생하는 것을 볼 수 있습니다).

이를 다루려면 수백 줄 awk이나 sed코드가 필요합니다.

csh, fish, perl, python와 같이 주석 리더로 따옴표 와 따옴표가 있는 ruby다른 언어에 대한 규칙은 완전히 다릅니다."..."'...'#

만약에

  • 이는 쉘 구문과 관련이 없습니다.
  • 따옴표가 이스케이프되지 않았다고 가정할 수 있습니다.
  • 인용된 문자열에는 개행 문자가 포함되어 있지 않습니다.
  • 모든 따옴표가 일치합니다.
  • #공백이나 다른 구분 기호가 뒤에 오는 것뿐만 아니라 따옴표 밖의 모든 것은 주석을 시작합니다.
  • 입력이 현재 로케일의 유효한 텍스트입니다.

통과되면기준POSIX 2018 이하를 의미한다면 다음과 같이 할 수 있습니다 sed.

sed "s/^\(\(\([^\"'#]\)*\(\"[^\"]*\"\)\{0,1\}\('[^']*'\)\{0,1\}\)*\)#.*/\1/"

POSIX 2018은 교대 연산자에 필요한 ERE를 sed지원하지 않지만 여기서는 ( ERE에서) 동등한 사용 을 전달합니다 .-E\(a\{0,1\}b\{0,1\}\)*(a?b?)*(a|b)*(a*b*)*라케쉬의 답변작동할 것입니다.

grepgrep표준은 완전히 일치하는 행만 인쇄하므로 옵션이 아닙니다 . awk하지만 ERE를 사용하세요. 표준 awk에는 캡처 그룹이 없지만 다음을 수행할 수 있어야 합니다.

awk "match(\$0, /^([^'\"#]|\"[^\"]*\"|'[^']*')*#/) {
       \$0 = substr(\$0, 1, RLENGTH-1)
     }
     {print}"

"(\\.|[^\\"])*"편집자의 요구 사항에 따라 또는 이에 상응하는 BRE를 사용하여 이스케이프 따옴표를 처리할 수 있습니다.

sed 's/^\(\(\([^"\\'\''#]\)*\(\\.\)\{0,1\}\("\([^"\\]*\(\\.\)\{0,1\}\)*"\)\{0,1\}\('"'[^']*'\)\{0,1\}\)*\)#.*/\1/"

또는:

awk 'match($0, /^([^'\''"\\#]|\\.|"(\\.|[^\\"])*"|'\''(\\.|[^\\'\''])*'\'')*#/) {
       $0 = substr($0, 1, RLENGTH-1)
     }
     {print}'

둘 다 이스케이프 따옴표도 처리합니다.외부따옴표(예 foo\"bar # comment: ).

리터럴을 얻기 위해 삽입해야 하는 백슬래시 수를 줄이기 위해 여기에서는 작은따옴표를 대신 사용하고 있지만 \\데이터의 리터럴 작은따옴표는 다음과 같이 삽입되어야 합니다 'before'\''after'. 즉, 백슬래시를 사용하여 인용된 문자열을 닫는 '\''첫 번째 문자 입니다. /translate 리터럴 (작은따옴표로 묶인 문자열 안에 작은따옴표를 삽입할 수 없기 때문에), 그 다음에는 따옴표 붙은 문자열을 입력합니다.''before'\'''after'

답변2

지정된 규칙에 따라 5가지 유형의 단어를 구별합니다.

  • 큰따옴표로 묶인 단어(이스케이프된 큰따옴표도 포함될 수 있음) "... \"... "

  • 작은따옴표로 묶인 단어에는 '...'작은따옴표가 포함되지 않습니다.

  • 백슬래시로 인용된 단어는 \.기본적으로 모든 이스케이프 문자입니다.

  • 비주석 시작 문자[^'#"]

  • 이제 남은 건 댓글을 다는 것뿐이다.

#! /bin/bash
# whitespace and horizontal whitespace
_ws_=$(printf '\t \nx') 
ws="[${_ws_%?}]" hws="[${_ws_%??}]"

_nac_="[^\"'#]" nac="\($_nac_\)" #not a comment char

_bqw_='[\].'    bqw="\($_bqw_\)" # backslashed word 

_sqw_="'[^']*'" sqw="\($_sqw_\)" # single quoted word 

#double quoted word 
_dqw_='
  "
    \(
      [^\"]* \([\][\]\)* [\]"
    \)*
    [^"]*
  "
'
dqw="\(${_dqw_//$ws/}\)"

sed \
  -e '/#/!b' \
  -e "s/^\(\($sqw*$dqw*$bqw*$nac*\)*\).*/\1/" \
  -e "s/$hws*$//" \
< file

이것은 엄밀히 말하면 POS IX입니다.

답변3

해결책

다음 솔루션은 다음을 지원하는 sedGNU와 같은 널리 사용되는 구현 에 적합합니다.sed확장하다정규식(ERE):

sed -E "s/^(([^#\"'\\]|'[^']*'|\"([^\"\\\\]|\\\\.)*\")*)#.*/\1/" input.txt

이 솔루션의 가장 큰 장점은 다른 많은 솔루션보다 가독성이 높다는 것입니다.

노트:-E스위치는 아직 POSIX 2018의 일부는 아니지만POSIX 2020의 일부가 되고 있습니다.. POSIX-2018 호환 솔루션이 필요한 경우 다음을 참조하세요.Stefan Chazeras의 답변.

어떻게 작동하나요?

다음의 더 긴 버전은 위의 정규식을 더 이해하기 쉬운 부분으로 나눕니다.

NON_QUOTED_TEXT="[^#\"'\\]"
SINGLE_QUOTED_STRING="'[^']*'"
DOUBLE_QUOTED_STRING='"([^"\\]|\\.)*"'
REMOVE_COMMENTS="^((${NON_QUOTED_TEXT}|${SINGLE_QUOTED_STRING}|${DOUBLE_QUOTED_STRING})*)#.*"
sed -E "s/${REMOVE_COMMENTS}/\1/" input.txt

sed에 포함된 정규식과 일치하는 텍스트를 검색 ${REMOVE_COMMENTS}하고 각 일치 항목을 첫 번째 캡처 그룹의 내용으로 바꾸는 데 사용합니다 \1. 이 캡처링 그룹에는 첫 번째 여는 괄호 와 마지막 닫는 괄호 (사이의 정규식 일치가 포함되어 있습니다 ). 정규식의 이 부분은 #인용된 문자열의 일부로 나타나지 않는 첫 번째 주석 기호( ) 앞의 모든 텍스트와 일치합니다. 자세히 설명하면 *다음 옵션의 0에서 N() 순서를 일치시킵니다 (a|b|c).

  • 인용되지 않은 텍스트: #, 및 를 "제외한 문자 .'\
  • 작은따옴표 리터럴: 작은따옴표 쌍으로 묶인 *( )를 제외한 모든 문자 ( ) .^'
  • 큰따옴표 텍스트: 큰따옴표 쌍으로 묶인 문자열입니다. 문자열에는 백슬래시( ) 앞에 오는 "\또는( )를 제외하고 모든 문자를 포함할 수 있습니다 .(a|b)\\.

위의 전체 솔루션에 이러한 부분을 결합할 때 Bash 규칙에서는 작은따옴표와 큰따옴표를 사용할 때 약간 다른 따옴표가 필요하다는 점을 기억해야 합니다. 바라보다Bash에서 작은 따옴표와 큰 따옴표의 차이점더 알아보기.

답변4

주문하다

 sed -e '/^#/d' filename| sed "s/# comment$//g"

파이썬

#!/usr/bin/python
import re
d=re.compile(r'^#')
r=re.compile(r'#\scomment$')
l=open('p','r')
for  i in l:
    if not re.search(d,i):
        e=re.sub(r,"",i)
        print e.strip()

산출

some text # with a comment
some "quoted text # not a comment"
'# not a comment' "# it's not a comment" '#still not a comment

'

관련 정보