代码之家  ›  专栏  ›  技术社区  ›  elmarco

在一个文件前加一个衬板

  •  123
  • elmarco  · 技术社区  · 16 年前

    这可能是 a complex solution .

    我正在寻找一个简单的运算符,如“>>”,但用于前置。

    恐怕它不存在。我得做点什么

     mv myfile tmp
     cat myheader tmp > myfile
    

    有更聪明的吗?

    32 回复  |  直到 7 年前
        1
  •  26
  •   John Mee    9 年前

    这个 乱劈 下面是一个快速的即兴回答,它起到了作用,并收到了很多赞成票。然后,随着这个问题越来越受欢迎,时间越来越长,愤怒的人们开始报告说这个问题有点奏效,但可能会发生一些奇怪的事情,或者它根本不起作用,所以有一段时间它被猛烈地否决了。如此有趣。

    该解决方案充分利用了系统上文件描述符的精确实现,由于实现在nixes之间的差异很大,因此它的成功完全依赖于系统,最终是不可移植的,不应该依赖于任何甚至是模糊的重要内容。

    现在,所有这些都不可能,答案是:


    为文件创建另一个文件描述符( 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
    
        2
  •  93
  •   Zombo    11 年前

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

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

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

        3
  •  28
  •   fluffle    14 年前
    echo '0a
    your text here
    .
    w' | ed some_file
    

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

        4
  •  20
  •   palacsint    13 年前

    John Mee:您的方法不一定能工作,如果您预先准备了4096字节以上的内容,那么它可能会失败(至少GNUawk会发生这种情况,但我认为其他实现也会有类似的约束)。在这种情况下,它不仅会失败,而且会进入一个无休止的循环,在那里它会读取自己的输出,从而使文件增长,直到所有可用空间都被填满。

    自己试试:

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

    (警告:过一会儿将其杀死,否则将填充文件系统)

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

        5
  •  15
  •   Vinko Vrsalovic    16 年前

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

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

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

        6
  •  15
  •   Eric Hansander Joe Harris    16 年前

    值得注意的是,它通常是 good idea 使用类似这样的工具安全地生成临时文件 mktemp ,至少如果脚本将以根权限执行。例如,您可以执行以下操作(同样在bash中):

    (tmpfile=`mktemp` && { echo "prepended text" | cat - yourfile > $tmpfile && mv $tmpfile yourfile; } )
    
        7
  •  13
  •   user2227573    11 年前

    如果您在控制的计算机上需要这个,请安装“moreutils”包并使用“sponce”。然后你可以做:

    cat header myfile | sponge myfile
    
        8
  •  11
  •   Eric Woodruff    9 年前

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

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

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

        9
  •  9
  •   cb0    15 年前

    假设要编辑的文件是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
  •  9
  •   balki    13 年前

    当您开始尝试在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?)方式…

        11
  •  8
  •   Community holdenweb    7 年前

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

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

    cat header main | tee main > /dev/null
    
        12
  •  7
  •   Max Tsepkov    12 年前

    就像丹尼尔·维尔科夫建议的那样,使用T恤。
    对我来说,这是一个简单的智能解决方案:

    { echo foo; cat bar; } | tee bar > /dev/null
    
        13
  •  3
  •   nemisj    13 年前

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

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

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

        14
  •  3
  •   benjwadams    11 年前

    主要是为了好玩/玩空壳高尔夫,但是

    ex -c '0r myheader|x' myfile
    

    这样就可以了,而且没有管道或重定向。当然,vi/ex并不是真正的非交互使用,所以vi将短暂地更新。

        15
  •  2
  •   Community holdenweb    7 年前

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

    另一个解决方法是使用jrgen htzel在 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
    

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

        16
  •  2
  •   Alberto A. Medina    13 年前

    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_u be modified_cat>tmp_file;mv tmp_file_u to_u be_modified

        17
  •  2
  •   Aziz Shaikh    13 年前

    以下是我发现的:

    echo -e "header \n$(cat file)" >file
    
        18
  •  2
  •   weakish    12 年前
    sed -i -e '1rmyheader' -e '1{h;d}' -e '2{x;G}' myfile
    
        19
  •  2
  •   Community holdenweb    7 年前

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

    有一种方法可以让@shixilun的SED方法发挥作用,尽管他有疑虑。在将文件读取到SED替换字符串(例如,用'\n'替换换行符)时,必须有bash命令来转义空白。外壳命令 vis cat 可以处理不可打印的字符,但不能处理空白字符,因此这不会解决OP的问题:

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

    失败的原因是替换脚本中的原始换行符,需要在换行符前面加上一个换行符(),然后可能加上一个&来保持shell和sed的快乐,例如 this SO answer

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

        20
  •  1
  •   Zombo    11 年前
    sed -i -e "1s/^/new first line\n/" old_file.txt
    
        21
  •  1
  •   JuSchu matthewrdev    10 年前

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

    originalContent=$(cat targetfile) && echo "text to prepend" > targetfile && echo "$originalContent" >> targetfile
    
        22
  •  1
  •   crizCraig    10 年前

    如果您有一个大文件(在我的例子中是几百千字节)并且可以访问python,那么这比 cat 管道解决方案:

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

        23
  •  1
  •   Alan H.    9 年前

    解决方案 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}"
    

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

        24
  •  1
  •   user5704481    8 年前

    您可以使用perl命令行:

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

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

    或者如果我的头是一个文件:

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

    其中/e允许在替换中对代码进行eval。

        25
  •  0
  •   glenn jackman    13 年前
    current=`cat my_file` && echo 'my_string' > my_file && echo $current >> my_file
    

    其中,“我的_文件”是要在“我的_字符串”之前添加的文件。

        26
  •  0
  •   Community holdenweb    7 年前

    我喜欢 @fluffle's ed approach 最好的。毕竟,任何工具的命令行开关和脚本编辑器命令在这里基本上都是相同的;没有看到脚本编辑器解决方案的“清洁度”有任何下降或下降。

    这是我的一行附加到 .git/hooks/prepare-commit-msg 提前回购 .gitmessage 要提交消息的文件:

    echo -e "1r $PWD/.gitmessage\n.\nw" | ed -s "$1"
    

    例子 GIT消息 :

    # Commit message formatting samples:
    #       runlevels: boot +consolekit -zfs-fuse
    #
    

    我正在做 1r 而不是 0r ,因为这将使空的“准备写入”行保留在原始模板文件的顶部。不要在你的 GIT消息 然后,您将以两个空行结束。 -s 抑制ED的诊断信息输出。

    关于经历这件事,我 discovered 对于vim buffs来说,最好有:

    [core]
            editor = vim -c ':normal gg'
    
        27
  •  0
  •   Jayen    13 年前

    变量,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
  •   Dave Butler    12 年前

    我认为这是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
    
        29
  •  0
  •   Tim Kennedy    11 年前

    如果您是在bash中编写脚本,实际上,您可以发布:

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

    这实际上是你在自己的问题中发布的一个复杂的例子。

        30
  •  0
  •   Community holdenweb    7 年前

    imho没有一个shell解决方案(而且永远不会是这样的解决方案),无论两个文件的大小如何,它都能始终如一地可靠地工作。 myheader myfile . 原因是,如果您希望在不重复出现临时文件的情况下执行此操作(并且不让shell静默地重复出现临时文件,例如通过类似 exec 3<>myfile 管道 tee 等。

    您正在寻找的“真正的”解决方案需要处理文件系统,因此它在用户空间中不可用,并且依赖于平台:您要求修改使用中的文件系统指针 我的档案 到文件系统指针的当前值 MyHead 在文件系统中替换 EOF 属于 MyHead 链接到当前文件系统地址 我的档案 . 这不是小事,显然不能由非超级用户完成,也可能不能由超级用户完成。玩inode等。

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