25

有没有办法,没有双循环来完成以下 sed 命令的作用

输入:

Time
Banana
spinach
turkey

sed -i "/Banana/ s/$/Toothpaste/" file

输出:

Time
BananaToothpaste
spinach
turkey

到目前为止,我所拥有的是一份双重清单,这两个清单都需要很长时间才能完成。

列表 a 有一堆数字 列表 b 有相同的一堆数字,但顺序不同

对于 A 中的每个条目,我想在 B 中找到具有相同数字的行并将值 C 添加到它的末尾。

希望这是有道理的,即使我的例子没有。

我在 Bash 中执行以下操作并且它正在工作但是它非常慢......

for line in $(cat DATSRCLN.txt.utf8); do
        srch=$(echo $line | awk -F'^' '{print $1}');
        rep=$(echo $line | awk -F'^' '{print $2}');
        sed -i "/$(echo $srch)/ s/$/^$(echo $rep)/" tmp.1;
done

谢谢!

4

8 回答 8

19

使用re.sub()

newstring = re.sub('(Banana)', r'\1Toothpaste', oldstring)

这会捕获一个组(在第一个括号之间),并将其替换为 ITSELF(\number 部分),后跟所需的后缀。需要使用r''(raw string) 以便正确解释转义。

于 2012-10-03T18:42:42.960 回答
17

比赛迟到了,这是我在 Python 中的 sed 实现:

import re
import shutil
from tempfile import mkstemp


def sed(pattern, replace, source, dest=None, count=0):
    """Reads a source file and writes the destination file.

    In each line, replaces pattern with replace.

    Args:
        pattern (str): pattern to match (can be re.pattern)
        replace (str): replacement str
        source  (str): input filename
        count (int): number of occurrences to replace
        dest (str):   destination filename, if not given, source will be over written.        
    """

    fin = open(source, 'r')
    num_replaced = count

    if dest:
        fout = open(dest, 'w')
    else:
        fd, name = mkstemp()
        fout = open(name, 'w')

    for line in fin:
        out = re.sub(pattern, replace, line)
        fout.write(out)

        if out != line:
            num_replaced += 1
        if count and num_replaced > count:
            break
    try:
        fout.writelines(fin.readlines())
    except Exception as E:
        raise E

    fin.close()
    fout.close()

    if not dest:
        shutil.move(name, source) 

例子:

sed('foo', 'bar', "foo.txt") 

将 foo.txt 中的所有 'foo' 替换为 'bar'

sed('foo', 'bar', "foo.txt", "foo.updated.txt")

将所有 'foo' 替换为 'foo.txt' 中的 'bar' 并将结果保存在 "foo.updated.txt" 中。

sed('foo', 'bar', "foo.txt", count=1)

将仅将第一次出现的 'foo' 替换为 'bar' 并将结果保存在原始文件 'foo.txt' 中

于 2016-11-28T11:49:30.793 回答
5

如果您使用的是 Python3,以下模块将为您提供帮助: https ://github.com/mahmoudadel2/pysed

wget https://raw.githubusercontent.com/mahmoudadel2/pysed/master/pysed.py

将模块文件放入您的 Python3 模块路径中,然后:

import pysed
pysed.replace(<Old string>, <Replacement String>, <Text File>)
pysed.rmlinematch(<Unwanted string>, <Text File>)
pysed.rmlinenumber(<Unwanted Line Number>, <Text File>)
于 2014-04-29T11:31:13.500 回答
5

您实际上可以从 python 调用 sed 。有很多方法可以做到这一点,但我喜欢使用 sh 模块。(yum -y 安装 python-sh)

我的示例程序的输出如下。

[me@localhost sh]$ cat input 
Time
Banana
spinich
turkey
[me@localhost sh]$ python test_sh.py 
[me@localhost sh]$ cat input 
Time
Toothpaste
spinich
turkey
[me@localhost sh]$ 

这是 test_sh.py

import sh

sh.sed('-i', 's/Banana/Toothpaste/', 'input')

这可能只适用于 LINUX。

于 2016-04-28T21:29:37.147 回答
2

可以使用系统要求低的 tmp 文件来执行此操作,并且只需一次迭代,而无需将整个文件复制到内存中:

#/usr/bin/python
import tempfile
import shutil
import os

newfile = tempfile.mkdtemp()
oldfile = 'stack.txt'

f = open(oldfile)
n = open(newfile,'w')

for i in f:
        if i.find('Banana') == -1:
                n.write(i)
                continue

        # Last row
        if i.find('\n') == -1:
                i += 'ToothPaste'
        else:
                i = i.rstrip('\n')
                i += 'ToothPaste\n'

        n.write(i) 

f.close()
n.close()

os.remove(oldfile)
shutil.move(newfile,oldfile)
于 2012-10-03T19:30:49.140 回答
1

我发现Oz123 提供的答案很棒,但似乎没有 100% 有效。我是 python 新手,但对它进行了修改并将其包装起来以在 bash 脚本中运行。这适用于osx,使用python 2.7。

# Replace 1 occurrence in file /tmp/1
$ search_replace "Banana" "BananaToothpaste" /tmp/1

# Replace 5 occurrences and save in /tmp/2
$ search_replace "Banana" "BananaToothpaste" /tmp/1 /tmp/2 5

搜索替换

#!/usr/bin/env python
import sys
import re
import shutil
from tempfile import mkstemp

total = len(sys.argv)-1
cmdargs = str(sys.argv)
if (total < 3):
    print ("Usage: SEARCH_FOR REPLACE_WITH IN_FILE {OUT_FILE} {COUNT}")
    print ("by default, the input file is replaced")
    print ("and the number of times to replace is 1")
    sys.exit(1)

# Parsing args one by one 
search_for = str(sys.argv[1])
replace_with = str(sys.argv[2])
file_name = str(sys.argv[3])
if (total < 4):
    file_name_dest=file_name
else:
    file_name_dest = str(sys.argv[4])
if (total < 5):
    count = 1
else:
    count = int(sys.argv[5])

def sed(pattern, replace, source, dest=None, count=0):
    """Reads a source file and writes the destination file.

    In each line, replaces pattern with replace.

    Args:
        pattern (str): pattern to match (can be re.pattern)
        replace (str): replacement str
        source  (str): input filename
        count (int): number of occurrences to replace
        dest (str):   destination filename, if not given, source will be over written.        
    """

    fin = open(source, 'r')
    num_replaced = 0

    fd, name = mkstemp()
    fout = open(name, 'w')

    for line in fin:
        if count and num_replaced < count:
            out = re.sub(pattern, replace, line)
            fout.write(out)
            if out != line:
                num_replaced += 1
        else:
            fout.write(line)

    fin.close()
    fout.close()

    if file_name == file_name_dest:
        shutil.move(name, file_name) 
    else:
        shutil.move(name, file_name_dest) 

sed(search_for, replace_with, file_name, file_name_dest, count)
于 2019-12-02T16:36:10.387 回答
0

多亏了上面的 Oz123,这里是 sed,它不是逐行的,因此您的替换可以跨越换行符。较大的文件可能是个问题。

import re
import shutil
from tempfile import mkstemp

def sed(pattern, replace, source, dest=None):
"""Reads a source file and writes the destination file.

Replaces pattern with replace globally through the file.
This is not line-by-line so the pattern can span newlines.

Args:
    pattern (str): pattern to match (can be re.pattern)
    replace (str): replacement str
    source  (str): input filename
    dest (str):   destination filename, if not given, source will be over written.
"""

if dest:
    fout = open(dest, 'w')
else:
    fd, name = mkstemp()
    fout = open(name, 'w')

with open(source, 'r') as file:
    data = file.read()

    p = re.compile(pattern)
    new_data = p.sub(replace, data)
    fout.write(new_data)

fout.close()

if not dest:
    shutil.move(name, source)
于 2020-12-03T16:36:22.990 回答
0

大众编辑

您可以将其用作命令行工具:

# Will change all test*.py in subdirectories of tests.
massedit.py -e "re.sub('failIf', 'assertFalse', line)" -s tests test*.py

您也可以将其用作库:

import massedit
filenames = ['massedit.py']
massedit.edit_files(filenames, ["re.sub('Jerome', 'J.', line)"])
于 2018-07-31T03:22:29.307 回答