Unix命令将文本添加到文件之前


124

是否有Unix命令将一些字符串数据添加到文本文件中?

就像是:

prepend "to be prepended" text.txt

您是在寻找单个命令还是小型脚本/别名命令?
莱文2012年

2
如果将所有答案组合在一起,这可能是最紧凑的方法:<<(echo "to be prepended") < text.txt | sponge text.txt
Sridhar Sarnobat '17

Answers:


110
sed -i.old '1s;^;to be prepended;' inFile
  • -i将更改写到位,如果有扩展名,则进行备份。(在这种情况下,.old
  • 1s;^;to be prepended;;用作命令定界符,用给定的替换字符串替换第一行的开头。

19
如果您可以在命令中添加一些说明,这对不熟悉sed的其他人会有所帮助。OP可能要\n在前置之后插入;根据他们的需求。干净的解决方案。
莱文

是的,一个解释会很好。inFile路径中可以包含目录吗?
zakdances 2013年

3
@Levon我完全想插入一个\n。应该先阅读评论。该死的。
chishaku

就我而言,我想在前置行的末尾使用分号,所以我选择了'1s;^;my-prepended-line\;\n;'
SamGamgee

5
请注意,“ \ n”仅适用于GNU sed,不适用于BSD(例如OSX,FreeBSD等)。要更方便地使用它们,请访问:stackoverflow.com/questions/1421478/…–
jwd

162
printf '%s\n%s\n' "to be prepended" "$(cat text.txt)" >text.txt

2
在一行中并在原始文本文件中!这是正确的简单答案。我建议添加新的换行\ n echo -e“
prepended

32
此解决方案有问题...它将出现的“ \ n”转换为实际的新行...如果您正在使用包含“ \ n”的字符串的代码文件,则会出现问题。
Paul Go

1
我在我的文件中有此文件,git config --get-regex $arg | sed -r 's/\t{3}/\\n/g';并且在将\tand 转换时将其弄乱了\n
2015年

8
这(1)将整个文件加载到内存中,(2)将整个文件粘贴到单个命令行参数中。对于简单的东西很好,但是要当心限制。
jwd

2
最后我尝试了一下,我相信这会在非常大的文件上中断,因为cat在覆盖之前无法读取text.txt的全部内容。如果echo的-e选项有问题,则可以在bash中引用换行符或执行echo "to be prepended"$'\n'"$(cat text.txt)"
jbo5112 '17

41

流程替代

我很惊讶没有人提到这一点。

cat <(echo "before") text.txt > newfile.txt

可以说,这比接受的答案更自然(打印某些内容并将其通过管道传递到替换命令中,按字典顺序是违反直觉的)。

...并劫持瑞安(Ryan)所说的内容,sponge您不需要临时文件:

sudo apt-get install moreutils
<<(echo "to be prepended") < text.txt | sponge text.txt

编辑:看起来这在Bourne Shell中不起作用 /bin/sh


此处为字符串(仅zsh)

使用here-string- <<<,您可以执行以下操作:

<<< "to be prepended" < text.txt | sponge text.txt

3
这个答案对我来说是赢家。简单且可通过内置函数实现。干得好!
PiersyP '17

1
@PiersyP我同意!这很有帮助,谢谢Sridhar-Sarnobat。

问题是有1个文件,而不是2个文件。因此,答案的第一行并没有真正起作用。最后一行可能有效(但需要海绵)。
VasiliNovikov

1
海绵做什么?
Hemanta '18年

1
<<(echo "to be prepended") < text.txt和的<<< "to be prepended" < text.txt建筑无法正常使用;他们需要zsh。
安德斯·卡塞格

31

这是一种可能性:

(echo "to be prepended"; cat text.txt) > newfile.txt

您可能不会轻易绕过中间文件。

替代方案(如果要转义,可能会很麻烦):

sed -i '0,/^/s//to be prepended/' text.txt

我认为策略1)是这里所有答案中最直观的。并有轻微的变化:cat <(echo "to be prepended") text.txt > newfile.txt 。想一想,我不确定我的与我有什么关系,所以我张贴了一个单独的答案。
Sridhar Sarnobat

1
第一种方法不会保留文件权限
Xlsx

保留权限的唯一方法是使用海绵
Sridhar Sarnobat

19

这将形成输出。-表示标准输入,它是通过管道从回声中提供的。

echo -e "to be prepended \n another line" | cat - text.txt

要重写该文件,需要一个临时文件,因为它不能通过管道返回到输入文件中。

echo "to be prepended" | cat - text.txt > text.txt.tmp
mv text.txt.tmp text.txt

2
虽然不会text.txt按要求将文本添加到文件中,但是会在stdout上显示它。输出可以漏斗到不同的文件,如果该工程为OP
捷尔

1
这肯定会打印“待添加”,然后打印“ text.txt”的内容。但我希望文本放在文件的开头
一二三2012年

1
如果我有多行文字怎么办?如何在其中放置换行符?
一二三

这会很酷,但是bash不喜欢它:$ echo RewriteBase / | cat-web / .htaccess> web / .htaccess cat:web / .htaccess:输入文件为输出文件
geek-merlin

14

喜欢亚当的答案

我们可以使使用海绵更容易。现在我们不需要创建临时文件并通过以下方式重命名

echo -e "to be prepended \n another line" | cat - text.txt | sponge text.txt

这是唯一为我工作的解决方案。有趣的是,我的服务器名称是海绵宝宝。
奥马尔的

9

如果可以替换输入文件:

注意:

  • 这样做可能会带来意想不到的副作用,特别是可能用常规文件替换符号链接,最终对文件具有不同的权限,并更改文件的创建(出生)日期。

  • sed -i,就像约翰·卫斯理王子的回答一样,试图至少恢复原始权限,但其他限制同样适用。

这是一个使用临时文件简单选择(避免像shime的解决方案那样将整个输入文件读入内存):

{ printf 'to be prepended'; cat text.txt; } > tmp.txt && mv tmp.txt text.txt

如使用0xC0000022L的解决方案一样,使用命令({ ...; ...; })比使用子shell(...; ...))效率更高。

优点是:

  • 很容易控制是将新文本直接添加到第一行还是将其插入为新行(只需附加\nprintf参数中)。

  • sed解决方案不同,它在输入文件为空(0字节)的情况下有效。


如果意图是在一条或多条整行之前添加,则可以简化该sed解决方案在现有内容的(假设输入文件为非空),:

sedi函数插入整行:

使用GNU sed

# Prepends 'to be prepended' *followed by a newline*, i.e. inserts a new line.
# To prepend multiple lines, use '\n' as part of the text.
# -i.old creates a backup of the input file with extension '.old'
sed -i.old '1 i\to be prepended' inFile

一个可移植的变体也可以与macOS / BSD一起使用sed

# Prepends 'to be prepended' *followed by a newline*
# To prepend multiple lines, escape the ends of intermediate
# lines with '\'
sed -i.old -e '1 i\
to be prepended' inFile

请注意,后面的文字换行符\是必需的。


如果必须在适当位置编辑输入文件(保留其所有属性的索引节点):

使用古老的edPOSIX实用程序

注意:

  • ed总是读取输入文件作为一个整体的第一到存储器中。

直接放在第一行之前(与一样sed,如果输入文件完全为空(0字节),这将不起作用):

ed -s text.txt <<EOF
1 s/^/to be prepended/
w
EOF
  • -s被抑制ed的状态消息。
  • 注意如何将命令ed作为多行here-document<<EOF\n...\nEOF)提供,即通过stdin;通过默认字符串膨胀在这样的文档(shell变量被内插)来进行; 引用开头的定界符以取消该限制(例如<<'EOF')。
  • 1 使第一行成为当前行
  • 函数s在当前行上执行基于正则表达式的字符串替换,如sed;您可以在替换文本中包含文字换行符,但是它们必须是\换行。
  • w将结果写回到输入文件(用于测试,代替w,p打印的结果,而无需修改所述输入文件)。

在前面加上一个或多个整行

与一样sed,该i函数始终将尾随换行符添加到要插入的文本中。

ed -s text.txt <<EOF
0 i
line 1
line 2
.
w
EOF
  • 0 i使0(文件的开头)为当前行并开始插入模式(i);请注意,否则行号1基于。
  • 以下各行是要插入到当前行之前的文本,.以其自己的行终止。

7

可能没有内置的内容,但是您可以很轻松地编写自己的代码,如下所示:

#!/bin/bash
echo -n "$1" > /tmp/tmpfile.$$
cat "$2" >> /tmp/tmpfile.$$
mv /tmp/tmpfile.$$ "$2"

至少像这样...


6
+1表示将$$(当前PID)用作临时文件名的一部分。如果创建通用脚本,这将防止另一个用户通过同时运行相同脚本来覆盖临时文件。
E布朗

3
但是,使用$$生产代码不足(谷歌符号链接攻击);但它肯定比静态文件名好。
2012年

1
只需使用mktemp
mewa

7

在某些情况下,前置文本仅可从stdin获得。然后,此组合将起作用。

echo "to be prepended" | cat - text.txt | tee text.txt

如果要忽略tee输出,请追加> /dev/null


我真的很喜欢这个答案。这是一种非常干净明确的方法。使用tee是解决将目录输出到输入文件的问题的自然解决方案。也没有重命名的技巧。比目前投票最多的解决方案更好,因为它不会创建新文件。从本质上讲,这是最接近>>而不是追加的>>。
DC2

这是最好,最干净的答案。
nerdoc

6
是否有保证在阅读之前tee不会造成麻烦?我认为不是—这会使该解决方案对文件的健康造成危险。text.txtcat
乔纳森·莱夫勒

3
@JonathanLeffler可能没有。我尝试了以下代码进行测试:lenA=1000000; yes a | head -c $lenA > a.txt; lenB=10000; b=$(yes b | head -c $lenB); echo "$b" | cat - a.txt | tee a.txt > /dev/null。如果lenA为1000000(文件为1Mb)且lenB为10000(文件为10Kb),则文件“ a.txt”将被20kb的“ b”字母覆盖。这完全被打破了。现在,如果使用1Mb a.txt和1Mb文本作为前缀,tee则进入生成7Gb +文件的循环,我必须停止该命令。因此,很明显,对于大尺寸而言,结果是不可预测的。我不知道它是否应该在小尺寸上工作。
VasiliNovikov

3
从概念上讲:如果输入文件碰巧大于系统的管道缓冲区大小(当前通常为64 KB),此命令将导致数据丢失
mklement0

7

解:

printf '%s\n%s' 'text to prepend' "$(cat file.txt)" > file.txt

请注意,这对所有类型的输入都是安全的,因为没有扩展。例如,如果您想添加!@#$%^&*()ugly text\n\t\n,它将可以正常工作:

printf '%s\n%s' '!@#$%^&*()ugly text\n\t\n' "$(cat file.txt)" > file.txt

需要考虑的最后一部分是命令替换期间文件末尾的空格删除"$(cat file.txt)"。为此的所有变通办法都相对复杂。如果要在file.txt的末尾保留换行符,请参见:https : //stackoverflow.com/a/22607352/1091436


爱它。超级可移植,不需要生成新文件。谢谢!
pyb

1
唯一的问题是,如果的内容file.txt大于可容纳到参数列表中的内容printf
乔纳森·勒夫勒

6

另一种使用方式sed

sed -i.old '1 {i to be prepended
}' inFile

如果要添加的行是多行:

sed -i.old '1 {i\ 
to be prepended\
multiline
}' inFile

为什么不sed -i '1ito be prepended' inFile-或者只允许GNU sed使用呢?
用户未知

@userunknown:在standard中sedi命令后跟反斜杠和换行符,除最后一行外,输入的每一行也以反斜杠结尾。GNU sed允许您按照您的要求sed行事,但只有GNU 可以这样做。
乔纳森·勒夫勒

3

如在Bash(在Ubuntu中)中所测试的,如果通过来启动测试文件;

echo "Original Line" > test_file.txt

您可以执行;

echo "$(echo "New Line"; cat test_file.txt)" > test_file.txt

或者,如果bash的版本对于$()而言过旧,则可以使用反引号;

echo "`echo "New Line"; cat test_file.txt`" > test_file.txt

并接收“ test_file.txt”的以下内容;

New Line
Original Line

没有中间文件,只有bash / echo。


2
最佳答案,我使用以下答案编写了这种单行格式以旋转文件:对于$(<file2.txt)中的i;做echo“ $(echo $ i; cat file.txt)”> file.txt; 完成
奥兰则布

2

另一个相当简单的解决方案是:

    $ echo -e "string\n" $(cat file)

这对我有用...只是稍作修改,为了维护输入文件中的多行,您必须将其用引号引起来,如下所示:echo -e“ string \ n”“ $(cat〜/ file.txt) “>〜/ file.txt;
克雷格·韦恩

2
不建议cat在双引号中使用参数扩展,这是降低投票率的一个很好的理由。此代码将文件的内容拆分为多个单词,并将每个单词作为单独的参数传递给echo。您失去了原始参数边界,失去了换行符/制表符/等,以及诸如\t和的字符串\n在原文与选项卡和换行,而不是因为他们是被保留取代。
查尔斯·达菲


0
# create a file with content..
echo foo > /tmp/foo
# prepend a line containing "jim" to the file
sed -i "1s/^/jim\n/" /tmp/foo
# verify the content of the file has the new line prepened to it
cat /tmp/foo

0

我建议定义一个函数,然后在需要的地方导入和使用它。

prepend_to_file() { 
    file=$1
    text=$2

    if ! [[ -f $file ]] then
        touch $file
    fi

    echo "$text" | cat - $file > $file.new

    mv -f $file.new $file
}

然后像这样使用它:

prepend_to_file test.txt "This is first"
prepend_to_file test.txt "This is second"

您的文件内容将为:

This is second
This is first

我将使用这种方法来实现变更日志更新程序。

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.