【问题标题】:Unix command to prepend text to a file将文本添加到文件的 Unix 命令
【发布时间】:2012-05-22 04:34:04
【问题描述】:

是否有 Unix 命令可以将一些字符串数据添加到文本文件中?

类似:

prepend "to be prepended" text.txt

【问题讨论】:

  • 您是在寻找单个命令,还是小脚本/别名命令可以?
  • 如果你结合所有的答案,这可能是最紧凑的方式:<<(echo "to be prepended") < text.txt | sponge text.txt
  • 这个问题真的激发了很多人的创造力!

标签: unix command-line utilities prepend


【解决方案1】:

可能没有任何内置功能,但您可以很容易地编写自己的内容,如下所示:

#!/bin/bash
echo -n "$1" > /tmp/tmpfile.$$
cat "$2" >> /tmp/tmpfile.$$
mv /tmp/tmpfile.$$ "$2"

至少是这样的……

【讨论】:

  • +1 用于使用 $$(当前 PID)作为临时文件名的一部分。如果创建一般用途的脚本,这将防止其他用户通过同时运行相同的脚本来覆盖临时文件。
  • 使用$$ 不足以用于生产代码,但是(谷歌符号链接攻击);但它肯定比静态文件名好。
  • 只需使用mktemp
【解决方案2】:

这是一种可能性:

(echo "to be prepended"; cat text.txt) > newfile.txt

您可能不会轻易绕过中间文件。

替代方案(外壳转义可能很麻烦):

sed -i '0,/^/s//to be prepended/' text.txt

【讨论】:

  • 我觉得 策略 1) 是这里所有答案中最直观的。还有一点点变化:cat <(echo "to be prepended") text.txt > newfile.txt 。想一想,我不确定我的是否相关,所以我发布了一个单独的答案。
  • 第一种方法不会保留文件权限
  • 保留权限的唯一方法是使用海绵
  • 不需要使用( ... ) 生成子shell:shell 完全可以重定向组命令{ ... } 的输出。警告:shell 语法要求在 } 之前使用终止分号或换行符,但在 ) 之前不需要。
【解决方案3】:

这将用于形成输出。 - 表示标准输入,通过管道从 echo 提供。

echo -e "to be prepended \n another line" | cat - text.txt

要重写文件,需要一个临时文件,因为无法通过管道返回输入文件。

echo "to be prepended" | cat - text.txt > text.txt.tmp
mv text.txt.tmp text.txt

【讨论】:

  • 这不会按照要求将文本添加到文件text.txt,而是将其显示在标准输出上。如果适用于 OP,则输出可以汇集到不同的文件中
  • 这肯定会打印“to be prepend”,然后是“text.txt”的内容。但我希望将文本添加到文件中
  • 如果我有多行文本怎么办?如何在其中放置换行符?
  • 这很酷,但 bash 不喜欢它: $ echo RewriteBase / | cat - web/.htaccess > web/.htaccess cat: web/.htaccess: 输入文件是输出文件
【解决方案4】:
sed -i.old '1s;^;to be prepended;' inFile
  • -i 将更改写入原处,并在提供任何扩展名时进行备份。 (在这种情况下,.old
  • 1s;^;to be prepended; 将第一行的开头替换为给定的替换字符串,使用 ; 作为命令分隔符。

【讨论】:

  • 如果您可以在命令中添加一些解释,这将对不太熟悉 sed 工作原理的其他人有所帮助。 OP 可能希望在前置后插入\n;取决于他们的需要。整洁的解决方案。
  • 是的,一个解释会很棒。 inFile 可以在其路径中包含目录吗?
  • @Levon 我完全想插入一个\n。应该先阅读cmets。该死的。
  • 就我而言,我想在前置行的末尾有一个分号,所以我选择了'1s;^;my-prepended-line\;\n;'
  • 请注意,'\n' 仅适用于 GNU sed,不适用于 BSD(例如 OSX、FreeBSD 等)。为了更便于携带,请参阅:stackoverflow.com/questions/1421478/…
【解决方案5】:

使用sed的另一种方式:

sed -i.old '1 {i to be prepended
}' inFile

如果要添加的行是多行:

sed -i.old '1 {i\ 
to be prepended\
multiline
}' inFile

【讨论】:

  • 为什么不 sed -i '1ito be prepended' inFile - 或者它只允许用于 GNU sed?
  • @userunknown:在标准sed 中,i 命令后跟一个反斜杠和一个换行符,并且除了最后一行之外的每一行输入也以反斜杠结尾。 GNU sed 允许按照您询问的方式使用速记,但只有 GNU sed 可以这样做。'
【解决方案6】:
printf '%s\n%s\n' "to be prepended" "$(cat text.txt)" >text.txt

【讨论】:

  • 在一行中,在原始文本文件中!这是正确的简单答案。我建议附加新行 \n echo -e "to be prepended\n$(cat text.txt)" > text.txt
  • 这个解决方案有问题......它将出现的“\n”转换为实际的新行......如果您在代码文件中添加包含“\n”的字符串,则会出现问题。
  • 我在我的文件git config --get-regex $arg | sed -r 's/\t{3}/\\n/g'; 中有这个,当它转换\t\n 时这会搞砸。
  • 这 (1) 将整个文件加载到内存中,(2) 将整个文件粘贴到单个命令行参数中。适合简单的东西,但要注意限制。
  • 上次我尝试过,我相信这会破坏非常大的文件,其中 cat 在覆盖之前无法读取 text.txt 的全部内容。如果 echo 上的 -e 选项有问题,您可以在 bash 中引用换行符或执行 echo "to be prepended"$'\n'"$(cat text.txt)"
【解决方案7】:

如果你喜欢 vi/vim,这可能更符合你的风格。

printf '0i\n%s\n.\nwq\n' prepend-text | ed file

【讨论】:

    【解决方案8】:
    # create a file with content..
    echo foo > /tmp/foo
    # prepend a line containing "jim" to the file
    sed -i "1s/^/jim\n/" /tmp/foo
    # verify the content of the file has the new line prepened to it
    cat /tmp/foo
    

    【讨论】:

      【解决方案9】:

      如果可以替换输入文件:

      注意:

      • 这样做可能会产生意想不到的副作用,特别是可能会用常规文件替换符号链接、最终获得文件的不同权限以及更改文件的创建(出生)日期。

      • sed -iPrince John Wesley's answer 一样,会尝试至少恢复原始权限,但其他限制也适用。

      这是一个使用临时文件的简单替代方案(它避免像shime's solution 那样将整个输入文件读入内存):

      { printf 'to be prepended'; cat text.txt; } > tmp.txt && mv tmp.txt text.txt
      

      使用 group 命令 ({ ...; ...; }) 比使用 subshel​​l ((...; ...)) 稍微高效一些,例如 0xC0000022L's solution

      优点是:

      • 很容易控制新文本是否应直接添加到第一行或是否应作为新行插入(只需将\n 附加到printf 参数)。

      • sed 解决方案不同,它在输入文件为空(0 字节)时工作。


      sed solution 可以简化,如果其意图是在现有内容前添加一个或多个整行(假设输入文件非空):

      sedi 函数插入整行:

      使用 GNU sed:

      # Prepends 'to be prepended' *followed by a newline*, i.e. inserts a new line.
      # To prepend multiple lines, use '\n' as part of the text.
      # -i.old creates a backup of the input file with extension '.old'
      sed -i.old '1 i\to be prepended' inFile
      

      一个也适用于 macOS / BSD sed 的便携式变体:

      # Prepends 'to be prepended' *followed by a newline*
      # To prepend multiple lines, escape the ends of intermediate
      # lines with '\'
      sed -i.old -e '1 i\
      to be prepended' inFile
      

      请注意,\ 后面的文字换行符是必需的。


      如果输入文件必须在原地编辑(保留其所有属性的inode):

      使用可敬的ed POSIX utility

      注意:

      • ed 总是首先将输入文件作为一个整体读入内存。

      直接添加到第一行(与sed 一样,如果输入文件完全为空(0 字节),这将不起作用):

      ed -s text.txt <<EOF
      1 s/^/to be prepended/
      w
      EOF
      
      • -s 抑制了 ed 的状态消息。
      • 注意命令是如何作为多行here-document (&lt;&lt;EOF\n...\nEOF) 提供给ed 的,即通过stdin;默认情况下,字符串扩展 is 在此类文档中执行(插入 shell 变量); 引用开头的分隔符来抑制它(例如,&lt;&lt;'EOF')。
      • 1 将第一行设为当前行
      • 函数s对当前行执行基于正则表达式的字符串替换,如sed;您可以在替换文本中包含 literal 换行符,但它们必须是 \-escaped。
      • w 将结果写回输入文件(为了测试,将w 替换为,p 以仅打印结果,而不修改输入文件)。

      添加一个或多个整行

      sed 一样,i 函数总是在要插入的文本中添加一个尾随换行符。

      ed -s text.txt <<EOF
      0 i
      line 1
      line 2
      .
      w
      EOF
      
      • 0 i 使0(文件的开头)成为当前行并开始插入模式(i);请注意,行号是基于 1 的。
      • 以下几行是插入之前当前行的文本,以. 结束。

      【讨论】:

        【解决方案10】:

        另一个相当直接的解决方案是:

            $ echo -e "string\n" $(cat file)
        

        【讨论】:

        • 这对我有用...只是稍作修改,为了维护输入文件中的多行,您必须将其用引号括起来,如下所示: echo -e "string\n" "$ (cat ~/file.txt)" > ~/file.txt;
        • 在双引号中没有 cat 参数扩展是 downvote 的一个很好的理由。此代码将文件内容拆分为单词,并将每个单词作为单独的参数传递给echo。您丢失了原始参数边界,丢失了换行符/制表符/等,并且原始文本中的 \t\n 等字符串被制表符和换行符替换,而不是保持原样。
        【解决方案11】:

        更喜欢亚当的answer

        我们可以更轻松地使用sponge。现在我们不需要创建一个临时文件并通过

        重命名它
        echo -e "to be prepended \n another line" | cat - text.txt | sponge text.txt
        

        【讨论】:

        • 这是唯一适合我的解决方案。有趣的是我的服务器的名字是海绵宝宝。
        【解决方案12】:

        流程替换

        我很惊讶没有人提到这一点。

        cat <(echo "before") text.txt > newfile.txt
        

        这可以说比公认的答案更自然(打印某些内容并将其传递到替换命令中,这在字典上是违反直觉的)。

        ...劫持 ryan 上面所说的,使用 sponge 你不需要临时文件:

        sudo apt-get install moreutils
        <<(echo "to be prepended") < text.txt | sponge text.txt
        

        编辑:看起来这在 Bourne Shell /bin/sh 中不起作用


        此处为字符串(仅限 zsh)

        使用这里的字符串 - &lt;&lt;&lt;,你可以这样做:

        <<< "to be prepended" < text.txt | sponge text.txt
        

        【讨论】:

        • 这个答案是我的赢家。仅使用内置函数简单且可实现。干得好!
        • @PiersyP 我同意!这很有帮助,谢谢 Sridhar-Sarnobat。
        • 问题是有1个文件,不是2个文件。所以答案的第一行并没有真正起作用。最后一行可能有效(但它需要海绵)。
        • 海绵有什么作用?
        • 您的 &lt;&lt;(echo "to be prepended") &lt; text.txt&lt;&lt;&lt; "to be prepended" &lt; text.txt 结构在 bash 中不起作用;他们需要 zsh。
        【解决方案13】:

        编者注:

        • 如果输入文件碰巧大于系统的管道缓冲区大小(现在通常为 64 KB),此命令将导致数据丢失。有关详细信息,请参阅 cmets。

        在某些情况下,前置文本可能只能从标准输入获得。 那么这个组合就可以了。

        echo "to be prepended" | cat - text.txt | tee text.txt
        

        如果您想省略tee 输出,则追加&gt; /dev/null

        【讨论】:

        • 我真的很喜欢这个答案。这是一种非常干净和清晰的方法。使用 tee 是尝试将输出 cat 输出到输入文件的问题的自然解决方案。也没有重命名的技巧。比目前投票率最高的解决方案要好,因为它不会创建新文件。这基本上是您最接近的 >> 前置而不是附加。
        • 是否可以保证teecat 阅读之前不会破坏text.txt?我认为不会——这会使这个解决方案对文件的健康造成危险。
        • 我也喜欢这个答案的简单性。它适用于 Bourne shell,这对我的回答不正确
        • @JonathanLeffler 可能没有。我试过这段代码来测试:lenA=1000000; yes a | head -c $lenA &gt; a.txt; lenB=10000; b=$(yes b | head -c $lenB); echo "$b" | cat - a.txt | tee a.txt &gt; /dev/null。如果 lenA 是 1000000(1Mb 文件)并且 lenB 是 10000(10Kb 文本前置),则文件“a.txt”会被 20Kb 的“b”字母覆盖。这完全被打破了。现在,如果您使用 1Mb a.txt 和 1Mb 文本作为前缀,tee 进入生成 7Gb+ 文件的循环,我不得不停止该命令。因此,很明显,对于大尺寸,结果是不可预测的。我不知道它是否适用于小尺寸。
        • 从概念上讲:如果输入文件碰巧大于系统的管道缓冲区大小,此命令将导致数据丢失,现在通常为 64 KB .
        【解决方案14】:

        解决方案:

        printf '%s\n%s' 'text to prepend' "$(cat file.txt)" > file.txt
        

        请注意,这对所有类型的输入都是安全的,因为没有扩展。例如,如果你想在前面加上!@#$%^&amp;*()ugly text\n\t\n,它就可以了:

        printf '%s\n%s' '!@#$%^&*()ugly text\n\t\n' "$(cat file.txt)" > file.txt
        

        最后要考虑的部分是在命令替换"$(cat file.txt)" 期间在文件末尾删除空格。对此的所有解决方法都相对复杂。如果您想在 file.txt 的末尾保留换行符,请参阅:https://stackoverflow.com/a/22607352/1091436

        【讨论】:

        • 爱它。超级便携,不需要生成新文件。谢谢!
        • 唯一的问题是file.txt 的内容大于printf 的参数列表。
        【解决方案15】:

        我建议定义一个函数,然后在需要的地方导入和使用它。

        prepend_to_file() { 
            file=$1
            text=$2
        
            if ! [[ -f $file ]] then
                touch $file
            fi
        
            echo "$text" | cat - $file > $file.new
        
            mv -f $file.new $file
        }
        

        然后像这样使用它:

        prepend_to_file test.txt "This is first"
        prepend_to_file test.txt "This is second"
        

        您的文件内容将是:

        This is second
        This is first
        

        我将使用这种方法来实现更改日志更新器。

        【讨论】:

          【解决方案16】:

          如在 Bash(在 Ubuntu 中)中测试过,如果从测试文件通过;

          echo "Original Line" &gt; test_file.txt

          你可以执行;

          echo "$(echo "New Line"; cat test_file.txt)" &gt; test_file.txt

          或者,如果bash的版本对于$()来说太旧了,你可以使用反引号;

          echo "`echo "New Line"; cat test_file.txt`" &gt; test_file.txt

          并接收“test_file.txt”的以下内容;

          New Line
          Original Line
          

          没有中间文件,只有 bash/echo。

          【讨论】:

          • 最佳答案,我用这个答案写了这个单行来旋转我的文件:for i in $( file.txt;完成;
          • 最佳答案。您可能想使用 '>|'运算符(这不是管道...),即使外壳设置了“noclobber”,也可以覆盖文本文件。所以: echo "$(echo "New Line"; cat test_file.txt)" >| test_file.txt
          【解决方案17】:

          你可以用 awk 轻松做到这一点

          cat text.txt|awk '{print "to be prepended"$0}'
          

          【讨论】:

            【解决方案18】:

            对于希望附加一行或多行文本(带有变量甚至是 subshel​​l 代码)并保持其可读性和格式化的未来读者,您可能会喜欢这个:

            echo "Lonely string" > my-file.txt
            

            然后运行

            cat <<EOF > my-file.txt
            Hello, there!
            
            $(cat my-file.txt)
            EOF
            

            cat my-file.txt的结果:

            Hello, there!
            
            Lonely string
            

            这是有效的,因为my-file.txt 的读取首先发生在子shell 中。我一直使用这个技巧将重要规则附加到 Docker 容器中的配置文件中,而不是复制整个配置文件。

            【讨论】:

            • 我最喜欢的答案
            【解决方案19】:
            % echo blaha > blaha
            % echo fizz > fizz
            % cat blaha fizz > buzz
            % cat buzz 
            blaha
            fizz
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2013-01-30
              • 1970-01-01
              • 2022-08-15
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2019-08-20
              相关资源
              最近更新 更多