Shell one liner要预先添加到文件中

时间:2008-09-10 15:17:10

标签: shell unix

这可能是a complex solution

我正在寻找一个像“>>”这样的简单操作符,但是需要预先添加。

恐怕它不存在。我必须做一些像

这样的事情
 mv myfile tmp
 cat myheader tmp > myfile

更聪明吗?

32 个答案:

答案 0 :(得分:94)

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

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

信用:BASH: Prepend A Text / Lines To a File

答案 1 :(得分:28)

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

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

答案 2 :(得分:27)

下面的 hack 是一个快速的袖手旁观的答案,它起作用并收到了很多赞成票。然后,随着问题变得越来越流行,时间越来越长,愤怒的人们开始报告它有点工作,但奇怪的事情可能会发生,或者它根本不起作用,所以它被激怒了一段时间。这样的乐趣。

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

现在,尽管答案是:


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

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

答案 3 :(得分:20)

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

亲自试试:

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

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

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

答案 4 :(得分:16)

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

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

您可以使用其他工具(例如ed或perl)来执行此操作而无需临时文件。

答案 5 :(得分:15)

值得注意的是,使用像good idea这样的实用程序来安全生成临时文件通常是mktemp,至少如果脚本将以root权限执行。例如,您可以执行以下操作(再次使用bash):

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

答案 6 :(得分:14)

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

cat header myfile | sponge myfile

答案 7 :(得分:11)

使用bash heredoc可以避免使用tmp文件:

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

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

答案 8 :(得分: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脚本,我认为临时文件是迄今为止最容易(/唯一?)的方式..

答案 9 :(得分:9)

假设您要编辑的文件是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'。

答案 10 :(得分:8)

编辑:这已经破了。请参阅Weird behavior when prepending to a file with cat and tee

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

cat header main | tee main > /dev/null

答案 11 :(得分:7)

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

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

答案 12 :(得分:3)

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

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

P.S:只有当文件包含带反斜杠的文本时它才会起作用,导致它被解释为转义字符

答案 13 :(得分:3)

主要是为了娱乐/贝壳高尔夫,但是

ex -c '0r myheader|x' myfile

会做到这一点,而且没有管道或重定向。当然,vi / ex并不适合非交互式使用,所以vi会短暂闪现。

答案 14 :(得分:2)

为什么不简单地使用ed命令(这里已经提到了flffle)?

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ö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

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

答案 15 :(得分:2)

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

应该有一种方法可以通过@shixilun工作来实现sed方法,尽管他有疑虑。在将文件读入sed替换字符串时必须有一个bash命令来转义空格(例如用'\ n'替换换行符.Shell命令viscat可以处理非打字字符,但不能空白,所以这不会解决OP的问题:

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

由于替换脚本中的原始换行而失败,需要在换行符前加上一行,然后加上&amp;,以保持shell和sed的快乐,如this SO answer

对于非全局搜索替换命令,

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

答案 16 :(得分:2)

这是我发现的:

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

答案 17 :(得分:2)

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

答案 18 :(得分:2)

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

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

这又依赖于子shell执行 - (..) - 以避免cat拒绝为输入和输出提供相同的文件。

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

答案 19 :(得分:1)

您可以使用perl命令行:

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

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

或者如果my_header是文件:

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

/ e将允许替换中的代码评估。

答案 20 :(得分: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}"

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

答案 21 :(得分:1)

如果你有一个大文件(在我的情况下几百千字节)和访问python,这比cat管道解决方案快得多:

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

答案 22 :(得分:1)

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

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

答案 23 :(得分:1)

sed -i -e "1s/^/new first line\n/" old_file.txt

答案 24 :(得分: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

答案 25 :(得分:0)

current=`cat my_file` && echo 'my_string' > my_file && echo $current >> my_file

其中“my_file”是将“my_string”添加到。

的文件

答案 26 :(得分:0)

恕我直言,没有任何shell解决方案(永远不会是一个),无论两个文件myheadermyfile的大小如何,都能始终可靠地工作。原因是如果你想这样做而不重复使用临时文件(并且不让shell以静默方式重复到临时文件,例如通过像exec 3<>myfile这样的结构,管道到tee等等。 / p>

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

但是,您可以使用循环设备或多或少地伪装这个。例如,请参阅this SO thread

答案 27 :(得分: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

答案 28 :(得分:0)

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

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

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

答案 29 :(得分: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

答案 30 :(得分:0)

我最喜欢@fluffle's ed approach。毕竟,任何工具的命令行切换与脚本编辑器命令在这里基本相同;没有看到脚本编辑器解决方案“清洁度”是更小或什么。

这是我的一行附加到.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。

的诊断信息输出

关于完成这个,我discovered对于vim-buff来说,它也很好:

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

答案 31 :(得分:-1)

呸!没有人关心 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/>