将文本添加到文件的 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 命令 ( ...; ...;
) 比使用 subshell ((...; ...)
) 稍微高效一些,例如 0xC0000022L's solution。
优点是:
很容易控制新文本是否应直接添加到第一行或是否应作为新行插入(只需将\n
附加到printf
参数)。
与sed
解决方案不同,它在输入文件为空(0
字节)时工作。
sed
solution 可以简化,如果其意图是在现有内容前添加一个或多个整行(假设输入文件非空):
sed
的i
函数插入整行:
使用 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 (<<EOF\n...\nEOF
) 提供给ed
的,即通过stdin;默认情况下,字符串扩展 is 在此类文档中执行(插入 shell 变量); 引用开头的分隔符来抑制它(例如,<<'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
输出,则追加> /dev/null
。
【讨论】:
我真的很喜欢这个答案。这是一种非常干净和清晰的方法。使用 tee 是尝试将输出 cat 输出到输入文件的问题的自然解决方案。也没有重命名的技巧。比目前投票率最高的解决方案要好,因为它不会创建新文件。这基本上是您最接近的 >> 前置而不是附加。 是否可以保证tee
在cat
阅读之前不会破坏text.txt
?我认为不会——这会使这个解决方案对文件的健康造成危险。
我也喜欢这个答案的简单性。它适用于 Bourne shell,这对我的回答不正确
@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+ 文件的循环,我不得不停止该命令。因此,很明显,对于大尺寸,结果是不可预测的。我不知道它是否适用于小尺寸。
从概念上讲:如果输入文件碰巧大于系统的管道缓冲区大小(现在通常为 64 KB),此命令将导致数据丢失 .【参考方案3】:
% echo blaha > blaha
% echo fizz > fizz
% cat blaha fizz > buzz
% cat buzz
blaha
fizz
【讨论】:
【参考方案4】:对于希望附加一行或多行文本(带有变量甚至是 subshell 代码)并保持其可读性和格式化的未来读者,您可能会喜欢这个:
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)
使用这里的字符串 - <<<
,你可以这样做:
<<< "to be prepended" < text.txt | sponge text.txt
【讨论】:
这个答案是我的赢家。仅使用内置函数简单且可实现。干得好! @PiersyP 我同意!这很有帮助,谢谢 Sridhar-Sarnobat。 问题是有1个文件,不是2个文件。所以答案的第一行并没有真正起作用。最后一行可能有效(但需要海绵)。 海绵有什么作用? 您的<<(echo "to be prepended") < text.txt
和 <<< "to be prepended" < text.txt
结构在 bash 中不起作用;他们需要 zsh。【参考方案7】:
如在 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。
【讨论】:
最佳答案,我用这个答案写了这个单行来旋转我的文件:for i in $(sed -i.old '1s;^;to be prepended;' inFile
-i
将更改写入原处,并在提供任何扩展名时进行备份。 (在这种情况下,.old
)
1s;^;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
请注意,这对所有类型的输入都是安全的,因为没有扩展。例如,如果你想在前面加上!@#$%^&*()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 <(echo "to be prepended") text.txt > 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的话