是否有Unix命令将一些字符串数据添加到文本文件中?
就像是:
1
| prepend"to be prepended" text.txt |
-
你在寻找单个命令,还是一个小脚本/别名命令?
-
如果将所有答案组合在一起,这可能是最紧凑的方式:<<(echo"to be prepended") < text.txt | sponge text.txt
1 2 3
| printf '%s
%s
'"to be prepended""$(cat text.txt)">text.txt |
-
这应该是公认的答案。
-
在一行和原始文本文件中!这是正确的简单答案。我建议额外的新行 n echo -e"要预先添加 n $(cat text.txt)"> text.txt
-
这个解决方案有问题...它将" n"的出现转换为实际的新行...如果您在字符串包含" n"的代码文件前面,则会出现问题。
-
@FernandoSilveira是的。也许这是机器特定的事情。我在OS X上。
-
我在我的文件git config --get-regex $arg | sed -r 's/\t{3}/\
/g';中有这个,这会因为它转换\t和
而混乱。
-
这(1)将整个文件加载到内存中,(2)将整个文件粘贴到一个命令行参数中。很简单的东西,但要注意限制。
-
最后我试过,我相信这会破坏真正的大文件,cat在覆盖之前无法读取text.txt的全部内容。如果echo上的-e选项有问题,可以在bash中引用换行符或执行echo"to be prepended"$'
'"$(cat text.txt)"
-
@Wolverine我写了一个类似的解决方案,在所有类型的输入上都是正确的:stackoverflow.com/a/47399735/1091436(它没有扩展/解释)
-
@shime,你会接受修复错误的编辑吗? printf '%s
%s
'"to be prepended""$(cat text.txt)">text.txt会避免echo -e更不幸的副作用。 (它仍然不是特别好的做法 - 取决于shell在重定向之前执行命令替换 - 但至少在修复时它不会在一般情况下使用以通常方式运行的shell进行不希望的修改)。
-
@CharlesDuffy是的,我会接受修正这些错误的编辑。
1
| sed -i.old '1s;^;to be prepended;' inFile |
-
-i将更改写入到位,并在给出任何扩展名时进行备份。 (在这种情况下,.old)
-
1s;^;to be prepended;使用;作为命令分隔符,用给定的替换字符串替换第一行的开头。
-
如果你能用命令添加一些解释,那么对那些不熟悉sed工作的人会有所帮助。 OP可能希望在前置后插入
;取决于他们的需求。整洁的解决方案。
-
是的,解释会很棒。 inFile可以在其路径中包含目录吗?
-
@Levon我完全想插入
。应该先阅读评论。该死的。
-
在我的情况下,我想在前面的行的末尾有一个分号,所以我选择'1s;^;my-prepended-line\;
;'
-
请注意,' n'仅适用于GNU sed,而不适用于BSD(例如OSX,FreeBSD等)。要更方便,请参阅:stackoverflow.com/questions/1421478/…
-
您的分隔符不是很直观。但是正确答案
流程替代
我很惊讶没有人提到这一点。
1
| cat <(echo"before") text.txt > newfile.txt |
这可以说比接受的答案更自然(打印一些东西并将其输入到替换命令中,这是字典上反直觉的)。
...并且劫持了上面所说的ryan,用sponge你不需要临时文件:
1 2
| sudo apt-get install moreutils
<<(echo"to be prepended") < text.txt | sponge text.txt |
编辑:看起来这在Bourne Shell /bin/sh中不起作用
这里是String
使用here-string - <<<(再次,你需要bash),你可以这样做:
1
| <<<"to be prepended" < text.txt | sponge text.txt |
-
这个答案对我来说是胜利者。仅使用内置版即可轻松实现。干得好!
-
@PiersyP我同意!这有很大帮助,谢谢你Sridhar-Sarnobat。
-
问题是有1个文件,而不是2个文件。所以答案的第一行并没有真正起作用。最后一行可能有效(但需要海绵)。
-
是的(虽然这不是我的答案特别的问题,这是shell的不便)。如果它有助于澄清如何使用它,请随时更新我的??答案。
-
海绵做什么?
-
它可以防止输入文件在读取和写入时被擦除。
这是一种可能性:
1
| (echo"to be prepended"; cat text.txt) > newfile.txt |
你可能不会轻易绕过中间文件。
替代方案(shell逃逸可能很麻烦):
1
| sed -i '0,/^/s//to be prepended/' text.txt |
-
战略1)是这里任何答案中最直观的,我觉得。并略有变化:cat <(echo"to be prepended") text.txt > newfile.txt 。想想看,我不确定我的相关,所以我发布了一个单独的答案。
-
第一种方法不会保留文件权限
这将有助于形成输出。 - 表示标准输入,通过回声管道提供。
1 2
| echo -e"to be prepended
another line" | cat - text.txt |
要重写文件,需要临时文件,因为无法管道输入到输入文件中。
1 2
| echo"to be prepended" | cat - text.txt > text.txt.tmp
mv text.txt.tmp text.txt |
-
这不会根据请求将文本添加到文件text.txt,但会在stdout上显示。如果适用于OP,则输出可以汇集到不同的文件中
-
这肯定会打印"要预先",然后打印"text.txt"的内容。但我希望将文本添加到文件中
-
如果我有多行文字怎么办?如何将换行符放在哪里?
-
@Levon谢谢。我已经更新了我的答案。
-
这会很酷,但bash不喜欢它:$ echo RewriteBase / | cat - web / .htaccess> web / .htaccess cat:web / .htaccess:输入文件是输出文件
更喜欢亚当的回答
我们可以更容易地使用海绵。现在我们不需要创建临时文件并将其重命名
1 2
| echo -e"to be prepended
another line" | cat - text.txt | sponge text.txt |
-
这是唯一适合我的解决方案。有趣的是,我的服务器名称是海绵宝宝。
在某些情况下,前置文本只能从stdin获得。
那么这种组合应该有效。
1
| echo"to be prepended" | cat - text.txt | tee text.txt |
如果要省略tee输出,则追加> /dev/null。
-
我真的很喜欢这个答案。这是一个非常干净和清晰的方法。使用tee是尝试将cat输出到输入文件的问题的自然解决方案。也没有重命名的黑客。比现在最高的投票解决方案更好,因为它不会创建新文件。这基本上是你最接近>>的前置而不是追加。
-
这是最好,最干净的答案。
-
在cat读取它之前,是否保证tee不会破坏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 +文件的循环,我必须停止命令。因此,很明显,对于大尺寸,结果是不可预测的。我没有关于它是否适用于小尺寸的信息。
可能没有任何内置功能,但您可以轻松编写自己的内容,如下所示:
1 2 3 4
| #!/bin/bash
echo -n"$1"> /tmp/tmpfile.$$
cat"$2">> /tmp/tmpfile.$$
mv /tmp/tmpfile.$$"$2" |
这样的东西至少......
-
使用$$(当前PID)作为临时文件名的一部分+1。如果创建一般用途的脚本,这将阻止其他用户通过同时运行相同的脚本来覆盖临时文件。
-
但是,使用$$不足以生成代码(谷歌符号链接攻击);但它肯定比静态文件名更好。
-
只需使用mktemp
解:
1 2
| printf '%s
%s' 'text to prepend'"$(cat file.txt)"> file.txt |
请注意,这对所有类型的输入都是安全的,因为没有扩展。例如,如果您想要预先添加!@#$%^&*()ugly text
\t
,它将起作用:
1 2 3 4
| printf '%s
%s' '!@#$%^&*()ugly text
\t
'"$(cat file.txt)"> file.txt |
剩下要考虑的最后一部分是在命令替换"$(cat file.txt)"期间在文件末尾删除空格。所有解决方法都相对复杂。如果要在file.txt结尾处保留换行符,请参阅:https://stackoverflow.com/a/22607352/1091436
-
爱它。超级便携,无需生成新文件。谢谢!
-
如果file.txt的内容大于printf的参数列表中的内容,则唯一的问题就出现了。
如果替换输入文件是可以接受的:
注意:这样做可能会产生意外的副作用,特别是用常规文件替换符号链接,可能以文件的不同权限结束,以及更改文件的创建(出生)日期。
sed -i,如约翰·韦斯利亲王的回答,试图至少恢复原始权限,但其他限制适用。
1 2 3
| { printf 'line 1
line 2
'; cat text.txt; } > tmp.txt && mv tmp.txt text.txt |
注意:使用组命令{ ...; ... }比使用子shell((...; ...))更有效。
如果输入文件应该就地编辑(保留其inode及其所有属性):
使用古老的ed POSIX实用程序:
注意:ed总是首先将输入文件作为整体读入内存。
1 2 3 4 5 6 7
| ed -s text.txt <<'EOF'
1i
line 1
line 2
.
w
EOF |
-
-s抑制了ed的状态消息。
-
注意命令如何作为多行here-文档(<<'EOF' ... EOF)提供给ed,即通过stdin。
-
1i使1(第1行)成为当前行并开始插入模式(i)。
-
以下行是在当前行之前插入的文本,在其自己的行上以.结尾。
-
w将结果写回输入文件(用于测试,将w替换为,p仅打印结果,而不修改输入文件)。
在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"的以下内容;
没有中间文件,只是bash / echo。
-
最好的答案,我写这个单行使用这个答案旋转我的文件:for $ in $( file.txt;完成;
使用sed的另一种方法:
1 2
| sed -i.old '1 {i to be prepended
}' inFile |
如果要预先添加的行是多行:
1 2 3 4
| 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这样做。
另一个相当直接的解决方案是:
1 2
| $ echo -e"string
" $(cat file) |
-
这对我有用......只需稍加修改,为了保持输入文件的多行,你必须用引号将其包装起来,如下所示:echo -e"string n""$(cat~ / file.txt)">?/ file.txt;
-
那为什么不upvote @CraigWayne? jus'sayin'
-
没有双引号中的cat参数扩展是一个非常好的理由进行downvote。此代码将文件的内容拆分为单词,并将每个单词作为单独的参数传递给echo。您丢失了原始参数边界,丢失了换行符/制表符等等,原始文本中的\t和
等字符串将替换为制表符和换行符,而不是保留原样。
如果你喜欢vi / vim,这可能更符合你的风格。
1 2 3 4 5
| printf '0i
%s
.
wq
' prepend-text | ed file |
我真的不喜欢这里的任何答案,所以我建立了自己的命令:pre。
安装go:
go get -u github.com/Flaque/pre
将stdin中的一些文本预先添加到文件中:
echo"some at the start" | pre myFile.txt | sponge myFile.txt
该命令不会写入,它只是输出到stdout,所以你需要来自moreutils的sponge来保存文件。
-
在这里使用tee是不安全的。管道的所有部分都是同时启动的,因此在pre是否在tee截断其目标文件以进行写入之前完成读取文件的旧内容时,您会遇到竞争条件。
-
我可能会问你测试过的最大输入文件大小是多少?
-
@CharlesDuffy无可否认地开始在我生成的977K文件中花费了不合理的时间:yes"some text" | head -n 100000 > large-file然后运行:cat large-file | pre large-file
-
尝试echo"One line" | pre large-file | tee large-file,并检查生成的输出文件的大小。
-
@CharlesDuffy你能解释一下如何在这里做得更好吗? pre应该写到位吗? 我以为不是"unix"
-
@CharlesDuffy那个echo生成一个单行文件,很有意思。 没有意识到tee那样工作。
-
tee在首次调用时用O_TRUNC打开其输出文件,并在其stdin上接收内容时流式传输到该文件。 因为管道的所有部分都是同时调用的,这意味着当输入文件打开以用作输出时,它会被截断。
-
让我们在聊天中继续讨论。
我建议定义一个函数,然后在需要的地方导入和使用它。
1 2 3 4 5 6 7 8 9 10 11 12
| 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
} |
然后像这样使用它:
1 2
| prepend_to_file test.txt"This is first"
prepend_to_file test.txt"This is second" |
您的文件内容将是:
1 2
| This is second
This is first |
我即将使用这种方法来实现更改日志更新程序。
1 2 3 4 5 6 7
| # create a file with content..
echo foo > /tmp/foo
# prepend a line containing"jim" to the file
sed -i"1s/^/jim
/" /tmp/foo
# verify the content of the file has the new line prepened to it
cat /tmp/foo |