Bash 使用不同的字符串在多行上编辑文件

Bash 使用不同的字符串在多行上编辑文件,bash,Bash,我正在编写一个脚本,该脚本将在文件的所有行中查找字符串a是否后跟下一行的字符串B。如果这不是真的,它将添加字符串C 我已经设法做到这一点,但我想在文件中的几个地方这样做。像这样 #!/bin/bash match="match1" match_following_line="following1" insert='line_insert1' file='words' testfile='testfile' #find linenumber for the first match linen

我正在编写一个脚本,该脚本将在文件的所有行中查找字符串a是否后跟下一行的字符串B。如果这不是真的,它将添加字符串C

我已经设法做到这一点,但我想在文件中的几个地方这样做。像这样

#!/bin/bash

match="match1"
match_following_line="following1"
insert='line_insert1'

file='words'
testfile='testfile'

#find linenumber for the first match
linenumber=$(awk -v myvar="$match" '$0~myvar {print NR}' $file)

#calculate linenumber for the comming line
linenumber2=$(( linenumber + 1 ))

#find string of that line
linecontent=$(sed -n "$linenumber2"p < $file)
echo $linecontent
#if it isn't match2 then add match2
if [ "$linecontent" != "$match_following_line" ]
then
    awk -v line="$linenumber" -v text="\n$insert" ' NR!=line{print} NR==line{print $0 text}' $file > $testfile
fi
此文件
testfile
的输出将为:

A
B
C
D
match1
line_insert1
After execusion the line before this line will become "line_insert1"
E
我试过几行:

match[0]='match1'
match_follower[0]='following1'
insert[0]='line_insert1'

match[1]='match2'
match_follower[1]='following2'
insert[1]='line_insert2'

match[2]='match3'
match_follower[2]='following3'
insert[2]='line_insert3'

file='1.csv'

for i in 0 1 2
do

#find linenumber for the first match
linenumber=$(awk -v myvar="${match[$i]}" '$0~myvar {print NR}' $file)
echo $linenumber
#calculate linenumber for the comming line
linenumber2=$(( linenumber + 1 ))

#find string of that line
linecontent=$(sed -n "$linenumber2"p < $file)
#if it isn't match_follower then add insert
if [ "$linecontent" != "${match_follower[$i]}" ]
then
        awk -v line="$linenumber" -v text="${insert[$i]}" ' NR!=line{print} NR==line{print $0 text}' $file > $file
fi
done
match[0]=“match1”
匹配跟随者[0]=“跟随者1”
插入[0]=“行插入1”
匹配[1]=“匹配2”
匹配跟随者[1]=“跟随者2”
插入[1]=“行插入2”
匹配[2]=“匹配3”
匹配跟随者[2]=“跟随者3”
插入[2]='line\u insert3'
文件='1.csv'
对于0 1 2中的i
做
#查找第一个匹配的行号
linenumber=$(awk-v myvar=“${match[$i]}”'$0~myvar{print NR}'$file)
echo$行号
#计算输送线的线号
行号2=$((行号+1))
#找到那一行的字符串
linecontent=$(sed-n“$linenumber2”p<$文件)
#如果不匹配,则添加插入
如果[“$linecontent”!=“${match_follower[$i]}”]
然后
awk-v line=“$linenumber”-v text=“${insert[$i]}”NR=行{print}NR==行{print$0 text}'$file>$file
fi
完成
当我尝试对几个字符串执行此操作时,我不知道当awk写入$testfile并在每个循环中覆盖该文件时该怎么做。有人知道解决这个问题的好办法吗

match[0]='match1'
match_follower[0]='following1'
insert[0]='line_insert1'

match[1]='match2'
match_follower[1]='following2'
insert[1]='line_insert2'

match[2]='match3'
match_follower[2]='following3'
insert[2]='line_insert3'

file='1.csv'

for i in 0 1 2
do

sed -E -i '/'"${match[$i]}"'/!b;n;/^'"${match_follower[$i]}"'|^'"${insert[$i]}"'/! i '"${insert[$i]}" ${file}

done
为了在
sed
中使用shell变量,必须关闭并打开变量的
'
,,然后重新打开
'
等。这就是为什么sed行中有这么多qoute

首先,对于循环sed,如下所示:

sed -E -i '/match1/!b;n;/^following1|^line_insert1/! i line_insert1' 1.csv
在哪里

/match1/查找不(
)包含
match1
的行,然后
b
阻止在当前行上执行sed脚本的其余部分。如果不是这样,sed脚本将继续执行
n
是命令块之间的分隔符)

n
将下一行读入模式空间,因此sed脚本的其余部分将在下一行执行

/^1 | ^line_insert1/
这确保脚本仅在当前模式空间(行)不(
)启动(
^
)时插入行,使用
以下1
行插入1
-E
开关需要此regexp才能工作

i line\u insert1
这是使用给定文本插入新行的实际行
i
是插入运算符

sed-i
将文件更改到位


使用AWK,只需将sed线替换为:

awk -v v_match="${match[$i]}" -v v_follower="${match_follower[$i]}" -v v_insert="${insert[$i]}" '
{if (find_match == 1  && $0 !~ v_insert  && $0 !~ v_follower  ) { find_match=0 ; print v_insert } { find_match=0 ;print } } 
$0 ~ v_match { find_match=1 }' ${file} > tmp_file && mv tmp_file ${file}
$0~v_match{find_match=1}
如果行是
${match[$i]}
find_match
变量设置为
1

{if(find_match==1&&$0!~v_insert&$0!~v_follower){find_match=0;print}}
如果
find_match
1
而行不是
${match_follower[$i]}
{insert[$i]}
则打印
然后只打印当前行,在这两种情况下,将
find_match
设置为
0


>tmp_文件和&mv tmp_文件${file}
因为awk没有像sed这样的更改文件就地功能,所以需要临时文件(
tmp_文件
)。

您提前定位目标线位置的方法可能无法正常工作 在这种情况下,行号会动态变化,尤其是在 一行一行地添加多行

请尝试以下操作:

#/bin/bash

declare -a match=("match1" "match2" "match3")
declare -a match_follower=("following1" "following2" "following3")
declare -a insert=("line_insert1" "line_insert2" "line_insert3")

file="1.csv"

declare -a list
readarray -t list < "$file"

for i in "${!match[@]}"; do
    for j in "${!list[@]}"; do
        if [[ ${match[$i]} = ${list[$j]} ]] && [[ ${match_follower[$i]} != ${list[$(( $j + 1 ))]} ]]; then
            k=$(( j + 1 ))
            list=( "${list[@]:0:$k}" "${insert[$i]}" "${list[@]:$k}" )
            j=$k
        fi
    done
done

(IFS=$'\n'; echo "${list[*]}")
输出

A
B
C
D
match1
line_insert1
After execusion the line before this line will become "line_insert1"
E
F
G
match2
following2
After execusion the line before this line will NOT become "line_insert2"
H
I
J
match3
line_insert3
After execusion the line before this line will become "line_insert3"
K
  • readarray
    将输入文件读入数组,设置数组元素 每行。(需要bash 4.0或更高版本。)
  • 脚本在这些行上迭代。如果一行与
    ${match[$i]}
    下一行与
    ${match\u follower[$i]}
    然后在下一个索引中插入元素
    ${insert[$i]}

请给出一个示例文件和所需的输出。请认识到
awk
具有一次(可能2次)读取文件所需的所有工具,并在一组代码中执行所需的所有操作和逻辑。每次代码查找规则的匹配项时,为该条件设置一个标志变量(即
/match1/{m1=1}
)。然后测试你的标志条件,当它(或全部)为真时,执行你的操作。(
如果{m1&&m2&&m3{do stuff}
)你也可以依靠
print$0
根据需要输出当前行。祝你好运。谢谢,sed one行程序工作得很好。你介意解释一下里面发生了什么吗?
sed-i'/'“${match[$i]}”“/!b;n;/^'${match_follower[$i]}”“/!i'${insert[$i]}”${file}
出于某种原因,sed脚本只工作一次。当我再次运行脚本时,文件不应更改。但是,行会再次添加。
^'${insert[$i]}”“
-E
必须添加,以处理行已经添加,而不是再次添加的情况,我将很快更新描述sed正在做什么。还修复了awk,并添加了更多详细的解释sed是如何做的。如果您有任何其他问题,请告诉我。非常感谢,非常有用和有趣!
A
B
C
D
match1
After execusion the line before this line will become "line_insert1"
E
F
G
match2
following2
After execusion the line before this line will NOT become "line_insert2"
H
I
J
match3
After execusion the line before this line will become "line_insert3"
K
A
B
C
D
match1
line_insert1
After execusion the line before this line will become "line_insert1"
E
F
G
match2
following2
After execusion the line before this line will NOT become "line_insert2"
H
I
J
match3
line_insert3
After execusion the line before this line will become "line_insert3"
K