引用符付きコメントフラグを無視しながら、ファイルからコメントを削除します。

引用符付きコメントフラグを無視しながら、ファイルからコメントを削除します。

で始まるコメントを削除したいと思います#。私は説明したより簡単なアプローチを試しました。ファイルからすべてのコメントを削除する方法は?しかし、いくつかの追加ルールがあります。

  • 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他の言語の規則はまったく異なります。perlpythonruby"..."'...'#

もし

  • これはシェル構文とは関係ありません。
  • 引用符がエスケープされていないと仮定できます。
  • 引用符付き文字列には改行文字は含まれていません。
  • すべての引用符が一致します。
  • #スペースやその他の区切り文字が続くだけでなく、引用符以外のすべてがコメントを開始します。
  • 入力は現在のロケールの有効なテキストです。

合格すると基準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"

Python

#!/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

'

関連情報