我有一个如下的文本文件
# jkakjshkjh
* drink (2 spaces * 2 spaces)(non hash starting)
* biscuit (1 space * 2 spaces)(non hash starting)
* paper (* 1 space)(non has starting)
... (many more lines) of non hash starting
* tea (7 spaces * 3 space)(non has starting)
# happy
* cup (* 1 space)(non has starting)
* bat (2 spaces * 2 spaces)(non hash starting)
* scooter (1 space * 2 spaces)(non hash starting)
... (many more lines) of non hash starting
* disk (7 spaces * 3 space)(non has starting)
我希望所有非哈希起始行都具有与第一条非哈希起始行相同的开始
即:
# jkakjshkjh
* drink (2 spaces * 2 spaces)(non hash starting)
* biscuit (2 spaces * 2 spaces)(non hash starting)
* paper (2 spaces * 2 spaces)(non hash starting)
... (many more lines of non hash starting)
* tea (2 spaces * 2 spaces)(non hash starting)
# happy
* cup (* 1 space)(non has starting)
* bat (* 1 space)(non has starting)
* scooter (* 1 space)(non has starting)
... (many more lines) of non hash starting
* disk (* 1 space)(non has starting)
现在,以上问题有所不同。
1)第一个非哈希行并不总是以(2个空格* 2个空格)开头
它可以变化(1个空格* 1个空格)或(前置空格的rad数量*帖子空格的随机数量)
2)在中间,如果有一行以哈希开头,则不应触摸该行
那么如何用sed解决以上问题
我尝试了以下方法:
sed -Ez 's/(\n)([^#]\s+\*\s+)([^\n]*\n)([^#]\s+\*\s+)([^\n]*\n)/\1\2\3\2\5/g' filename
上面只会检查两条结果行。这样做的问题是将2行视为一个单元。因此,两行的组将具有相同的开始。但我希望所有人都具有与第一条非哈希起跑线相同的开始
答案 0 :(得分:2)
如果awk也是一个选项,那么这里是一个可移植的选项:
awk 'match($0,/^ *\* */){
if(b) $0=b substr($0,RLENGTH+1)
else b=substr($0,1,RLENGTH)
} /^#/{b=""} 1' file
说明:
b
代表开始,即您在问题中使用它的含义(n个空间* n个空间),match($0, /^ *\* */)
与/^ *\* */
几乎相同,它匹配零个或多个空格,后跟一个*
,然后在$0
的开头匹配零个或多个空格(即当前行)。如果存在匹配项,RSTART
和RLENGTH
分别设置为匹配的子字符串开始的位置及其长度,
if (b)
是if (b != "")
的简写,并且在这里可以安全使用,因为在这种情况下b
的值不能为0,$0 = b substr($0, RLENGTH + 1)
用b
替换当前行的开头,b = substr($0, 1, RLENGTH)
将b
设置为当前行的开始,/^#/ { b = "" }
表示当前行是否以#
重置b
开始,1
表示打印$0
。答案 1 :(得分:1)
如果您可以采用非sed解决方案,则可以:使用GNU awk将第三个参数匹配():
$ cat tst.awk
{
match($0,/^(\s*(\S)\s*)(.*)/,a)
currHead = a[1]
currChar = a[2]
currTail = a[3]
}
currChar == "#" { indent = currHead }
currChar != "#" { indent = (prevChar == "#" ? currHead : indent) }
{ printf "%s%s\n", indent, currTail; prevChar = currChar }
$ awk -f tst.awk file
# jkakjshkjh
* drink (2 spaces * 2 spaces)(non hash starting)
* biscuit (1 space * 2 spaces)(non hash starting)
* paper (* 1 space)(non has starting)
* .. (many more lines) of non hash starting
* tea (7 spaces * 3 space)(non has starting)
# happy
* cup (* 1 space)(non has starting)
* bat (2 spaces * 2 spaces)(non hash starting)
* scooter (1 space * 2 spaces)(non hash starting)
* .. (many more lines) of non hash starting
* disk (7 spaces * 3 space)(non has starting)
通过其他操作,您只需使用substr()
来获取match()
放入a[]
中的部分,并使用[[:space:]]
和[^[:space:]]
分别用于\s
和\S
。
为帮助您理解语法,如果我是使用类似C的语言编写以上内容,那么它将是:
while ( read(FILENAME,line) ) { # awk does this for you
NR++; # awk does this for you
NF = split(line into $1, $2, $3, ... $NF); # awk does this for you
match(line,/^(\s*(\S)\s*)(.*)/,a);
currHead = a[1];
currChar = a[2];
currTail = a[3];
if (currChar == "#") { indent = currHead; }
if (currChar != "#") { indent = (prevChar == "#" ? currHead : indent); }
printf "%s%s\n", indent, currTail; prevChar = currChar;
} # awk does this for you
实际上,您可以在awks BEGIN部分中使用以下方式复制该语法:
BEGIN {
filename = ARGV[1]
ARGV[1] = ""
ARGC--
while ( (getline line < filename) > 0) ) {
nr++
nf = split(line,flds)
match(line,/^(\s*(\S)\s*)(.*)/,a)
currHead = a[1]
currChar = a[2]
currTail = a[3]
if (currChar == "#") { indent = currHead }
if (currChar != "#") { indent = (prevChar == "#" ? currHead : indent) }
printf "%s%s\n", indent, currTail; prevChar = currChar
}
}
但是请参见http://awk.freeshell.org/AllAboutGetline,除非您有非常特殊的需求,否则为什么不这样做。
答案 2 :(得分:1)
我认为这可以做到:
/
翻译:
如果该行以“#”开头,则也请阅读下一行,同时打印它们,然后删除第一行以及空格和星星串之后的所有内容,然后将剩下的内容(即空格和星星串)放入保持空间并结束。
否则,请删除前导空格和星号,附加保留空间的内容(即所需的空格和星号字符串),然后交换两部分(从而将所需的前缀放在前面)。
答案 3 :(得分:0)
如果您的“ d”文件中的数据尝试gnu sed,
sed -E ':b /#/{n; p;s/(\s*\*\s*).+/\1/;h;Tb;:l $!N;s/(.*)\n[* ]+(.+)/\1\2/;Tn;p;g;bl;:n D}' d