【问题标题】:Shell one liner to prepend to a file将一个衬垫添加到文件中
【发布时间】:2010-09-08 10:27:49
【问题描述】:

这可能是a complex solution

我正在寻找像“>>”这样的简单运算符,但用于前置。

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

mv myfile tmp 猫 myheader tmp > myfile

有什么更聪明的吗?

【问题讨论】:

标签: shell unix


【解决方案1】:

对于破折号/灰:

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

示例:

$ echo "line" > myfile
$ cat myfile
line
$ echo "line1\n$(cat myfile)" > myfile
$ cat myfile
line1
line

【讨论】:

    【解决方案2】:

    你可以使用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 将允许在替换中对代码进行评估。

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

      • 如果您的目标文件中有任何被 printf 解释为格式化选项的内容,这似乎会爆炸(并截断您的文件!)。
      • echo 似乎是一个更安全的选择。 echo "my new line\n$(cat my/file.txt)" > my/file.txt
      • @AlanH。我警告答案中的危险。
      • 实际上,您只警告了${new_line} 中的百分比,而不是目标文件
      • 你能说得更清楚些吗?这是一个非常大的警告IMO。 “任何地方”并没有说明这一点。
      【解决方案4】:

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

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

      现在,答案是:


      为文件 (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
      

      【讨论】:

      • 警告:检查输出以确保除了第一行之外没有任何变化。我使用了这个解决方案,虽然它似乎有效,但我注意到似乎添加了一些新行。我不明白这些是从哪里来的,所以我不能确定这个解决方案是否真的有问题,但要小心。
      • 请注意,在上面的 bash 示例中,双引号跨越回车符,以演示前置多行。检查您的 shell 和文本编辑器是否正在合作。 \r\n 浮现在脑海中。
      • 谨慎使用!请参阅以下评论了解原因:stackoverflow.com/questions/54365/…
      • 如果现在不可靠,如何用更好的解决方案更新您的答案?
      • hack 是有用的>在仔细观察的约束条件下。尽管有您的免责声明,但您的 hack 不符合这些标准(“完全依赖于系统”、“似乎可以克服”、“对我有用”)。如果我们认真对待您的免责声明,没有人 应该使用您的 hack - 我同意。那么我们还剩下什么?嘈杂的干扰。我看到了两种选择:(a) 删除您的答案,或者 (b) 将其变成一个警示故事,解释为什么——尽管它可能很诱人——这种方法不能一般工作>.
      【解决方案5】:

      又快又脏,用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
      

      【讨论】:

        【解决方案6】:

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

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

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

        【讨论】:

          【解决方案7】:

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

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

          【讨论】:

            【解决方案8】:

            恕我直言,无论myheadermyfile 这两个文件的大小如何,都没有可以始终如一且可靠地工作的shell 解决方案(永远不会是这样)。原因是,如果您想在不重复到临时文件的情况下执行此操作(并且不让 shell 以静默方式重复到临时文件,例如通过 exec 3&lt;&gt;myfile、管道到 tee 等结构)。

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

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

            【讨论】:

              【解决方案9】:

              编辑:这是坏的。见Weird behavior when prepending to a file with cat and tee

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

              cat header main | tee main > /dev/null
              

              【讨论】:

              • 挂了一会儿。以“tee:main:设备上没有剩余空间”结尾。 main 是几个 GB,虽然两个原始文本文件都只有几个 KB。
              • 如果您发布的解决方案已损坏(实际上填满了用户的硬盘),请帮助社区并删除您的答案。
              • 你能给我指出一条应该删除错误答案的指南吗?
              【解决方案10】:

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

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

              【讨论】:

                【解决方案11】:

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

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

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

                【讨论】:

                • 一位编辑建议将其更改为 cat - yourfile &lt;&lt;&lt;"text" &gt; /tmp/out &amp;&amp; mv /tmp/out yourfile,但这与我的答案有很大不同,应该是它自己的答案。
                【解决方案12】:

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

                ex -c '0r myheader|x' myfile
                

                可以解决问题,并且没有管道或重定向。当然,vi/ex 并不是真正用于非交互式使用,所以 vi 会短暂闪烁。

                【讨论】:

                • 从我的角度来看最好的配方,因为它使用现有命令并且以简单的方式执行。
                【解决方案13】:

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

                cat header myfile | sponge myfile
                

                【讨论】:

                • 结合@Vinko Vrsalovic 的回答,这对我来说效果很好:{ echo "prepended text"; cat myfile } | sponge myfile
                【解决方案14】:

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

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

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

                【讨论】:

                • -cat 之后在做什么?
                • 有没有办法在不换行的情况下添加“文本”?
                • 一个警告:如果yourfile 是一个符号链接,这不会做你想要的。
                • @makboi -cat 之后是Shell 编程中使用的约定,表示应从stdin 读取命令的输入。答案中echo等命令的输出写入stdout,并使用管道|写入cat命令的stdin。将管道视为将一个命令的stdout 传递给另一个命令的stdin,因此得名 - 左侧命令的输出流入右侧命令的输入
                • 对不起,我是个书呆子,但&amp;&amp; 意味着这是一个“单行”,就像缩小 Javascript 文件创建一个“单行”一样,你可能会混淆新手不知道这个运算符分隔两个命令。写成分隔线 echo "text" | cat - yourfile &gt; /tmp/out mv /tmp/out yourfile 会更清楚 - 也许sed -i '1s/^/prepend me\n/' filename 更适合真正的“单行”前置器
                【解决方案15】:
                sed -i -e "1s/^/new first line\n/" old_file.txt
                

                【讨论】:

                • 正是我想要的,但确实不是问题的正确答案
                【解决方案16】:

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

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

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

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

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

                【讨论】:

                  【解决方案17】:

                  我认为这是 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
                  

                  【讨论】:

                    【解决方案18】:
                    sed -i -e '1rmyheader' -e '1{h;d}' -e '2{x;G}' myfile
                    

                    【讨论】:

                      【解决方案19】:

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

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

                      【讨论】:

                        【解决方案20】:

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

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

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

                        【讨论】:

                          【解决方案21】:

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

                          自己试试吧:

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

                          (警告:过一会儿就杀死它,否则它会填满文件系统)

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

                          【讨论】:

                          • 这应该是一个评论,而不是一个单独的答案。
                          • @Ikraav:也许吧,但是“评论”很长而且很详细(而且很有用),看起来不太好,而且可能不适合 StackOverflow 有限的评论容量。
                          【解决方案22】:

                          这是我的发现:

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

                          【讨论】:

                          • 这和@nemisj 之前的建议一样吗?
                          【解决方案23】:

                          变量,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
                          

                          【讨论】:

                            【解决方案24】:

                            我最喜欢@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 爱好者来说也很好:

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

                            【讨论】:

                              【解决方案25】:

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

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

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

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

                              【讨论】:

                              • 我也发现原始文件丢失了。 Ubuntu Lucid。
                              【解决方案26】:

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

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

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

                              【讨论】:

                                【解决方案27】:
                                current=`cat my_file` && echo 'my_string' > my_file && echo $current >> my_file
                                

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

                                【讨论】:

                                • 除非你想让一些“魔法”不时发生,否则应该引用变量替换。
                                【解决方案28】:

                                为什么不简单地使用 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
                                

                                当然,所有这些都可以放在一行中。

                                【讨论】:

                                  【解决方案29】:
                                  echo '0a
                                  your text here
                                  .
                                  w' | ed some_file
                                  

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

                                  【讨论】:

                                  • 或者,而不是硬编码要插入的文本:0r header.file
                                  • 既然问题要求一个班轮:echo -e '0a\nyour text here\n.\nw' | ed some_file
                                  【解决方案30】:

                                  呸!没有人愿意提及 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/>
                                  

                                  【讨论】:

                                  • tac 可以解决问题,但是您只是打印了 tac 的帮助...不是特别有用。
                                  • 在这里不是很有帮助——应该是:{ tac myfile; tac header; } | tac &gt; newfile &amp;&amp; mv newfile myfile
                                  猜你喜欢
                                  • 2021-12-18
                                  • 2014-05-24
                                  • 1970-01-01
                                  • 1970-01-01
                                  • 1970-01-01
                                  • 2014-08-13
                                  • 2015-03-05
                                  • 1970-01-01
                                  相关资源
                                  最近更新 更多