如何用行号替换文本文件中的整行


152

我有一种情况,我希望bash脚本替换文件中的整行。行号始终相同,因此可以是硬编码的变量。

我不是要替换该行中的某些子字符串,而只是想用新行完全替换该行。

是否有执行此操作的bash方法(或可以放入.sh脚本的简单方法)。

Answers:


228

不是最大的,但这应该起作用:

sed -i 'Ns/.*/replacement-line/' file.txt

N应该在哪里替换为您的目标行号。这将替换原始文件中的行。要将更改后的文本保存在其他文件中,请删除以下-i选项:

sed 'Ns/.*/replacement-line/' file.txt > new_file.txt

1
有没有办法让sed修改有问题的文件,而不是转储到屏幕上?
user788171 2012年

8
对我来说,它说:sed: -e expression #1, char 26: unknown option to ``s'我的行是:sed -i '7s/.*/<param-value>http://localhost:8080/ASDF/services/REWS.REWSHttpSoap12Endpoint/</param-value>/' $TCE_SVN_HOME\trunk\tce\EWC\WebContent\WEB-INF\web.xml。任何想法?
Danijel

4
除非转义(),否则/替换文本中的每个将解释为s命令的斜杠\/。不过,最简单的方法是选择一个不同的未使用字符作为分隔符。例如,sed -i '7s{.*}{<param-value>http://...}' $TCE_SVN_HOME/trunk...
chepner 2013年

4
这种解释有点混乱,似乎没有用。如果您对sed及其分隔符设置功能有疑问,您可能需要看看以下内容:grymoire.com/Unix/Sed.html#uh-2它说,s确定分隔符后面的第一个字符。因此,例如,要更改您的命令以使用#它:sed -i '7s#.*#<param-value>http://localhost:8080/ASDF/services/REWS.REWSHttpSoap12Endpo‌​int/</param-value>#'
func0der 2015年

7
为了扩展@ meonlol,macOS也需要给出-eif -i。因此,正确的命令将变为:sed -e 'Ns/.*/replacement-line/' -i '' file.txt
ELLIOTTCABLE

44

我实际上使用此脚本来替换我们公司的UNIX服务器上的cron文件中的代码行。我们将其作为普通的shell脚本执行,没有任何问题:

#Create temporary file with new line in place
cat /dir/file | sed -e "s/the_original_line/the_new_line/" > /dir/temp_file
#Copy the new file over the original file
mv /dir/temp_file /dir/file

这不是按行号排列的,但是您可以通过将行号放在之前s/并将通配符替换为来轻松切换到基于行号的系统the_original_line


17
猫的无用使用:sed -e "s/.../.../" /dir/file > /dir/temp_file
chepner 2012年

24
可能对解释器/编译器没有用,但对人工阅读而言却没有用。@Kyle的代码从左到右很好看;由于动词位于名词之前,因此您使用IMO的习语没有。
克莱顿·斯坦利

1
有没有办法通过像那些两行合并,例如,sed -e "s/.../.../" /dir/file > /dir/file
Pubudu Dodangoda

22

假设您要用文本“ different”替换第4行。您可以像这样使用AWK:

awk '{ if (NR == 4) print "different"; else print $0}' input_file.txt > output_file.txt

AWK认为输入是分为“字段”的“记录”。默认情况下,一行是一条记录。 NR是看到的记录数。 $0代表当前的完整记录(而这$1是该记录的第一个字段,依此类推;默认情况下,这些字段是该行中的单词)。

因此,如果当前行号为4,则打印字符串“ different”,否则打印该行不变。

在AWK中,随附的程序代码{ }在每个输入记录上运行一次。

您需要用单引号引起来的AWK程序,以防止外壳程序尝试解释诸如之类的东西$0

编辑:在以下评论中,来自@chepner的更短,更优雅的AWK程序:

awk 'NR==4 {$0="different"} { print }' input_file.txt

仅对于记录(即第4行),将整个记录替换为字符串“ different”。然后对于每个输入记录,打印记录。

显然我的AWK技能很生锈!谢谢@chepner。

编辑:并从@Dennis Williamson看甚至更短的版本:

awk 'NR==4 {$0="different"} 1' input_file.txt

注释中说明了它的工作方式:1始终评估为true,因此关联的代码块始终运行。但是没有关联的代码块,这意味着AWK会执行其默认操作,即仅打印整行。AWK旨在允许这样的简洁程序。


3
短一点:awk 'NR==4 {$0="different"} { print }' input_file.txt
chepner 2012年

2
@chepner:简短一点:awk 'NR==4 {$0="different"}1' input_file.txt
暂停,直到另行通知。

@DennisWilliamson,我什至不知道它是如何工作的!那条尾随是1做什么的?(注意:我测试了它,它的确起作用了!我只是不明白如何。)
steveha 2012年

1
我想有一种方法可以简化无条件打印!@stevaha:1只是一个真实值,表示始终匹配的“模式”。匹配的默认操作是打印当前行。
chepner 2012年

19

给定此测试文件(test.txt)

Lorem ipsum dolor sit amet,
consectetur adipiscing elit. 
Duis eu diam non tortor laoreet 
bibendum vitae et tellus.

以下命令将第一行替换为“换行符”

$ sed '1 c\
> newline text' test.txt

结果:

newline text
consectetur adipiscing elit. 
Duis eu diam non tortor laoreet 
bibendum vitae et tellus.

更多信息可以在这里找到

http://www.thegeekstuff.com/2009/11/unix-sed-tutorial-append-insert-replace-and-count-file-lines/


2
这应该是正确的答案。c标志完全满足要求(用给定的文本替换编号的行)。
adelphus 2015年

sed的替代语法不会在stdout上回显文件:stackoverflow.com/a/13438118/4669135
Gabriel Devillers

这是正确的答案,但是为什么丑陋的行会中断? sed '1 cnewline text' test.txt也会做。
dev-null

7

在bash中,将N,M替换为行号,并将xxx yyy替换为所需的行号

i=1
while read line;do
  if((i==N));then
    echo 'xxx'
  elif((i==M));then
    echo 'yyy'
  else
    echo "$line"
  fi
  ((i++))
done  < orig-file > new-file

编辑

实际上,在此解决方案中存在一些问题,字符“ \ 0”,“ \ t”和“ \”

“ \ t”,可以通过在读取前将IFS =放在“ \”处来解决:-r

IFS= read -r line

但是对于“ \ 0”,该变量将被截断,纯bash中没有解决方案: 将包含空字符(\ 0)的字符串分配给Bash中的变量 但是在普通文本文件中没有nul字符\ 0

perl是一个更好的选择

perl -ne 'if($.==N){print"xxx\n"}elsif($.==M){print"yyy\n"}else{print}' < orig-file > new-file

我什至没有想到要尝试一个纯粹的BASH解决方案!
steveha 2012年

4
# Replace the line of the given line number with the given replacement in the given file.
function replace-line-in-file() {
    local file="$1"
    local line_num="$2"
    local replacement="$3"

    # Escape backslash, forward slash and ampersand for use as a sed replacement.
    replacement_escaped=$( echo "$replacement" | sed -e 's/[\/&]/\\&/g' )

    sed -i "${line_num}s/.*/$replacement_escaped/" "$file"
}

3

Chepner的出色回答。它在bash Shell中为我工作。

 # To update/replace the new line string value with the exiting line of the file
 MyFile=/tmp/ps_checkdb.flag

 `sed -i "${index}s/.*/${newLine}/" $MyFile`

此处
index-行号
newLine-我们要替换的新行字符串。


同样,下面的代码用于读取文件中的特定行。这不会影响实际文件。

LineString=`sed "$index!d" $MyFile` 

此处
!d-将删除第no行以外的行,$index 因此我们将$index在文件中将输出作为no的行字符串获得。


但是为什么结果只显示出来${newline}呢?
sikisis

1

在Mac上我用过

sed -i '' -e 's/text-on-line-to-be-changed.*/text-to-replace-the=whole-line/' file-name

-2

您甚至可以将参数传递给sed命令:

test.sh

#!/bin/bash
echo "-> start"
for i in $(seq 5); do
  # passing parameters to sed
  j=$(($i+3))
  sed -i "${j}s/.*/replaced by '$i'!/" output.dat
done
echo "-> finished"
exit 

原始输出。

a
b
c
d
e
f
g
h
i
j

执行./test.sh给出新的output.dat

a
b
c
replaced by '1'!
replaced by '2'!
replaced by '3'!
replaced by '4'!
replaced by '5'!
i
j

1
为什么在此示例中需要循环,只是混淆了解决方案?line=5; sed -i "${line}s/.*/replaced by '$i'!/" output.dat
罗布

你并不需要一个循环这个
chandu vutukuri
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.