How can I use a file in a command and redirect output to the same file without truncating it?

前端 未结 14 2787
终归单人心
终归单人心 2020-11-21 22:11

Basically I want to take as input text from a file, remove a line from that file, and send the output back to the same file. Something along these lines if that makes it any

相关标签:
14条回答
  • 2020-11-21 22:37

    There's also ed (as an alternative to sed -i):

    # cf. http://wiki.bash-hackers.org/howto/edit-ed
    printf '%s\n' H 'g/seg[0-9]\{1,\}\.[0-9]\{1\}/d' wq |  ed -s file_name
    
    0 讨论(0)
  • 2020-11-21 22:40

    You cannot do that because bash processes the redirections first, then executes the command. So by the time grep looks at file_name, it is already empty. You can use a temporary file though.

    #!/bin/sh
    tmpfile=$(mktemp)
    grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name > ${tmpfile}
    cat ${tmpfile} > file_name
    rm -f ${tmpfile}
    

    like that, consider using mktemp to create the tmpfile but note that it's not POSIX.

    0 讨论(0)
  • 2020-11-21 22:40

    You can't use redirection operator (> or >>) to the same file, because it has a higher precedence and it will create/truncate the file before the command is even invoked. To avoid that, you should use appropriate tools such as tee, sponge, sed -i or any other tool which can write results to the file (e.g. sort file -o file).

    Basically redirecting input to the same original file doesn't make sense and you should use appropriate in-place editors for that, for example Ex editor (part of Vim):

    ex '+g/seg[0-9]\{1,\}\.[0-9]\{1\}/d' -scwq file_name
    

    where:

    • '+cmd'/-c - run any Ex/Vim command
    • g/pattern/d - remove lines matching a pattern using global (help :g)
    • -s - silent mode (man ex)
    • -c wq - execute :write and :quit commands

    You may use sed to achieve the same (as already shown in other answers), however in-place (-i) is non-standard FreeBSD extension (may work differently between Unix/Linux) and basically it's a stream editor, not a file editor. See: Does Ex mode have any practical use?

    0 讨论(0)
  • 2020-11-21 22:42

    I usually use the tee program to do this:

    grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name | tee file_name
    

    It creates and removes a tempfile by itself.

    0 讨论(0)
  • 2020-11-21 22:47

    The following will accomplish the same thing that sponge does, without requiring moreutils:

        shuf --output=file --random-source=/dev/zero 
    

    The --random-source=/dev/zero part tricks shuf into doing its thing without doing any shuffling at all, so it will buffer your input without altering it.

    However, it is true that using a temporary file is best, for performance reasons. So, here is a function that I have written that will do that for you in a generalized way:

    # Pipes a file into a command, and pipes the output of that command
    # back into the same file, ensuring that the file is not truncated.
    # Parameters:
    #    $1: the file.
    #    $2: the command. (With $3... being its arguments.)
    # See https://stackoverflow.com/a/55655338/773113
    
    function siphon
    {
        local tmp=$(mktemp)
        local file="$1"
        shift
        $* < "$file" > "$tmp"
        mv "$tmp" "$file"
    }
    
    0 讨论(0)
  • 2020-11-21 22:48

    try this simple one

    grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name | tee file_name
    

    Your file will not be blank this time :) and your output is also printed to your terminal.

    0 讨论(0)
提交回复
热议问题