将文本添加到文件的 Unix 命令

Posted

技术标签:

【中文标题】将文本添加到文件的 Unix 命令【英文标题】:Unix command to prepend text to a file 【发布时间】:2022-01-12 19:22:37 【问题描述】:

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

类似:

prepend "to be prepended" text.txt

【问题讨论】:

您是在寻找单个命令,还是小脚本/别名命令可以? 如果你结合所有的答案,这可能是最紧凑的方式:<<(echo "to be prepended") < text.txt | sponge text.txt 这个问题真的激发了很多人的创造力! 【参考方案1】:

如果可以替换输入文件:

注意:

这样做可能会产生意想不到的副作用,特别是可能会用常规文件替换符号链接、最终获得文件的不同权限以及更改文件的创建(出生)日期。

sed -i 与Prince John Wesley's answer 一样,会尝试至少恢复原始权限,但其他限制也适用。

这是一个使用临时文件的简单替代方案(它避免像shime's solution 那样将整个输入文件读入内存):

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

使用 group 命令 ( ...; ...; ) 比使用 subshel​​l ((...; ...)) 稍微高效一些,例如 0xC0000022L's solution。

优点是:

很容易控制新文本是否应直接添加到第一行或是否应作为新行插入(只需将\n 附加到printf 参数)。

sed 解决方案不同,它在输入文件为空(0 字节)时工作。


sed solution 可以简化,如果其意图是在现有内容前添加一个或多个整行(假设输入文件非空):

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

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


如果输入文件必须在原地编辑(保留其所有属性的inode):

使用可敬的ed POSIX utility:

注意:

ed 总是首先将输入文件作为一个整体读入内存。

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

ed -s text.txt <<EOF
1 s/^/to be prepended/
w
EOF
-s 抑制了 ed 的状态消息。 注意命令是如何作为多行here-document (&lt;&lt;EOF\n...\nEOF) 提供给ed 的,即通过stdin;默认情况下,字符串扩展 is 在此类文档中执行(插入 shell 变量); 引用开头的分隔符来抑制它(例如,&lt;&lt;'EOF')。 1 将第一行设为当前行 函数s对当前行执行基于正则表达式的字符串替换,如sed;您可以在替换文本中包含 literal 换行符,但它们必须是 \-escaped。 w 将结果写回输入文件(为了测试,将w 替换为,p 以仅打印结果,而不修改输入文件)。

添加一个或多个整行

sed 一样,i 函数总是在要插入的文本中添加尾随换行符。

ed -s text.txt <<EOF
0 i
line 1
line 2
.
w
EOF
0 i 使0(文件的开头)成为当前行并开始插入模式(i);请注意,行号是基于 1 的。 以下几行是插入之前当前行的文本,以. 结束。

【讨论】:

【参考方案2】:

编者注:

如果输入文件碰巧大于系统的管道缓冲区大小(现在通常为 64 KB),此命令将导致数据丢失。有关详细信息,请参阅 cmets。

在某些情况下,前置文本可能只能从标准输入获得。 那么这个组合就可以了。

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

如果您想省略tee 输出,则追加&gt; /dev/null

【讨论】:

我真的很喜欢这个答案。这是一种非常干净和清晰的方法。使用 tee 是尝试将输出 cat 输出到输入文件的问题的自然解决方案。也没有重命名的技巧。比目前投票率最高的解决方案要好,因为它不会创建新文件。这基本上是您最接近的 >> 前置而不是附加。 是否可以保证teecat 阅读之前不会破坏text.txt?我认为不会——这会使这个解决方案对文件的健康造成危险。 我也喜欢这个答案的简单性。它适用于 Bourne shell,这对我的回答不正确 @JonathanLeffler 可能没有。我试过这段代码来测试:lenA=1000000; yes a | head -c $lenA &gt; a.txt; lenB=10000; b=$(yes b | head -c $lenB); echo "$b" | cat - a.txt | tee a.txt &gt; /dev/null。如果 lenA 是 1000000(1Mb 文件)并且 lenB 是 10000(10Kb 文本前置),则文件“a.txt”会被 20Kb 的“b”字母覆盖。这完全被打破了。现在,如果您使用 1Mb a.txt 和 1Mb 文本作为前缀,tee 进入生成 7Gb+ 文件的循环,我不得不停止该命令。因此,很明显,对于大尺寸,结果是不可预测的。我不知道它是否适用于小尺寸。 从概念上讲:如果输入文件碰巧大于系统的管道缓冲区大小(现在通常为 64 KB),此命令将导致数据丢失 .【参考方案3】:
% echo blaha > blaha
% echo fizz > fizz
% cat blaha fizz > buzz
% cat buzz 
blaha
fizz

【讨论】:

【参考方案4】:

对于希望附加一行或多行文本(带有变量甚至是 subshel​​l 代码)并保持其可读性和格式化的未来读者,您可能会喜欢这个:

echo "Lonely string" > my-file.txt

然后运行

cat <<EOF > my-file.txt
Hello, there!

$(cat my-file.txt)
EOF

cat my-file.txt的结果:

Hello, there!

Lonely string

这是有效的,因为my-file.txt 的读取首先发生在子shell 中。我一直使用这个技巧将重要的规则附加到 Docker 容器中的配置文件中,而不是复制整个配置文件。

【讨论】:

我最喜欢的答案【参考方案5】:

你可以用 awk 轻松做到这一点

cat text.txt|awk 'print "to be prepended"$0'

【讨论】:

【参考方案6】:

流程替换

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

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)

使用这里的字符串 - &lt;&lt;&lt;,你可以这样做:

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

【讨论】:

这个答案是我的赢家。仅使用内置函数简单且可实现。干得好! @PiersyP 我同意!这很有帮助,谢谢 Sridhar-Sarnobat。 问题是有1个文件,不是2个文件。所以答案的第一行并没有真正起作用。最后一行可能有效(但需要海绵)。 海绵有什么作用? 您的 &lt;&lt;(echo "to be prepended") &lt; text.txt&lt;&lt;&lt; "to be prepended" &lt; text.txt 结构在 bash 中不起作用;他们需要 zsh。【参考方案7】:

如在 Bash(在 Ubuntu 中)中测试过,如果从测试文件通过;

echo "Original Line" &gt; test_file.txt

你可以执行;

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

或者,如果bash的版本对于$()来说太旧了,你可以使用反引号;

echo "`echo "New Line"; cat test_file.txt`" &gt; test_file.txt

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

New Line
Original Line

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

【讨论】:

最佳答案,我用这个答案写了这个单行来旋转我的文件:for i in $( file.txt;完成; 最佳答案。您可能想使用 '>|'运算符(这不是管道...),即使外壳设置了“noclobber”,也可以覆盖文本文件。所以: echo "$(echo "New Line"; cat test_file.txt)" >| test_file.txt【参考方案8】:
sed -i.old '1s;^;to be prepended;' inFile
-i 将更改写入原处,并在提供任何扩展名时进行备份。 (在这种情况下,.old1s;^;to be prepended; 将第一行的开头替换为给定的替换字符串,使用 ; 作为命令分隔符。

【讨论】:

如果您可以在命令中添加一些解释,这将对不太熟悉 sed 工作原理的其他人有所帮助。 OP 可能希望在前置后插入\n;取决于他们的需要。整洁的解决方案。 是的,一个解释会很棒。 inFile 可以在其路径中包含目录吗? @Levon 我完全想插入一个\n。应该先阅读cmets。该死的。 在我的情况下,我想在前置行的末尾有一个分号,所以我选择了'1s;^;my-prepended-line\;\n;' 请注意,'\n' 仅适用于 GNU sed,不适用于 BSD(例如 OSX、FreeBSD 等)。为了更便于携带,请参阅:***.com/questions/1421478/…【参考方案9】:

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

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

我将使用这种方法来实现更改日志更新器。

【讨论】:

【参考方案10】:
printf '%s\n%s\n' "to be prepended" "$(cat text.txt)" >text.txt

【讨论】:

在一行中,在原始文本文件中!这是正确的简单答案。我建议附加新行 \n echo -e "to be prepended\n$(cat text.txt)" > text.txt 这个解决方案有问题......它将出现的“\n”转换为实际的新行......如果您在代码文件中添加包含“\n”的字符串,则会出现问题。 我在我的文件 git config --get-regex $arg | sed -r 's/\t3/\\n/g'; 中有这个,当它转换 \t\n 时,这搞砸了。 这 (1) 将整个文件加载到内存中,(2) 将整个文件粘贴到单个命令行参数中。适合简单的东西,但要注意限制。 上次我尝试过,我相信这会破坏非常大的文件,其中 cat 在覆盖之前无法读取 text.txt 的全部内容。如果 echo 上的 -e 选项有问题,您可以在 bash 中引用换行符或执行 echo "to be prepended"$'\n'"$(cat text.txt)"【参考方案11】:

解决方案:

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

请注意,这对所有类型的输入都是安全的,因为没有扩展。例如,如果你想在前面加上!@#$%^&amp;*()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://***.com/a/22607352/1091436

【讨论】:

爱它。超级便携,不需要生成新文件。谢谢! 唯一的问题是file.txt 的内容大于printf 的参数列表。【参考方案12】:

更喜欢亚当的answer

我们可以更轻松地使用sponge。现在我们不需要创建一个临时文件并将其重命名为

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

【讨论】:

这是唯一适合我的解决方案。有趣的是我的服务器的名字是海绵宝宝。【参考方案13】:

另一个相当直接的解决方案是:

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

【讨论】:

这对我有用...只是稍作修改,为了维护输入文件中的多行,您必须将其用引号括起来,如下所示: echo -e "string\n" "$ (cat ~/file.txt)" > ~/file.txt; 在双引号中没有 cat 参数扩展是 downvote 的一个很好的理由。此代码将文件内容拆分为单词,并将每个单词作为单独的参数传递给echo。您丢失了原始参数边界,丢失了换行符/制表符/等,并且原始文本中的 \t\n 等字符串被制表符和换行符替换,而不是保持原样。【参考方案14】:
# 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

【讨论】:

【参考方案15】:

如果你喜欢 vi/vim,这可能更符合你的风格。

printf '0i\n%s\n.\nwq\n' prepend-text | ed file

【讨论】:

【参考方案16】:

使用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:在标准sed 中,i 命令后跟一个反斜杠和一个换行符,并且除了最后一行之外的每一行输入也以反斜杠结尾。 GNU sed 允许按照您询问的方式使用速记,但只有 GNU sed 可以这样做。'【参考方案17】:

这将用于形成输出。 - 表示标准输入,通过管道从 echo 提供。

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

【讨论】:

这不会根据要求将文本添加到文件 text.txt 中,而是将其显示在标准输出上。如果适用于 OP,则输出可以汇集到不同的文件中 这肯定会打印“要添加”,然后是“text.txt”的内容。但我希望将文本添加到文件中 如果我有多行文本怎么办?如何在其中放置换行符? 这很酷,但 bash 不喜欢它: $ echo RewriteBase / | cat - web/.htaccess > web/.htaccess cat: web/.htaccess: 输入文件是输出文件【参考方案18】:

这是一种可能性:

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

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

替代方案(外壳转义可能很麻烦):

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

【讨论】:

我觉得 Strategy 1) 是这里所有答案中最直观的。还有一点变化:cat &lt;(echo "to be prepended") text.txt &gt; newfile.txt 。想一想,我不确定我的是否相关,所以我发布了一个单独的答案。 第一种方法不会保留文件权限 保留权限的唯一方法是使用海绵 不需要使用( ... ) 生成子shell:shell 完全可以重定向组命令 ... 的输出。警告:shell 语法要求在 之前使用终止分号或换行符,但在 ) 之前不需要。【参考方案19】:

可能没有任何内置功能,但您可以很容易地自己编写,如下所示:

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

至少是这样的……

【讨论】:

+1 用于使用 $$(当前 PID)作为临时文件名的一部分。如果创建一般用途的脚本,这将防止其他用户通过同时运行相同的脚本来覆盖临时文件。 使用$$ 不足以用于生产代码,但是(谷歌符号链接攻击);但它肯定比静态文件名好。 只要使用mktemp

以上是关于将文本添加到文件的 Unix 命令的主要内容,如果未能解决你的问题,请参考以下文章

unix sed怎么给每行前添加字符“/” 如果添加HEAD的话正常,但是要添加的文本是URL的话

是否有 Unix 实用程序可以将时间戳添加到标准输入?

如何在unix中的每一行末尾添加文本

如何将一行文本添加到目录中的多个文件

UNIX API调用:使用read()函数打开文件并将其打印到屏幕会添加额外的随机字符

如何在 Unix 上包含 «pty.h» 头文件/将 «pty.h» 添加到标准库 C / C++