从 Bash 命令在文本文件中查找和替换

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/525592/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 17:55:48  来源:igfitidea点击:

Find and Replace Inside a Text File from a Bash Command

bashsshreplacescriptingironpython

提问by Ash

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

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

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 做到这一点,实现我的目标的最简单的(一行)脚本是什么?

回答by johnny

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

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

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

Which will invoke sed to do an in-place edit due to the -ioption. 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,}

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.

这会遍历每一行,进行替换并写入临时文件(不想破坏输入)。最后的移动只是暂时移动到原始名称。

回答by Alnitak

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 -iflag tells it to do an in-place replacement.

-i标志告诉它进行就地替换。

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

有关man perlrun更多详细信息,包括如何备份原始文件,请参见。

回答by rayro

I was surprised when I stumbled over this...

当我偶然发现这个时,我很惊讶......

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

软件包replace附带了一个命令"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 replacefor more on this.

有关man replace更多信息,请参阅。

回答by zcourts

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

回答by slim

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 的“:”提示符下给它同样的命令。

回答by centurian

I found this thread among others and I agree it contains the most complete answers so I'm adding mine too:

我在其他帖子中找到了这个帖子,我同意它包含最完整的答案,所以我也添加了我的:

  1. sedand edare so useful...by hand. Look at this code from @Johnny:

    sed -i -e 's/abc/XYZ/g' /tmp/file.txt
    
  2. When my restriction is to use it in a shell script, no variable can be used inside in place of "abc" or "XYZ". The BashFAQseems to agree with what I understand at least. So, I can't use:

    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:

    #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 nullglobis set then, it behaves strangely when there is a string containing a *as in:

    <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.

  4. This kind of parsing should be slower than one-hit search and replace but, as you already saw, there are four variables for four different search patterns working out of one parse cycle.

  1. sed并且ed是如此有用...手。看看@Johnny 的这段代码:

    sed -i -e 's/abc/XYZ/g' /tmp/file.txt
    
  2. 当我的限制是在 shell 脚本中使用它时,不能在内部使用变量来代替“abc”或“XYZ”。该BashFAQ似乎与我至少明白同意。所以,我不能使用:

    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
    

    但是,我们能做什么?因为,@Johnny 说使用while read...但是,不幸的是这不是故事的结尾。以下对我来说效果很好:

    #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. 如您所见,如果nullglob设置了,当有一个包含 a 的字符串时,它的行为会很奇怪*

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

    变成

    <VirtualHost ServerName www.example.com
    

    没有结束尖括号,Apache2 甚至无法加载。

  4. 这种解析应该比一键式搜索和替换慢,但是,正如您已经看到的,在一个解析周期内有四种不同搜索模式的四个变量。

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

根据给定的问题假设,我能想到的最合适的解决方案。

回答by MMParvin

You can use sed:

您可以使用sed

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

Use -ifor "ignore case" if you are not sure text to find is abcor ABCor AbC, etc.

-i如果您不确定要查找的文本是abcorABCAbC等,请用于“忽略大小写” 。

You can use findand sedif you don't know your filename:

你可以用findsed,如果你不知道你的文件名:

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' {} \;

回答by MMParvin

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

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

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

See more in "Editing files via scripts with ed".

请参阅“使用脚本通过脚本编辑文件ed”中的更多信息。

回答by johnraff

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

回答by Linux4you

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