在文件前加上一个内胆shell?

这可能是一个复杂的解决scheme 。

我正在寻找一个像“>>”这样简单的操作符,但是用于预先考虑。

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

  mv myfile tmp
  cat myheader tmp> myfile

什么更聪明? (我不喜欢tmp文件)

下面的黑客是一个快速的非正式的答案,工作,并收到了很多upvotes。 然后,随着问题变得越来越stream行,越来越多的时间过去了,愤怒的人开始报告说,这个问题起了作用,但是奇怪的事情可能会发生,或者根本就不起作用,所以一度被愤怒地低估了。 好好玩。

该解决scheme利用了系统上文件描述符的准确实现,而且由于nix之间的实现差异很大,它的成功完全取决于系统,绝对不可移植,不应该依赖于任何非常重要的事情。

现在,所有这一切的答案是:


为文件( exec 3<> yourfile )创build另一个文件描述符,然后写入( >&3 )似乎克服了相同的文件困境的读/写。 适用于使用awk的600K文件。 然而,使用“猫”尝试相同的技巧失败。

将prependage作为variables传递给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 

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

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

信用: BASH:将文本/行添加到文件中

 echo '0a your text here . w' | ed some_file 

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

John Mee:你的方法不能保证能正常工作,而且如果你添加了超过4096字节的东西(至lessgnu awk会发生这种情况,但是我认为其他的实现会有类似的约束),可能会失败。 在这种情况下,不仅会失败,而且会进入一个无限循环,读取自己的输出,从而使文件增长,直到所有可用空间填满。

自己尝试一下:

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

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

而且,以这种方式编辑文件是非常危险的,这是一个非常糟糕的build议,就像在编辑文件时(崩溃,磁盘已满)发生什么事情一样,你几乎可以保证文件处于不一致的状态。

值得一提的是,使用像mktemp这样的工具安全地生成临时文件通常是一个好主意 ,至less如果脚本将以root权限执行的话。 你可以例如做以下(再次在bash中):

 (tmpfile=`mktemp` && { echo "prepended text" | cat - yourfile > $tmpfile && mv $tmpfile yourfile; } ) 

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

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

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

如果您需要在您控制的计算机上安装,请安装软件包“moreutils”并使用“海绵”。 那你可以这样做:

 cat header myfile | sponge myfile 

当你开始尝试做一些在shell脚本中变得困难的事情时,我强烈build议寻找用“适当的”脚本语言(Python / Perl / Ruby / etc)来重写脚本。

至于在文件前加一行,不可能通过pipe道来完成,就像你做任何事情一样,比如cat blah.txt | grep something > blah.txt cat blah.txt | grep something > blah.txt ,它无意间空白该文件。 有一个叫做sponge的小实用程序命令可以安装(你可以用cat blah.txt | grep something | sponge blah.txt来缓冲文件内容,然后写入文件)。 它与临时文件类似,但你不必明确地做到这一点。 但是我会说这比Perl要糟糕的多。

有可能通过awk或类似的方式来做到这一点,但如果你必须使用shell脚本,我认为临时文件是最简单的(/只?)方式。

编辑:这是坏了。 使用cat和tee预先考虑文件时,请参阅奇怪的行为

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

 cat header main | tee main > /dev/null 

假定你想编辑的文件是my.txt

 $cat my.txt this is the regular file 

而你要预先的文件是标题

 $ cat header this is the header 

确保头文件中有最后一个空白行。
现在你可以预先configuration它

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

你结束了

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

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

就像丹尼尔·维可夫(Daniel Velkov)build议的那样,用三通
对我来说,这是简单的智能解决scheme:

 { echo foo; cat bar; } | tee bar > /dev/null 

使用bash heredoc你可以避免需要一个tmp文件:

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

这是有效的,因为$(cat myfile)在评估bash脚本时被执行,然后cat执行redirect。

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

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

PS:只有在文件中包含带反斜杠的文本时,它才会工作,导致它被解释为转义字符

主要是为了打高尔夫球,但是

 ex -c '0r myheader|x' myfile 

将做的伎俩,并没有pipe道或redirect。 当然,vi / ex并不是非交互式的用途,所以vi会短暂闪烁。

为什么不简单地使用ed命令(正如这里所说的那样)呢?

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ürgenHötzelbuild议在redirect输出从sed's / c / d /'myFile到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 

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

cb0“无临时文件”的解决scheme的一个变种预先固定文本:

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

再次,这依赖于子shell执行 – (..) – 以避免猫拒绝具有相同的文件input和输出。

注意:喜欢这个解决scheme。 但是,在我的Mac中原来的文件丢失(认为它不应该,但它确实)。 这可以通过编写解决scheme来解决:echo“text to prepend”| cat – file_to_be_modified | cat> tmp_file; mv tmp_file file_to_be_modified

 sed -i -e '1rmyheader' -e '1{h;d}' -e '2{x;G}' myfile 

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

尽pipe有疑虑,应该有办法让@shixilun工作。 当读入一个sedreplacestring时,必须有一个bash命令来转义空格(例如用'\ n'replace换行符)Shell命令viscat可以处理不可打印的字符,而不是空格,所以这不会解决OP的问题:

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

由于replace脚本中的原始换行符失败,这需要在行后续字符()后面加一个&,以保持shell和sed的快乐,就像这样

sed对非全局searchreplace命令的大小限制为40K(模式之后没有跟踪/ g),所以可能会避免匿名警告awk的可怕的缓冲区溢出问题。

使用$(command)可以将命令的输出写入一个variables。 所以我在一行三个命令中做了这个,没有临时文件。

 originalContent=$(cat targetfile) && echo "text to prepend" > targetfile && echo "$originalContent" >> targetfile 

如果你有一个很大的文件(在我的情况下是几百KB)并且可以访问python,这比catpipe道解决scheme快得多:

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

printf的解决scheme:

 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}" 

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

你可以使用perl命令行:

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

其中-i将创build一个内联replace文件,-0777将啜泣整个文件,并只匹配开始。 -pe将打印所有行

或者,如果my_header是一个文件:

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

/ e将允许replace代码的评估。

呸! 没有人关心提及。

 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/> 
 current=`cat my_file` && echo 'my_string' > my_file && echo $current >> my_file 

其中“my_file”是将“my_string”前置的文件。

我喜欢@蓬松的方法是最好的。 毕竟,任何工具的命令行开关与脚本编辑器命令在本质上是相同的。 没有看到一个脚本编辑器解决scheme“清洁”是任何较小或什么的。

这是我的.gitmessage附加到.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 ,因为这会使原来的模板文件顶部留下空的准备写入行。 不要在你的.gitmessage之上放一个空行,最后会有两个空行。 -s取消ed的诊断信息输出。

在通过这个过程中,我发现对于vim-buffs来说,也是有好处的:

 [core] editor = vim -c ':normal gg' 

variables,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 

这是我发现的:

 echo -e "header \n$(cat file)" >file 

我认为这是ed最清晰的变化:

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

作为一个function:

 function prepend() { { echo '0a'; cat ; echo -e ".\nw";} | ed $1; } cat myheader | prepend myfile 
 sed -i -e "1s/^/new first line\n/" old_file.txt 

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

 cat  -  yourfile / tmp / out && mv / tmp / out yourfile

这实际上是你自己发布的复杂例子。