这可能是一个复杂的解决方案。
我正在寻找一个简单的运算符,例如">>",但要放在前面。
恐怕它不存在。 我必须做类似的事情
1 2
| mv myfile tmp
cat myheader tmp > myfile |
还有什么更聪明的吗?
这仍然使用一个临时文件,但至少它在一行上:
1
| echo"text" | cat - yourfile > /tmp/out && mv /tmp/out yourfile |
信用:BASH:在文件前添加文本/行
1 2 3 4
| echo '0a
your text here
.
w' | ed some_file |
ed是标准编辑器! http://www.gnu.org/fun/jokes/ed.msg.html
下面的hack是一个快速的即席答案,该方法行之有效并获得了很多好评。然后,随着这个问题变得越来越流行和更多的时间流逝,愤怒的人们开始报告说这确实有效,但是奇怪的事情可能会发生,或者根本就不起作用,因此一度被否决了。好好玩。
该解决方案利用了系统上文件描述符的确切实现,并且由于实现之间的差异很大,因此它的成功完全取决于系统,绝对不可移植,并且即使有任何非常重要的意义也不应依赖于此。
现在,所有的答案都是:
因此,为文件(exec 3<> yourfile)创建另一个文件描述符,从而写入该文件(>&3)似乎可以克服对同一文件的读写难题。使用awk为我处理600K文件。但是,使用" cat"尝试相同的技巧失败。
将前置变量作为变量传递给awk(-v TEXT="$text")克服了文字引号问题,该问题阻止了使用" sed"执行此技巧。
1 2 3 4 5
| #!/bin/bash
text="Hello world
What's up?"
exec 3<> yourfile && awk -v TEXT="$text" 'BEGIN {print TEXT}{print}' yourfile >&3 |
约翰·米(John Mee):您的方法不能保证能正常工作,并且如果您将4096字节以上的内容添加到内容中,可能会失败(至少gnu awk会发生这种情况,但我想其他实现将具有类似的约束)。在这种情况下,它不仅会失败,而且将进入一个无限循环,在循环中它将读取自己的输出,从而使文件增长,直到所有可用空间都被填满。
自己尝试:
1
| exec 3<>myfile && awk 'BEGIN{for(i=1;i<=1100;i++)print i}{print}' myfile >&3 |
(警告:过一会儿杀死它,否则它将填满文件系统)
此外,以这种方式编辑文件非常危险,这是非常糟糕的建议,因为好像在编辑文件时发生了某些情况(崩溃,磁盘已满),几乎可以保证文件处于不一致状态。
没有临时文件是不可能的,但是这里有一个单面纸
1
| { echo foo; cat oldfile; } > newfile && mv newfile oldfile |
您可以使用ed或perl等其他工具来执行此操作,而无需使用临时文件。
可能值得注意的是,使用mktemp之类的实用程序安全地生成临时文件通常是个好主意,至少在脚本将以root特权执行的情况下也是如此。例如,您可以执行以下操作(再次以bash进行):
1
| (tmpfile=`mktemp` && { echo"prepended text" | cat - yourfile > $tmpfile && mv $tmpfile yourfile; } ) |
如果在您控制的计算机上需要此功能,请安装软件包" moreutils"并使用"海绵"。然后,您可以执行以下操作:
1
| cat header myfile | sponge myfile |
使用bash heredoc可以避免需要tmp文件:
1 2 3 4
| cat <<-EOF > myfile
$(echo this is prepended)
$(cat myfile)
EOF |
之所以有效,是因为在执行带重定向的cat之前,在评估bash脚本时会评估$(cat myfile)。
当您开始尝试使用shell脚本执行变得困难的事情时,强烈建议您尝试使用"适当的"脚本语言(Python / Perl / Ruby / etc)重写脚本
至于在文件前添加一行,则无法通过管道执行此操作,因为当您执行cat blah.txt | grep something > blah.txt之类的操作时,它会无意中使文件空白。您可以安装一个名为sponge的小实用程序命令(执行cat blah.txt | grep something | sponge blah.txt并缓冲文件的内容,然后将其写入文件)。它类似于临时文件,但您不必显式地执行该操作。但是我想说,这比Perl更为"糟糕"。
也许有一种方法可以通过awk或类似的方法来完成,但是如果您必须使用shell脚本,我认为临时文件是迄今为止最简单的(/ only?)方法。
假设您要编辑的文件是my.txt
1 2
| $cat my.txt
this is the regular file |
您要添加的文件是header
1 2
| $ cat header
this is the header |
确保头文件中有最后一个空白行。
现在,您可以添加
1
| $cat header <(cat my.txt) > my.txt |
你最终得到
1 2 3
| $ cat my.txt
this is the header
this is the regular file |
据我所知,这仅适用于" bash"。
编辑:这是坏的。在使用cat和tee前置文件时,请参阅奇怪的行为
解决覆盖问题的方法是使用tee:
1
| cat header main | tee main > /dev/null |
像Daniel Velkov建议的那样,使用tee。
对我来说,这是简单的智能解决方案:
1
| { echo foo; cat bar; } | tee bar > /dev/null |
主要用于娱乐/空壳高尔夫,但
1
| ex -c '0r myheader|x' myfile |
可以解决问题,并且没有管道或重定向。当然,vi / ex并非真的非交互使用,因此vi会短暂闪烁。
我用的那个。这使您可以按照自己喜欢的方式指定顺序,额外的字符等:
1 2 3
| echo -e"TEXTFIRSt
$(< header)
$(< my.txt)"> my.txt |
附言:仅当文件包含带反斜杠的文本时,它才起作用,因为它被解释为转义字符
为什么不简单地使用ed命令(正如fluffle在此处建议的那样)?
ed将整个文件读入内存并自动执行就地文件编辑!
所以,如果您的文件不是那么大...
1 2 3 4 5 6 7 8 9 10
| # cf."Editing files with the ed text editor from scripts.",
# http://wiki.bash-hackers.org/doku.php?id=howto:edit-ed
prepend() {
printf '%s
' H 1i"${1}" . wq | ed -s"${2}"
}
echo 'Hello, world!' > myfile
prepend 'line to prepend' myfile |
另一个变通办法是按照JürgenH?tzel在将sed s / c / d / myFile重定向输出到myFile中的建议使用打开文件句柄。
1 2 3 4 5
| 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解决方案的一个变体,用于"无临时文件"以添加固定文本:
1
| echo"text to prepend" | cat - file_to_be_modified | ( cat > file_to_be_modified ) |
再次,这依赖于子外壳程序的执行(..),以避免猫拒绝输入和输出相同的文件。
注意:喜欢此解决方案。但是,在我的Mac中,原始文件丢失了(本来应该不会,但是会丢失)。可以通过将解决方案编写为:
回显"要添加的文字" |猫-file_to_be_modified |猫> tmp_file; mv tmp_file file_to_be_modified
这是我发现的:
1 2
| echo -e"header
$(cat file)">file |
1
| sed -i -e '1rmyheader' -e '1{h;d}' -e '2{x;G}' myfile |
警告:这需要更多的工作来满足OP的需求。
尽管有@shixilun的疑虑,但应该有一种方法可以使@shixilun的sed方法起作用。将文件读入sed替代字符串时,必须有一个bash命令来转义空格(例如,用' n'替换换行符。Shell命令vis和cat可以处理不可打印的字符,但不能处理空格,因此无法解决OP的问题:
1
| sed -i -e"1s/^/$(cat file_with_header.txt)/" file_to_be_prepended.txt |
由于替换脚本中的原始换行而失败,该脚本需要在换行符之前加上换行符(),并可能在后面加上&,以使shell和sed保持满意,例如这样的答案
对于非全局搜索替换命令,sed的大小限制为40K(在模式之后没有尾随/ g),因此可以避免匿名警告的可怕的awk缓冲区溢出问题。
使用$(command)可以将命令的输出写入变量。
因此,我在一行中的三个命令中执行了此操作,并且没有临时文件。
1
| originalContent=$(cat targetfile) && echo"text to prepend"> targetfile && echo"$originalContent">> targetfile |
printf的解决方案:
1 2 3 4 5
| new_line='the line you want to add'
target_file='/file you/want to/write to'
printf"%s
$(cat ${target_file})""${new_line}">"${target_file}" |
您也可以这样做:
1 2
| printf"${new_line}
$(cat ${target_file})">"${target_file}" |
但是在那种情况下,您必须确保在任何地方都没有%,包括目标文件的内容,因为这可以解释并导致结果出错。
1 2
| sed -i -e"1s/^/new first line
/" old_file.txt |
您可以使用perl命令行:
1
| perl -i -0777 -pe 's/^/my_header/' tmp |
其中-i将创建文件的内联替换,并
-0777将处理整个文件,并使^仅匹配开头。
-pe将打印所有行
或者,如果my_header是文件:
1
| perl -i -0777 -pe 's/^/`cat my_header`/e' tmp |
/ e将允许替换代码的地方。
如果您有一个大文件(在我的情况下只有几百千字节)并且可以访问python,则这比cat管道解决方案要快得多:
python -c 'f ="filename"; t = open(f).read(); open(f,"w").write("text to prepend" + t)'
实际上,如果您使用BASH编写脚本,则可以发出以下命令:
1
| cat - yourfile /tmp/out && mv /tmp/out yourfile |
那实际上是您自己在自己的问题中发布的复杂示例。
快速又脏乱,用python缓冲内存中的所有内容:
1 2 3 4 5 6 7 8 9 10 11 12
| $ 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 |
恕我直言,无论两个文件myheader和myfile的大小如何,都没有能够始终如一且可靠地工作的shell解决方案(并且永远不会是这样)。原因是,如果您想这样做而无需重复使用临时文件(并且不让外壳静默地重复使用到临时文件),例如通过诸如exec 3<>myfile之类的结构,管道传输至tee等。
您正在寻找的"真实"解决方案需要摆弄文件系统,因此它在用户空间中不可用,并且将依赖于平台:您要将myfile使用的文件系统指针修改为的当前值。 myheader的文件系统指针,并在文件系统中用链接链接替换myheader的EOF到myfile指向的当前文件系统地址。这并非微不足道,而且显然非超级用户也无法完成,而且超级用户可能也无法做到这一点……使用inode,等等。
但是,您可以使用环路设备或多或少地伪造此内容。例如,参见此SO线程。
我认为这是ed最干净的版本:
1 2
| cat myheader | { echo '0a'; cat ; echo -e".
w";} | ed myfile |
作为功??能:
1 2 3 4
| function prepend() { { echo '0a'; cat ; echo -e".
w";} | ed $1; }
cat myheader | prepend myfile |
1
| current=`cat my_file` && echo 'my_string' > my_file && echo $current >> my_file |
其中" my_file"是在" my_string"之前添加的文件。
变量,ftw?
1 2
| 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 |
我最喜欢@fluffle的ed方法。毕竟,任何工具的命令行开关与脚本编辑器命令在这里基本上都是同一件事。没有看到脚本化的编辑器解决方案"整洁度"更小或什么都没有。
这是我在.git/hooks/prepare-commit-msg后面附加的单行代码,以在回购.gitmessage文件之前添加提交消息:
1 2 3
| echo -e"1r $PWD/.gitmessage
.
w" | ed -s"$1" |
示例.gitmessage:
1 2 3
| # Commit message formatting samples:
# runlevels: boot +consolekit -zfs-fuse
# |
我正在执行1r而不是0r,因为这将在原始模板的文件顶部保留空的可写行。不要在.gitmessage的顶部放置一个空行,然后将得到两个空行。 -s禁止输出ed的诊断信息。
关于这一点,我发现对于vim-buffs来说,拥有以下内容也是一件好事:
1 2
| [core]
editor = vim -c ':normal gg' |
呸!没有人愿意提起TAC。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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/> |