从Bash命令中查找并替换文本文件中的文本文件。

时间:2022-09-04 00:29:32

What's the simplest way to do a find and replace for a given input string, say abc, and replace with another string, say XYZ in file /tmp/file.txt?

对给定输入字符串进行查找和替换的最简单方法是什么,比如abc,然后用另一个字符串替换,比如文件/tmp/file.txt中的XYZ ?

I am writting an app and using IronPython to execute commands through SSH — but I don't know Unix that well and don't know what to look for.

我正在编写一个应用程序,并使用IronPython来通过SSH执行命令——但我不太了解Unix,也不知道要查找什么。

I have heard that Bash, apart from being a command line interface, can be a very powerful scripting language. So, if this is true, I assume you can perform actions like these.

我听说Bash除了是命令行接口之外,可以是一种非常强大的脚本语言。因此,如果这是真的,我假设您可以执行类似的操作。

Can I do it with bash, and what's the simplest (one line) script to achieve my goal?

我可以用bash来完成它吗?什么是最简单的(一行)脚本实现我的目标?

15 个解决方案

#1


588  

The easiest way is to use sed (or perl):

最简单的方法是使用sed(或perl):

sed -i -e 's/abc/XYZ/g' /tmp/file.txt

sed -i -e 's/abc/XYZ/g' /tmp/file.txt。

Which will invoke sed to do an in-place edit due to the -i option. This can be called from bash.

由于-i选项,将调用sed进行就地编辑。这可以从bash调用。

If you really really want to use just bash, then the following can work:

如果您真的非常想使用bash,那么下面的方法可以:

while read a ; do echo ${a//abc/XYZ} ; done < /tmp/file.txt > /tmp/file.txt.t ; mv /tmp/file.txt{.t,}

而阅读;做echo $ { / / abc / XYZ };< / tmp /文件完成。txt > / tmp / file.txt。t;mv / tmp / file.txt { .t,}

This loops over each line, doing a substitution, and writing to a temporary file (don't want to clobber the input). The move at the end just moves temporary to the original name.

这个循环遍历每一行,做一个替换,并写入一个临时文件(不要阻塞输入)。最后的动作只是暂时的移动到原来的名字。

#2


146  

File manipulation isn't normally done by Bash, but by programs invoked by Bash, e.g.:

文件操作通常不是由Bash完成的,而是由Bash调用的程序完成的。

> perl -pi -e 's/abc/XYZ/g' /tmp/file.txt

The -i flag tells it to do an in-place replacement.

i标志告诉它做一个就地替换。

See man perlrun for more details, including how to take a backup of the original file.

查看man perlrun了解更多细节,包括如何对原始文件进行备份。

#3


56  

I was surprised because i stumbled over this...

我很惊讶,因为我偶然发现了这个…

There is a "replace" command which ships with the package "mysql-server", so if you have installed it try it out:

有一个“替换”命令,它使用“mysql-server”包,所以如果您安装了它,可以尝试一下:

# replace string abc to XYZ in files
replace "abc" "XYZ" -- file.txt file2.txt file3.txt

# or pipe an echo to replace
echo "abcdef" |replace "abc" "XYZ"

See man replace for more on this...

关于这个问题,请参阅man替换。

#4


34  

Bash, like other shells, is just a tool for coordinating other commands. Typically you would try to use standard UNIX commands, but you can of course use Bash to invoke anything, including your own compiled programs, other shell scripts, Python and Perl scripts etc.

Bash和其他shell一样,只是用来协调其他命令的工具。通常,您会尝试使用标准的UNIX命令,但是您当然可以使用Bash来调用任何东西,包括您自己的编译程序、其他shell脚本、Python和Perl脚本等等。

In this case, there are a couple of ways to do it.

在这种情况下,有几种方法可以做到这一点。

If you want to read a file, and write it to another file, doing search/replace as you go, use sed:

如果你想读一个文件,并把它写到另一个文件,在你去的时候做搜索/替换,用sed:

sed 's/abc/XYZ/g' <infile >outfile

If you want to edit the file in place (as if opening the file in an editor, editing it, then saving it) supply instructions to the line editor 'ex'

如果您想要编辑该文件(就像在编辑器中打开文件,编辑它,然后将它保存),请向行编辑器“ex”提供指令

echo "%s/abc/XYZ/g
w
q
" | ex file

Ex is like vi without the fullscreen mode. You can give it the same commands you would at vi's ':' prompt.

Ex就像没有全屏模式的vi。你可以给它同样的命令,你会在vi' ':提示。

#5


30  

Found this thread among others and I agree it contains the most complete answers so I add mine too:

找到了这条线索,我同意它包含了最完整的答案,所以我也添加了我的答案:

1) sed and ed are so useful...by hand!!! Look at this code from @Johnny:

sed和ed非常有用……用手! ! !看看这个来自@Johnny的代码:

sed -i -e 's/abc/XYZ/g' /tmp/file.txt

2) when my restriction is to use it by a shell script then, no variable can be used inside in place of abc or XYZ! This seems to agree with what I understand at least. So, I can't use:

2)当我的限制是由一个shell脚本使用它时,没有变量可以在abc或XYZ的地方使用!这似乎与我所理解的至少是一致的。所以,我不能使用:

x='abc'
y='XYZ'
sed -i -e 's/$x/$y/g' /tmp/file.txt
#or,
sed -i -e "s/$x/$y/g" /tmp/file.txt

but, what can we do? As, @Johnny said use a 'while read...' but, unfortunately that's not the end of the story. The following worked well with me:

但是,我们能做什么呢?@Johnny说“在阅读的时候……”但不幸的是,这并不是故事的结局。以下是我的工作:

#edit user's virtual domain
result=
#if nullglob is set then, unset it temporarily
is_nullglob=$( shopt -s | egrep -i '*nullglob' )
if [[ is_nullglob ]]; then
   shopt -u nullglob
fi
while IFS= read -r line; do
   line="${line//'<servername>'/$server}"
   line="${line//'<serveralias>'/$alias}"
   line="${line//'<user>'/$user}"
   line="${line//'<group>'/$group}"
   result="$result""$line"'\n'
done < $tmp
echo -e $result > $tmp
#if nullglob was set then, re-enable it
if [[ is_nullglob ]]; then
   shopt -s nullglob
fi
#move user's virtual domain to Apache 2 domain directory
......

3) As one can see if nullglob is set then, it behaves strangely when there is a string containing a * as in

3)当一个人可以看到nullglob是否被设置时,当有一个包含*的字符串时,它的行为会很奇怪。

<VirtualHost *:80>
 ServerName www.example.com

which becomes

这就变成了

<VirtualHost ServerName www.example.com

there is no ending angle bracket and Apache2 can't even load!

没有结束角括号,Apache2甚至不能加载!

4) This kind of parsing should be slower than one-hit search and replace but, as you already have seen, there are 4 variables for 4 different search patterns working out of one only parse cycle!

4)这种解析应该比一次性的搜索和替换要慢,但是,正如您已经看到的那样,4个不同的搜索模式有4个变量,它们只在一个解析周期中运行!

The most suitable solution I can think of with the given assumptions of the problem.

我能想到的最合适的解决方案是考虑到问题的假设。

#6


22  

This is an old post but for anyone wanting to use variables as @centurian said the single quotes mean nothing will be expanded.

这是一个旧的帖子,但是对于任何想要使用变量的人来说,@centurian说,单引号意味着什么都不会扩展。

A simple way to get variables in is to do string concatenation since this is done by juxtaposition in bash the following should work:

获取变量的一种简单方法是进行字符串连接,因为这是通过在bash中并置来完成的,如下所示:

sed -i -e 's/'"$var1"'/'"$var2"'/g' /tmp/file.txt

sed -i -e 's/' $var1 ' '/' $var2 ' '/g' /tmp/file.txt。

#7


11  

You may also use the ed command to do in-file search and replace:

您还可以使用ed命令进行文件内搜索和替换:

# delete all lines matching foobar 
ed -s test.txt <<< $'g/foobar/d\nw' 

See more on bash-hackers site

更多关于bash-黑客站点的信息。

#8


10  

You can use sed

您可以使用sed

sed -i 's/abc/XYZ/gi' /tmp/file.txt

Use i for ignore case if You not sure text to find is abc or ABC or AbC ,...

如果你不确定要查找的文本是abc或abc或abc,请用i来忽略它。

You can use find and sed if you don't now your filename:

你可以使用find和sed如果你现在不知道你的文件名:

 find ./ -type f -exec sed -i 's/abc/XYZ/gi' {} \;

Find and replace in all python files:

查找并替换所有python文件:

find ./ -iname "*.py" -type f -exec sed -i 's/abc/XYZ/gi' {} \;

#9


6  

Be careful if you replace URLs with "/" character.

如果用“/”字符替换url,要小心。

An example of how to do it:

如何做到这一点的一个例子:

sed -i "s%http://domain.com%http://www.domain.com/folder/%g" "test.txt"

Extracted from: http://www.sysadmit.com/2015/07/linux-reemplazar-texto-en-archivos-con-sed.html

提取:http://www.sysadmit.com/2015/07/linux-reemplazar-texto-en-archivos-con-sed.html

#10


3  

If the file you are working on is not so big, and temporarily storing it in a variable is no problem, then you can use Bash string substitution on the whole file at once - there's no need to go over it line by line:

如果您正在处理的文件不是那么大,并且暂时将其存储在一个变量中,这是没有问题的,那么您可以一次使用Bash字符串替换整个文件——不需要逐行检查它:

file_contents=$(</tmp/file.txt)
echo "${file_contents//abc/XYZ}" > /tmp/file.txt

The whole file contents will be treated as one long string, including linebreaks.

整个文件内容将被视为一个长字符串,包括换行符。

XYZ can be a variable eg $replacement, and one advantage of not using sed here is that you need not be concerned that the search or replace string might contain the sed pattern delimiter character (usually, but not necessarily, /). A disadvantage is not being able to use regular expressions or any of sed's more sophisticated operations.

XYZ可以是一个变量,例如$replace,这里不使用sed的一个优点是,您不必担心搜索或替换字符串可能包含sed模式分隔符(通常是,但不一定是/)。缺点是不能使用正则表达式或任何sed的更复杂的操作。

#11


2  

To edit text in the file non-interactively, you need in-place text editor such as vim.

要在文件中不交互地编辑文本,您需要像vim这样的就地文本编辑器。

Here is simple example how to use it from the command line:

这里有一个简单的例子,如何从命令行使用它:

vim -esnc '%s/foo/bar/g|:wq' file.txt

This is equivalent to @slim answer of ex editor which is basically the same thing.

这等价于前编辑器的@slim回答,基本上是一样的。

Here are few ex practical examples.

下面是一些实际的例子。

Replacing text foo with bar in the file:

将文本foo替换为文件中的bar:

ex -s +%s/foo/bar/ge -cwq file.txt

Removing trailing whitespaces for multiple files:

删除多个文件的拖尾空白:

ex +'bufdo!%s/\s\+$//e' -cxa *.txt

See also:

参见:

#12


2  

find ./ -type f -name "file*.txt" | xargs sed -i -e 's/abc/xyz/g'

查找。/ -类型f -name "文件*。txt" | xargs sed -i -e 's/abc/xyz/g'

#13


1  

You can use rpl command. For example you want to change domain name in whole php project.

您可以使用rpl命令。例如,您希望在整个php项目中更改域名。

rpl -ivRpd -x'.php' 'old.domain.name' 'new.domain.name' ./path_to_your_project_folder/  

This is not clear bash of cause, but it's a very quick and usefull. :)

这不是一个明确的原因,但它是一个非常快速和有用的。:)

#14


1  

You can use python within the bash script too. I didn't have much success with some of the top answers here, and found this to work without the need for loops:

您也可以在bash脚本中使用python。我在一些最上面的答案中并没有取得太多的成功,并且在不需要循环的情况下发现了这一点:

#!/bin/bash
python
filetosearch = '/home/ubuntu/ip_table.txt'
texttoreplace = 'tcp443'
texttoinsert = 'udp1194'

s = open(filetosearch).read()
s = s.replace(texttoreplace, texttoinsert)
f = open(filetosearch, 'w')
f.write(s)
f.close()
quit()

#15


0  

Now that this thread seems to have turned into replacing strings (of bytes) with any other language than bash, here is a stupid C implementation:

现在,这个线程似乎已经变成了用任何其他语言替换字符串(字节),这是一个愚蠢的C实现:

<!-- language: c -->

/**
 * Usage:
 *      ./replace "foobar" "foobaz" < input_file > output_file
 * Note: input_file and output_file should be different
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

typedef struct string_t {
    const char * value;
    size_t length;
} string;

struct parser_t {
    string match_text, replace_text;

    char * match_buffer;
    unsigned int match_buffer_index;

    enum { STATE_INVALID, STATE_IN, STATE_OUT } state;
};

void parser_init(struct parser_t * parser, 
                 const char * match_text,
                 const char * replace_text)
{
    memset(parser, 0, sizeof(struct parser_t));

    parser->match_text.value = match_text;
    parser->match_text.length = strlen(match_text);
    parser->replace_text.value = replace_text;
    parser->replace_text.length = strlen(replace_text);
    parser->state = STATE_OUT;
    parser->match_buffer = malloc(parser->match_text.length);
}

void parser_free(struct parser_t * parser)
{
    free(parser->match_buffer);
}

void output_char(char current_char)
{
    fwrite(&current_char, sizeof(char), 1, stdout);
}

void buffer_match(struct parser_t * parser, char current_char)
{
    parser->match_buffer[parser->match_buffer_index++] = current_char;
}

void buffer_clear(struct parser_t * parser)
{
    parser->match_buffer_index = 0;
}

void buffer_flush(struct parser_t * parser)
{
    if (parser->match_buffer_index > 0) {
        fwrite(parser->match_buffer, sizeof(char), parser->match_buffer_index, stdout);
        buffer_clear(parser);
    }
}

int process_state_in(struct parser_t * parser, char current_char)
{
    if (parser->match_text.value[parser->match_buffer_index] == current_char) {
        buffer_match(parser, current_char);

        return STATE_IN;
    }

    if (parser->match_buffer_index == parser->match_text.length) {
        fwrite(parser->replace_text.value, sizeof(char), parser->replace_text.length, stdout);
        buffer_clear(parser);

        output_char(current_char);

        return STATE_OUT;
    }

    if (parser->match_text.value[parser->match_buffer_index] != current_char) {
        buffer_flush(parser);
        output_char(current_char);

        return STATE_OUT;
    }

    return STATE_INVALID;
}

int process_state_out(struct parser_t * parser, char current_char)
{
    if (parser->match_text.value[parser->match_buffer_index] == current_char) {
        buffer_match(parser, current_char);

        return STATE_IN;
    } 

    if (parser->match_text.value[parser->match_buffer_index] != current_char) {
        buffer_flush(parser);
        output_char(current_char);

        return STATE_OUT;
    }

    return STATE_INVALID;
}

int main(int argc, char *argv[])
{
    char current_char;
    struct parser_t parser;

    if (argc != 3) {
        fprintf(stdout, "Usage:\n\t%s match_text replace_text < in_file > out_file\n\t# note in_file and out_file should be different.\n", argv[0]);
        return 0;
    }

    parser_init(&parser, argv[1], argv[2]);

    while (fread(&current_char, sizeof(char), 1, stdin) != 0) {
        switch (parser.state) {
            case STATE_IN:
            {
                parser.state = process_state_in(&parser, current_char);
            }
            break;
            case STATE_OUT:
            {
                parser.state = process_state_out(&parser, current_char);
            }
            break;
            default:
                fprintf(stderr, "Error: Invalid state.\n");
                return -1;
            break;
        }
    }

    parser_free(&parser);

    return 0;
}

Compile and run:

编译并运行:

$ cc replace.c -oreplace
$ ./replace "foobar" "foobaz" < input_file > output_file

#1


588  

The easiest way is to use sed (or perl):

最简单的方法是使用sed(或perl):

sed -i -e 's/abc/XYZ/g' /tmp/file.txt

sed -i -e 's/abc/XYZ/g' /tmp/file.txt。

Which will invoke sed to do an in-place edit due to the -i option. This can be called from bash.

由于-i选项,将调用sed进行就地编辑。这可以从bash调用。

If you really really want to use just bash, then the following can work:

如果您真的非常想使用bash,那么下面的方法可以:

while read a ; do echo ${a//abc/XYZ} ; done < /tmp/file.txt > /tmp/file.txt.t ; mv /tmp/file.txt{.t,}

而阅读;做echo $ { / / abc / XYZ };< / tmp /文件完成。txt > / tmp / file.txt。t;mv / tmp / file.txt { .t,}

This loops over each line, doing a substitution, and writing to a temporary file (don't want to clobber the input). The move at the end just moves temporary to the original name.

这个循环遍历每一行,做一个替换,并写入一个临时文件(不要阻塞输入)。最后的动作只是暂时的移动到原来的名字。

#2


146  

File manipulation isn't normally done by Bash, but by programs invoked by Bash, e.g.:

文件操作通常不是由Bash完成的,而是由Bash调用的程序完成的。

> perl -pi -e 's/abc/XYZ/g' /tmp/file.txt

The -i flag tells it to do an in-place replacement.

i标志告诉它做一个就地替换。

See man perlrun for more details, including how to take a backup of the original file.

查看man perlrun了解更多细节,包括如何对原始文件进行备份。

#3


56  

I was surprised because i stumbled over this...

我很惊讶,因为我偶然发现了这个…

There is a "replace" command which ships with the package "mysql-server", so if you have installed it try it out:

有一个“替换”命令,它使用“mysql-server”包,所以如果您安装了它,可以尝试一下:

# replace string abc to XYZ in files
replace "abc" "XYZ" -- file.txt file2.txt file3.txt

# or pipe an echo to replace
echo "abcdef" |replace "abc" "XYZ"

See man replace for more on this...

关于这个问题,请参阅man替换。

#4


34  

Bash, like other shells, is just a tool for coordinating other commands. Typically you would try to use standard UNIX commands, but you can of course use Bash to invoke anything, including your own compiled programs, other shell scripts, Python and Perl scripts etc.

Bash和其他shell一样,只是用来协调其他命令的工具。通常,您会尝试使用标准的UNIX命令,但是您当然可以使用Bash来调用任何东西,包括您自己的编译程序、其他shell脚本、Python和Perl脚本等等。

In this case, there are a couple of ways to do it.

在这种情况下,有几种方法可以做到这一点。

If you want to read a file, and write it to another file, doing search/replace as you go, use sed:

如果你想读一个文件,并把它写到另一个文件,在你去的时候做搜索/替换,用sed:

sed 's/abc/XYZ/g' <infile >outfile

If you want to edit the file in place (as if opening the file in an editor, editing it, then saving it) supply instructions to the line editor 'ex'

如果您想要编辑该文件(就像在编辑器中打开文件,编辑它,然后将它保存),请向行编辑器“ex”提供指令

echo "%s/abc/XYZ/g
w
q
" | ex file

Ex is like vi without the fullscreen mode. You can give it the same commands you would at vi's ':' prompt.

Ex就像没有全屏模式的vi。你可以给它同样的命令,你会在vi' ':提示。

#5


30  

Found this thread among others and I agree it contains the most complete answers so I add mine too:

找到了这条线索,我同意它包含了最完整的答案,所以我也添加了我的答案:

1) sed and ed are so useful...by hand!!! Look at this code from @Johnny:

sed和ed非常有用……用手! ! !看看这个来自@Johnny的代码:

sed -i -e 's/abc/XYZ/g' /tmp/file.txt

2) when my restriction is to use it by a shell script then, no variable can be used inside in place of abc or XYZ! This seems to agree with what I understand at least. So, I can't use:

2)当我的限制是由一个shell脚本使用它时,没有变量可以在abc或XYZ的地方使用!这似乎与我所理解的至少是一致的。所以,我不能使用:

x='abc'
y='XYZ'
sed -i -e 's/$x/$y/g' /tmp/file.txt
#or,
sed -i -e "s/$x/$y/g" /tmp/file.txt

but, what can we do? As, @Johnny said use a 'while read...' but, unfortunately that's not the end of the story. The following worked well with me:

但是,我们能做什么呢?@Johnny说“在阅读的时候……”但不幸的是,这并不是故事的结局。以下是我的工作:

#edit user's virtual domain
result=
#if nullglob is set then, unset it temporarily
is_nullglob=$( shopt -s | egrep -i '*nullglob' )
if [[ is_nullglob ]]; then
   shopt -u nullglob
fi
while IFS= read -r line; do
   line="${line//'<servername>'/$server}"
   line="${line//'<serveralias>'/$alias}"
   line="${line//'<user>'/$user}"
   line="${line//'<group>'/$group}"
   result="$result""$line"'\n'
done < $tmp
echo -e $result > $tmp
#if nullglob was set then, re-enable it
if [[ is_nullglob ]]; then
   shopt -s nullglob
fi
#move user's virtual domain to Apache 2 domain directory
......

3) As one can see if nullglob is set then, it behaves strangely when there is a string containing a * as in

3)当一个人可以看到nullglob是否被设置时,当有一个包含*的字符串时,它的行为会很奇怪。

<VirtualHost *:80>
 ServerName www.example.com

which becomes

这就变成了

<VirtualHost ServerName www.example.com

there is no ending angle bracket and Apache2 can't even load!

没有结束角括号,Apache2甚至不能加载!

4) This kind of parsing should be slower than one-hit search and replace but, as you already have seen, there are 4 variables for 4 different search patterns working out of one only parse cycle!

4)这种解析应该比一次性的搜索和替换要慢,但是,正如您已经看到的那样,4个不同的搜索模式有4个变量,它们只在一个解析周期中运行!

The most suitable solution I can think of with the given assumptions of the problem.

我能想到的最合适的解决方案是考虑到问题的假设。

#6


22  

This is an old post but for anyone wanting to use variables as @centurian said the single quotes mean nothing will be expanded.

这是一个旧的帖子,但是对于任何想要使用变量的人来说,@centurian说,单引号意味着什么都不会扩展。

A simple way to get variables in is to do string concatenation since this is done by juxtaposition in bash the following should work:

获取变量的一种简单方法是进行字符串连接,因为这是通过在bash中并置来完成的,如下所示:

sed -i -e 's/'"$var1"'/'"$var2"'/g' /tmp/file.txt

sed -i -e 's/' $var1 ' '/' $var2 ' '/g' /tmp/file.txt。

#7


11  

You may also use the ed command to do in-file search and replace:

您还可以使用ed命令进行文件内搜索和替换:

# delete all lines matching foobar 
ed -s test.txt <<< $'g/foobar/d\nw' 

See more on bash-hackers site

更多关于bash-黑客站点的信息。

#8


10  

You can use sed

您可以使用sed

sed -i 's/abc/XYZ/gi' /tmp/file.txt

Use i for ignore case if You not sure text to find is abc or ABC or AbC ,...

如果你不确定要查找的文本是abc或abc或abc,请用i来忽略它。

You can use find and sed if you don't now your filename:

你可以使用find和sed如果你现在不知道你的文件名:

 find ./ -type f -exec sed -i 's/abc/XYZ/gi' {} \;

Find and replace in all python files:

查找并替换所有python文件:

find ./ -iname "*.py" -type f -exec sed -i 's/abc/XYZ/gi' {} \;

#9


6  

Be careful if you replace URLs with "/" character.

如果用“/”字符替换url,要小心。

An example of how to do it:

如何做到这一点的一个例子:

sed -i "s%http://domain.com%http://www.domain.com/folder/%g" "test.txt"

Extracted from: http://www.sysadmit.com/2015/07/linux-reemplazar-texto-en-archivos-con-sed.html

提取:http://www.sysadmit.com/2015/07/linux-reemplazar-texto-en-archivos-con-sed.html

#10


3  

If the file you are working on is not so big, and temporarily storing it in a variable is no problem, then you can use Bash string substitution on the whole file at once - there's no need to go over it line by line:

如果您正在处理的文件不是那么大,并且暂时将其存储在一个变量中,这是没有问题的,那么您可以一次使用Bash字符串替换整个文件——不需要逐行检查它:

file_contents=$(</tmp/file.txt)
echo "${file_contents//abc/XYZ}" > /tmp/file.txt

The whole file contents will be treated as one long string, including linebreaks.

整个文件内容将被视为一个长字符串,包括换行符。

XYZ can be a variable eg $replacement, and one advantage of not using sed here is that you need not be concerned that the search or replace string might contain the sed pattern delimiter character (usually, but not necessarily, /). A disadvantage is not being able to use regular expressions or any of sed's more sophisticated operations.

XYZ可以是一个变量,例如$replace,这里不使用sed的一个优点是,您不必担心搜索或替换字符串可能包含sed模式分隔符(通常是,但不一定是/)。缺点是不能使用正则表达式或任何sed的更复杂的操作。

#11


2  

To edit text in the file non-interactively, you need in-place text editor such as vim.

要在文件中不交互地编辑文本,您需要像vim这样的就地文本编辑器。

Here is simple example how to use it from the command line:

这里有一个简单的例子,如何从命令行使用它:

vim -esnc '%s/foo/bar/g|:wq' file.txt

This is equivalent to @slim answer of ex editor which is basically the same thing.

这等价于前编辑器的@slim回答,基本上是一样的。

Here are few ex practical examples.

下面是一些实际的例子。

Replacing text foo with bar in the file:

将文本foo替换为文件中的bar:

ex -s +%s/foo/bar/ge -cwq file.txt

Removing trailing whitespaces for multiple files:

删除多个文件的拖尾空白:

ex +'bufdo!%s/\s\+$//e' -cxa *.txt

See also:

参见:

#12


2  

find ./ -type f -name "file*.txt" | xargs sed -i -e 's/abc/xyz/g'

查找。/ -类型f -name "文件*。txt" | xargs sed -i -e 's/abc/xyz/g'

#13


1  

You can use rpl command. For example you want to change domain name in whole php project.

您可以使用rpl命令。例如,您希望在整个php项目中更改域名。

rpl -ivRpd -x'.php' 'old.domain.name' 'new.domain.name' ./path_to_your_project_folder/  

This is not clear bash of cause, but it's a very quick and usefull. :)

这不是一个明确的原因,但它是一个非常快速和有用的。:)

#14


1  

You can use python within the bash script too. I didn't have much success with some of the top answers here, and found this to work without the need for loops:

您也可以在bash脚本中使用python。我在一些最上面的答案中并没有取得太多的成功,并且在不需要循环的情况下发现了这一点:

#!/bin/bash
python
filetosearch = '/home/ubuntu/ip_table.txt'
texttoreplace = 'tcp443'
texttoinsert = 'udp1194'

s = open(filetosearch).read()
s = s.replace(texttoreplace, texttoinsert)
f = open(filetosearch, 'w')
f.write(s)
f.close()
quit()

#15


0  

Now that this thread seems to have turned into replacing strings (of bytes) with any other language than bash, here is a stupid C implementation:

现在,这个线程似乎已经变成了用任何其他语言替换字符串(字节),这是一个愚蠢的C实现:

<!-- language: c -->

/**
 * Usage:
 *      ./replace "foobar" "foobaz" < input_file > output_file
 * Note: input_file and output_file should be different
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

typedef struct string_t {
    const char * value;
    size_t length;
} string;

struct parser_t {
    string match_text, replace_text;

    char * match_buffer;
    unsigned int match_buffer_index;

    enum { STATE_INVALID, STATE_IN, STATE_OUT } state;
};

void parser_init(struct parser_t * parser, 
                 const char * match_text,
                 const char * replace_text)
{
    memset(parser, 0, sizeof(struct parser_t));

    parser->match_text.value = match_text;
    parser->match_text.length = strlen(match_text);
    parser->replace_text.value = replace_text;
    parser->replace_text.length = strlen(replace_text);
    parser->state = STATE_OUT;
    parser->match_buffer = malloc(parser->match_text.length);
}

void parser_free(struct parser_t * parser)
{
    free(parser->match_buffer);
}

void output_char(char current_char)
{
    fwrite(&current_char, sizeof(char), 1, stdout);
}

void buffer_match(struct parser_t * parser, char current_char)
{
    parser->match_buffer[parser->match_buffer_index++] = current_char;
}

void buffer_clear(struct parser_t * parser)
{
    parser->match_buffer_index = 0;
}

void buffer_flush(struct parser_t * parser)
{
    if (parser->match_buffer_index > 0) {
        fwrite(parser->match_buffer, sizeof(char), parser->match_buffer_index, stdout);
        buffer_clear(parser);
    }
}

int process_state_in(struct parser_t * parser, char current_char)
{
    if (parser->match_text.value[parser->match_buffer_index] == current_char) {
        buffer_match(parser, current_char);

        return STATE_IN;
    }

    if (parser->match_buffer_index == parser->match_text.length) {
        fwrite(parser->replace_text.value, sizeof(char), parser->replace_text.length, stdout);
        buffer_clear(parser);

        output_char(current_char);

        return STATE_OUT;
    }

    if (parser->match_text.value[parser->match_buffer_index] != current_char) {
        buffer_flush(parser);
        output_char(current_char);

        return STATE_OUT;
    }

    return STATE_INVALID;
}

int process_state_out(struct parser_t * parser, char current_char)
{
    if (parser->match_text.value[parser->match_buffer_index] == current_char) {
        buffer_match(parser, current_char);

        return STATE_IN;
    } 

    if (parser->match_text.value[parser->match_buffer_index] != current_char) {
        buffer_flush(parser);
        output_char(current_char);

        return STATE_OUT;
    }

    return STATE_INVALID;
}

int main(int argc, char *argv[])
{
    char current_char;
    struct parser_t parser;

    if (argc != 3) {
        fprintf(stdout, "Usage:\n\t%s match_text replace_text < in_file > out_file\n\t# note in_file and out_file should be different.\n", argv[0]);
        return 0;
    }

    parser_init(&parser, argv[1], argv[2]);

    while (fread(&current_char, sizeof(char), 1, stdin) != 0) {
        switch (parser.state) {
            case STATE_IN:
            {
                parser.state = process_state_in(&parser, current_char);
            }
            break;
            case STATE_OUT:
            {
                parser.state = process_state_out(&parser, current_char);
            }
            break;
            default:
                fprintf(stderr, "Error: Invalid state.\n");
                return -1;
            break;
        }
    }

    parser_free(&parser);

    return 0;
}

Compile and run:

编译并运行:

$ cc replace.c -oreplace
$ ./replace "foobar" "foobaz" < input_file > output_file