如何提取命令上方最近的无前置空白#起始行(支持空行间隔)
问题描述
需要在shell脚本中执行命令时,打印该命令上方最后一行行首无空格/制表符且以#开头的注释行,即使命令与该行之间存在空行。
示例脚本:
#!/bin/ksh # foobar1 # foobar2 my command here to print the line above which starts with # # another foobar sleep 1 if [ "1" -eq "1" ]; do sleep 1 my command here to print the line above which starts with # fi
预期输出:
# foobar1 # another foobar
此前尝试的命令 grep -B1 '^[^#]' $0 | head -1 无法处理命令与注释行之间有空行的场景,因为空行也会被匹配为^[^#],导致取到错误的行。
解决方案
使用awk结合当前行号$LINENO实现,它可以逐行跟踪符合条件的注释行,直到找到目标命令行,输出最近的有效注释。
将脚本中的目标命令替换为以下awk命令:
awk -v cmd_line="$LINENO" ' BEGIN { target = cmd_line } NR < target { if (/^#/) { last_valid = $0 } } NR == target { print last_valid } ' "$0"
替换后的完整脚本
#!/bin/ksh # foobar1 # foobar2 awk -v cmd_line="$LINENO" ' BEGIN { target = cmd_line } NR < target { if (/^#/) { last_valid = $0 } } NR == target { print last_valid } ' "$0" # another foobar sleep 1 if [ "1" -eq "1" ]; then sleep 1 awk -v cmd_line="$LINENO" ' BEGIN { target = cmd_line } NR < target { if (/^#/) { last_valid = $0 } } NR == target { print last_valid } ' "$0" fi
原理说明
-v cmd_line="$LINENO":将当前命令所在的行号传递给awk的target变量,明确要定位的目标行。NR < target:在处理到目标行之前,逐行检查每一行:如果行首直接以#开头(无前置空格/制表符),就更新last_valid变量为该行内容,始终保留最近的有效注释行。NR == target:当处理到目标命令行时,输出last_valid变量中存储的最近有效注释行。
原grep命令失效的原因:grep -B1 '^[^#]' $0会匹配所有非#开头的行,包括空行(空行的行首不属于#),导致当注释行与命令之间有空行时,grep会取空行的前一行,而非命令对应的最近有效注释行。
内容的提问来源于stack exchange,提问作者Feriman
相关产品推荐
相关产品推荐

