Awk

從文件中刪除評論,同時忽略引用的評論標誌

  • September 8, 2020

我想#從文件中刪除以開頭的評論。我嘗試瞭如何從文件中刪除所有註釋中描述的更簡單的方法?但我還有一些額外的規則:

  • 如果註釋作為引用字元串的一部分出現,則A#不會開始註釋。
  • 字元串可以用單引號'或雙引號引起來"
  • 如果前面有反斜杠,雙引號字元串可以包含引號\",反斜杠引用為\\.
  • 輸入中的所有引號都匹配。但是,對於作為字元串內容一部分的引號(換句話說"'""\""並且'"'是有效字元串),這不是必需的。
  • 帶引號的字元串不能包含換行符。
  • 註釋可以包含任何字元,包括任意數量的#、和。'``"``\
  • 引號之外的任何內容都會#開始註釋(正如Stéphane Chazelas指出的大多數 shell 的程式碼程式碼遵循更複雜的規則 - 想想$#不開始註釋的 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\""

我想在現代 Debian/Ubuntu 系統上awk使用流行grep的Unix 命令行工具來完成此任務。sed我並不嚴格限制於 POSIX 描述的功能,但最好使用符合 POSIX 的解決方案。

如果重點是從 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是其他語言有"..."'...'引用並#作為評論領導者將完全不同。

如果

  • 這與 shell 語法無關,
  • 您可以假設沒有轉義引號,
  • 引用的字元串不包含換行符,
  • 所有引號都匹配,
  • 引號之外的任何內容都會#開始註釋,而不僅僅是空格或其他分隔符之後的註釋,
  • 輸入是目前語言環境中的有效文本

如果按照標準您是指 POSIX 2018 或更早版本,您可以sed使用:

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

POSIX 2018sed不支持-E交替運算符所需的 ERE,但在這裡我們通過\(a\{0,1\}b\{0,1\}\)*(a?b?)*在 ERE 中)作為(a|b)*. 在Rakesh 的回答中使用(a*b*)*as也可以。

grep不會是一個選項,因為標準grep只列印完整的匹配行。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',即'\''首先'關閉帶'before'引號的字元串,\'使用反斜杠引用/轉義文字'(因為您不能在單引號字元串中插入單引號),然後'after'引用字元串。

引用自:https://unix.stackexchange.com/questions/608099