135

这可能是一个复杂的解决方案

我正在寻找像“>>”这样的简单运算符,但要预先添加。

恐怕它不存在。我将不得不做类似的事情

mv myfile tmp
 猫 myheader tmp > myfile

有什么更聪明的吗?

4

34 回答 34

105

这仍然使用一个临时文件,但至少它在一行:

echo "text" | cat - yourfile > /tmp/out && mv /tmp/out yourfile

学分:BASH:在文件中添加文本/行

于 2008-09-10T15:21:26.127 回答
32
echo '0a
your text here
.
w' | ed some_file

ed 是标准编辑器!http://www.gnu.org/fun/jokes/ed.msg.html

于 2010-07-17T16:24:17.397 回答
30

下面的破解是一个快速的即兴回答,它有效并获得了很多支持。然后,随着这个问题变得越来越流行并且时间越来越长,愤怒的人们开始报告说它有点工作,但可能会发生奇怪的事情,或者它根本不起作用,所以它一度被疯狂地否决了。好好玩。

该解决方案利用了系统上文件描述符的精确实现,并且由于 nixes 之间的实现差异很大,因此它的成功完全取决于系统,绝对不可移植,并且不应该依赖任何甚至模糊重要的事情。

现在,尽管如此,答案是:


为文件 ( ) 创建另一个文件描述符,exec 3<> yourfile然后写入该文件 ( >&3) 似乎克服了读取/写入同一文件的困境。使用 awk 在 600K 文件上为我工作。然而,使用 'cat' 尝试相同的技巧失败了。

将前缀作为变量传递给 awk ( -v TEXT="$text") 克服了文字引号问题,该问题阻止了使用 'sed' 执行此技巧。

#!/bin/bash
text="Hello world
What's up?"

exec 3<> yourfile && awk -v TEXT="$text" 'BEGIN {print TEXT}{print}' yourfile >&3
于 2009-03-07T12:43:49.330 回答
20

John Mee:不能保证您的方法有效,如果您预先添加超过 4096 字节的内容,可能会失败(至少 gnu awk 会发生这种情况,但我认为其他实现也会有类似的限制)。在这种情况下,它不仅会失败,而且会进入一个无限循环,读取自己的输出,从而使文件增长,直到所有可用空间都被填满。

自己试试吧:

exec 3<>myfile && awk 'BEGIN{for(i=1;i<=1100;i++)print i}{print}' myfile >&3

(警告:一段时间后将其杀死,否则它将填满文件系统)

此外,以这种方式编辑文件非常危险,而且这是非常糟糕的建议,好像在编辑文件时发生了某些事情(崩溃,磁盘已满),您几乎可以保证文件处于不一致的状态。

于 2010-05-02T16:03:15.637 回答
19

没有临时文件是不可能的,但这里有一个 oneliner

{ echo foo; cat oldfile; } > newfile && mv newfile oldfile

您可以使用 ed 或 perl 等其他工具在没有临时文件的情况下执行此操作。

于 2008-09-10T15:22:22.740 回答
17

如果您在您控制的计算机上需要此功能,请安装包“moreutils”并使用“sponge”。然后你可以这样做:

cat header myfile | sponge myfile
于 2013-03-30T17:10:17.650 回答
16

可能值得注意的是,使用mktemp之类的实用程序安全地生成临时文件通常是一个好主意,至少如果脚本将以 root 权限执行的话。例如,您可以执行以下操作(再次在 bash 中):

(tmpfile=`mktemp` && { echo "prepended text" | cat - yourfile > $tmpfile && mv $tmpfile yourfile; } )
于 2008-09-12T16:51:54.147 回答
13

使用 bash heredoc 可以避免对 tmp 文件的需要:

cat <<-EOF > myfile
  $(echo this is prepended)
  $(cat myfile)
EOF

这是因为 $(cat myfile) 是在评估 bash 脚本时评估的,在执行带有重定向的 cat 之前。

于 2015-02-13T16:41:08.003 回答
10

假设您要编辑的文件是 my.txt

$cat my.txt    
this is the regular file

您要添加的文件是标题

$ cat header
this is the header

确保头文件中有最后一个空行。
现在你可以在它前面加上

$cat header <(cat my.txt) > my.txt

你最终得到

$ cat my.txt
this is the header
this is the regular file

据我所知,这只适用于“bash”。

于 2009-07-16T14:09:13.800 回答
9

当您开始尝试在 shell 脚本中做一些困难的事情时,我强烈建议您考虑用“适当的”脚本语言(Python/Perl/Ruby/etc)重写脚本

至于将一行添加到文件中,不可能通过管道来执行此操作,因为当您执行类似 的任何操作时cat blah.txt | grep something > blah.txt,它会无意中使文件空白。有一个名为sponge您可以安装的小实用程序命令(您安装cat blah.txt | grep something | sponge blah.txt它,它会缓冲文件的内容,然后将其写入文件)。它类似于临时文件,但您不必明确地这样做。但我会说这是一个比 Perl 更“糟糕”的要求。

可能有一种方法可以通过 awk 或类似方法来完成,但如果你必须使用 shell 脚本,我认为临时文件是迄今为止最简单(/唯一?)的方法。

于 2008-09-14T23:08:41.447 回答
8

就像 Daniel Velkov 建议的那样,使用 tee。
对我来说,这是简单的智能解决方案:

{ echo foo; cat bar; } | tee bar > /dev/null
于 2012-02-28T16:01:31.063 回答
7

编辑:这是坏的。使用 cat 和 tee 附加到文件时,请参阅奇怪的行为

覆盖问题的解决方法是使用tee

cat header main | tee main > /dev/null
于 2010-10-26T19:49:49.883 回答
5
sed -i -e "1s/^/new first line\n/" old_file.txt
于 2010-09-30T22:34:49.723 回答
3

我使用的那个。这允许您以您喜欢的方式指定顺序、额外字符等:

echo -e "TEXTFIRSt\n$(< header)\n$(< my.txt)" > my.txt

PS:只有当文件包含带反斜杠的文本时它才不起作用,因为它被解释为转义字符

于 2011-05-24T09:19:18.550 回答
3

主要是为了好玩/打壳高尔夫,但是

ex -c '0r myheader|x' myfile

会成功的,并且没有管道或重定向。当然,vi/ex 并不是真正用于非交互式使用,所以 vi 会短暂闪现。

于 2013-07-08T17:21:32.583 回答
3

使用$( command )您可以将命令的输出写入变量。所以我在一行中用三个命令完成了它,没有临时文件。

originalContent=$(cat targetfile) && echo "text to prepend" > targetfile && echo "$originalContent" >> targetfile
于 2014-01-05T07:14:22.107 回答
2

cb0 的“无临时文件”解决方案的一个变体,用于添加固定文本:

echo "text to prepend" | cat - file_to_be_modified | ( cat > file_to_be_modified ) 

这再次依赖于子 shell 执行 - (..) - 以避免 cat 拒绝使用相同的文件进行输入和输出。

注意:喜欢这个解决方案。但是,在我的 Mac 中,原始文件丢失了(认为它不应该,但它确实丢失了)。这可以通过将您的解决方案编写为来解决: echo "text to prepend" | 猫-file_to_be_modified | 猫 > tmp_file; mv tmp_file file_to_be_modified

于 2010-03-02T11:57:43.213 回答
2

警告:这需要更多的工作来满足 OP 的需求。

尽管有疑虑,但应该有一种方法可以使@shixilun 的 sed 方法发挥作用。vis将文件读入 sed 替代字符串时必须有一个 bash 命令来转义空格(例如,用 '\n' 替换换行符。Shell 命令cat可以处理不可打印的字符,但不能处理空格,所​​以这不会解决 OP 的问题问题:

sed -i -e "1s/^/$(cat file_with_header.txt)/" file_to_be_prepended.txt

由于替代脚本中的原始换行符而失败,需要在其前面加上一个续行字符 () 并且可能后跟一个 &,以保持 shell 和 sed 快乐,就像这个 SO answer

sed非全局搜索替换命令的大小限制为 40K(模式后没有尾随 /g),因此可能会避免匿名警告的 awk 可怕的缓冲区溢出问题。

于 2011-03-06T08:11:55.997 回答
2

为什么不简单地使用 ed 命令(正如 fluffle 在这里所建议的那样)?

ed 将整个文件读入内存并自动执行就地文件编辑!

所以,如果你的文件不是那么大......

# cf. "Editing files with the ed text editor from scripts.",
# http://wiki.bash-hackers.org/doku.php?id=howto:edit-ed

prepend() {
   printf '%s\n' H 1i "${1}" . wq | ed -s "${2}"
}

echo 'Hello, world!' > myfile
prepend 'line to prepend' myfile

另一种解决方法是使用 Jürgen Hötzel 在Redirect output from sed 's/c/d/' myFile to myFile中建议的打开文件句柄

echo cat > manipulate.txt
exec 3<manipulate.txt
# Prevent open file from being truncated:
rm manipulate.txt
sed 's/cat/dog/' <&3 > manipulate.txt

当然,所有这些都可以放在一条线上。

于 2011-04-08T09:23:18.440 回答
2

这是我发现的:

echo -e "header \n$(cat file)" >file
于 2011-11-21T07:24:37.550 回答
2
sed -i -e '1rmyheader' -e '1{h;d}' -e '2{x;G}' myfile
于 2012-05-06T15:30:20.870 回答
1

如果您在 BASH 中编写脚本,实际上,您可以发出:

cat - 你的文件 /tmp/out && mv /tmp/out 你的文件

这实际上是在您自己在自己的问题中发布的复杂示例中。

于 2008-09-11T05:00:50.917 回答
1

如果您有一个大文件(在我的情况下为几百 KB)并且可以访问 python,这比cat管道解决方案要快得多:

python -c 'f = "filename"; t = open(f).read(); open(f, "w").write("text to prepend " + t)'

于 2014-10-12T21:42:25.920 回答
1

一个解决方案printf

new_line='the line you want to add'
target_file='/file you/want to/write to'

printf "%s\n$(cat ${target_file})" "${new_line}" > "${target_file}"

你也可以这样做:

printf "${new_line}\n$(cat ${target_file})" > "${target_file}"

但在这种情况下,您必须确保没有任何%地方,包括目标文件的内容,因为它可以被解释并搞砸您的结果。

于 2015-06-29T02:22:30.190 回答
1

您可以使用 perl 命令行:

perl -i -0777 -pe 's/^/my_header/' tmp

其中 -i 将创建文件的内联替换,而 -0777 将 slurp 整个文件并使 ^ 仅匹配开头。-pe 将打印所有行

或者如果 my_header 是一个文件:

perl -i -0777 -pe 's/^/`cat my_header`/e' tmp

/e 将允许在替换中对代码进行评估。

于 2016-01-07T00:15:05.337 回答
0
current=`cat my_file` && echo 'my_string' > my_file && echo $current >> my_file

其中“my_file”是要添加“my_string”的文件。

于 2010-04-28T16:54:23.483 回答
0

我最喜欢@fluffle 的ed方法。毕竟,任何工具的命令行开关与脚本编辑器命令在这里本质上是一样的;没有看到脚本编辑器解决方案“清洁度”变得更小或诸如此类。

这是我附加到的单行代码,用于添加.git/hooks/prepare-commit-msg一个 in-repo.gitmessage文件以提交消息:

echo -e "1r $PWD/.gitmessage\n.\nw" | ed -s "$1"

示例.gitmessage

# Commit message formatting samples:
#       runlevels: boot +consolekit -zfs-fuse
#

我正在做1r而不是0r,因为这会在原始模板的文件顶部留下空的准备写入行。不要在.gitmessagethen 上面放一个空行,你最终会得到两个空行。-s抑制 ed 的诊断信息输出。

在经历这个过程中,我发现对于 vim 爱好者来说,拥有以下内容也很好:

[core]
        editor = vim -c ':normal gg'
于 2011-08-13T11:01:07.017 回答
0

变量,ftw?

NEWFILE=$(echo deb http://mirror.csesoc.unsw.edu.au/ubuntu/ $(lsb_release -cs) main universe restricted multiverse && cat /etc/apt/sources.list)
echo "$NEWFILE" | sudo tee /etc/apt/sources.list
于 2011-08-17T10:20:38.867 回答
0

我认为这是 ed 最干净的变体:

cat myheader | { echo '0a'; cat ; echo -e ".\nw";} | ed myfile

作为一个函数:

function prepend() { { echo '0a'; cat ; echo -e ".\nw";} | ed $1; }

cat myheader | prepend myfile
于 2012-12-29T00:06:56.080 回答
0

恕我直言,无论两个文件myheadermyfile. 原因是,如果您想在不重复到临时文件的情况下执行此操作(并且不让 shell 以静默方式重复到临时文件,例如通过exec 3<>myfile、 管道tee等构造)。

您正在寻找的“真实”解决方案需要摆弄文件系统,因此它在用户空间中不可用并且取决于平台:您要求将正在使用的文件系统指针修改为文件系统指针myfile的当前值用指向当前文件系统地址的链接链接myheader替换文件系统中EOF的。这不是微不足道的,显然不能由非超级用户完成,也可能不是超级用户......玩 inode 等。myheadermyfile

不过,您可以或多或少地使用循环设备来伪造它。参见例如这个 SO thread

于 2014-08-16T14:05:40.043 回答
0

又快又脏,用 python 缓冲内存中的所有内容:

$ echo two > file
$ echo one | python -c "import sys; f=open(sys.argv[1]).read(); open(sys.argv[1],'w').write(sys.stdin.read()+f)" file
$ cat file
one
two
$ # or creating a shortcut...
$ alias prepend='python -c "import sys; f=open(sys.argv[1]).read(); open(sys.argv[1],\"w\").write(sys.stdin.read()+f)"'
$ echo zero | prepend file
$ cat file
zero
one
two
于 2015-06-05T10:00:23.243 回答
0

对于破折号/灰烬:

echo "hello\n$(cat myfile)" > myfile

例子:

$ echo "line" > myfile
$ cat myfile
line
$ echo "line1\n$(cat myfile)" > myfile
$ cat myfile
line1
line
于 2021-10-11T01:14:57.703 回答
0

我找到的最简单的解决方案是:

cat myHeader myFile | tee myFile

或者

echo "<line to add>" | cat - myFile | tee myFile

笔记:

  • 如果echo -n您只想附加一段文本(而不是整行),请使用。
  • &> /dev/null如果您不想看到输出(生成的文件),请添加到末尾。
  • 这可用于将 shebang 附加到文件中。例子:
    # make it executable (use u+x to allow only current user)
    chmod +x cropImage.ts
    # append the shebang
    echo '#''!'/usr/bin/env ts-node | cat - cropImage.ts | tee cropImage.ts &> /dev/null
    # execute it
    ./cropImage.ts myImage.png
    
于 2022-01-04T17:37:04.887 回答
-2

呸! 没有人愿意提及tac

endor@grid ~ $ tac --help
Usage: tac [OPTION]... [FILE]...
Write each FILE to standard output, last line first.
With no FILE, or when FILE is -, read standard input.

Mandatory arguments to long options are mandatory for short options too.
  -b, --before             attach the separator before instead of after
  -r, --regex              interpret the separator as a regular expression
  -s, --separator=STRING   use STRING as the separator instead of newline
      --help     display this help and exit
      --version  output version information and exit

Report tac bugs to bug-coreutils@gnu.org
GNU coreutils home page: <http://www.gnu.org/software/coreutils/>
General help using GNU software: <http://www.gnu.org/gethelp/>
Report tac translation bugs to <http://translationproject.org/team/>
于 2010-04-16T10:31:04.390 回答