简体   繁体   English

使用tac和sed反转文件

[英]Reverse file using tac and sed

I have a usecase where I need to search and replace the last occurrence of a string in a file and write the changes back to the file. 我有一个用例,我需要搜索并替换文件中最后一个字符串,并将更改写回文件。 The case below is a simplified version of that usecase: 下面的案例是该用例的简化版本:

I'm attempting to reverse the file, make some changes reverse it back again and write to the file. 我正在尝试反转文件,进行一些更改,然后再将其反转并写入文件。 I've tried the following snippet for this: 我为此尝试了以下代码段:

tac test | sed s/a/b/ | sed -i '1!G;h;$!d' test

test is a text file with contents: test是一个包含内容的文本文件:

a
1 
2 
3
4
5

I was expecting this command to make no changes to the order of the file, but it has actually reversed the contents to: 我希望这个命令不会改变文件的顺序,但它实际上已将内容转换为:

5
4
3
2
1
b

How can i make the substitution as well as retain the order of the file? 如何进行替换以及保留文件的顺序?

Try to cat test | rev | sed -i '1!G;h;$!d' | rev 尝试cat test | rev | sed -i '1!G;h;$!d' | rev cat test | rev | sed -i '1!G;h;$!d' | rev

Or you can use only sed coomand: For example you want to replace ABC on DEF : 或者你只能使用sed coomand:例如你想在DEF上替换ABC

You need to add 'g' to the end of your sed: 您需要在sed的末尾添加“g”:

sed -e 's/\(.*\)ABC/\1DEF/g'

This tells sed to replace every occurrence of your regex ("globally") instead of only the first occurrence. 这告诉sed替换每次出现的正则表达式(“全局”)而不是仅替换第一次出现的正则表达式。

You should also add a $ , if you want to ensure that it is replacing the last occurrence of ABC on the line: 如果要确保它替换行中最后一次出现的ABC,您还应该添加$

sed -e 's/\(.*\)ABC$/\1DEF/g'

EDIT 编辑

Or simply add another | tac 或者只是添加另一个| tac | tac to your command: tac test | sed s/a/b/ | sed -i '1!G;h;$!d' | tac | tac你的命令: tac test | sed s/a/b/ | sed -i '1!G;h;$!d' | tac tac test | sed s/a/b/ | sed -i '1!G;h;$!d' | tac

您可以tac你的文件,在所需的图案中,第一次出现适用替代tac一次tee效果给您原来的名字重新命名它之前的临时文件:

tac file | sed '0,/a/{s//b/}' | tac > tmp && mv tmp file

Another way is to user grep to get the number of the last line that contains the text you want to change, then use sed to change that line: 另一种方法是使用grep来获取包含要更改的文本的最后一行的编号,然后使用sed更改该行:

$ linno=$( grep -n 'abc' <file> | tail -1 | cut -d: -f1 )

$ sed -i "${linno}s/abc/def/" <file>

Here is a way to do this in a single command using awk . 这是使用awk在单个命令中执行此操作的方法。

First input file: 第一个输入文件:

cat file
a
1
2
3
4
a
5

Now this awk command: 现在这个awk命令:

awk '{a[i++]=$0} END{p=i; while(i--) if (sub(/a/, "b", a[i])) break;
      for(i=0; i<p; i++) print a[i]}' file
a
1
2
3
4
b
5

To save output back into original file use: 要将输出保存回原始文件,请使用:

awk '{a[i++]=$0} END{p=i; while(i--) if (sub(/a/, "b", a[i])) break;
for(i=0; i<p; i++) print a[i]}' file >> $$.tmp && mv $$.tmp f

Another in awk. 另一个在awk。 First a test file: 首先是一个测试文件:

$ cat file
a
1
a
2
a

and solution: 和解决方案:

$ awk '
$0=="a" && NR>1 {             # when we meet "a"
    print b; b=""             # output and clear buffer b
}
{
    b=b (b==""?"":ORS) $0     # gether the buffer
}
END {                         # in the end
    sub(/^a/,"b",b)           # replace the leading "a" in buffer b with "b"
    print b                   # output buffer 
}' file
a
1
a
2
b

Writing back the happens by redirecting the output to a temp file which replaces the original file ( awk ... file > tmp && mv tmp file ) or if you are using GNU awk v. 4.1.0+ you can use inplace edit ( awk -i inplace ... ). 通过将输出重定向到替换原始文件的临时文件( awk ... file > tmp && mv tmp file )或者如果使用GNU awk v.4.1.0+,可以使用inplace edit( awk -i inplace ...来回写awk -i inplace ... )。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM