Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 4597406
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: May 21, 20262026-05-21T23:15:09+00:00 2026-05-21T23:15:09+00:00

What does the sed expression: G; s/\n/&&/; /^\([ ~-]*\n\).*\n\1/d; s/\n//; h; P do? Exactly

  • 0

What does the sed expression: G; s/\n/&&/; /^\([ ~-]*\n\).*\n\1/d; s/\n//; h; P do? Exactly what does it match and how does it match it?

It’s from todo.sh. In context:

archive()
{
    #defragment blank lines
    sed -i.bak -e '/./!d' "$TODO_FILE"                     ## delete all empty lines
    [ $TODOTXT_VERBOSE -gt 0 ] && grep "^x " "$TODO_FILE"  ## if verbose mode print completed tasks..
    grep "^x " "$TODO_FILE" >> "$DONE_FILE"                ## append completed tasks to $DONE_FILE
    sed -i.bak '/^x /d' "$TODO_FILE"                       ## delete completed tasks
    cp "$TODO_FILE" "$TMP_FILE"


    sed -n 'G; s/\n/&&/; /^\([ ~-]*\n\).*\n\1/d; s/\n//; h; P' "$TMP_FILE" > "$TODO_FILE"


    ## G;                       Add a newline
    ## s/\n/&&/;                Substitute newline with && (two newlines?)
    ## /^\([ ~-]*\n\).*\n\1/d;  Delete duplicate lines???
    ## s/\n//                   Remove newlines
    ## h                        Hold: copy pattern space to buffer
    ## P                        Print first line of pattern space
    if [ $TODOTXT_VERBOSE -gt 0 ]; then
    echo "TODO: $TODO_FILE archived."
    fi
}
  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-05-21T23:15:10+00:00Added an answer on May 21, 2026 at 11:15 pm

    Ok, you’ve got some of the story already. Recall that the sed expression is executed for each input line. So the G at the beginning appends the contents of the hold space to the current line (with a newline in between). The contents of the hold space is empty initially but expanded by the h command at the end of each input cycle.

    Then s/\n/&&/ duplicates the first newline only, the one between the current line and what was grabbed from the hold space. This is in preparation for the next command. /^\([ -~]*\n\).*\n\1/ indeed matches if the current line is identical to a line in the hold space:
        ^\([ -~]*\n\) matches a line at the beginning of the buffer¹
            Note that this matches only if the line contains only printable ASCII characters.
            If your system supports locales, ^\([[:print:]]*\n\) would be better.
        .*\n matches at least one subsequent line
        \1 matches a line identical to the first line
    The extra newline added by the previous s command takes care of the case when the duplicate is the very first line from the hold space. The point of the \n\1 is to “anchor” the duplicate at the beginning of a line, otherwise bar would be considered a duplicate of foobar. If the current line is a duplicate, the d command discards it and execution branches to the next line.

    If the current line is not a duplicate, s/\n// discards that extra newline (again, no g modifier, so only the first newline is removed). Then the h command results in the hold space containing what it contained before, with the current line prepended. Finally P prints the current input line.

    Ok, now what does the hold space contain? It starts empty, then gets each successive line prepended unless it’s a duplicate. So the hold space contains the input lines, in reverse order, minus the duplicates.

    ¹ Uh, I don’t know how you did that, but that should be [ -~], not [ ~-] which wouldn’t make any sense.


    Here’s another way of doing this, if you have a POSIX-conforming set of tools (Single Unix v2 is good enough).

    <"$TMP_FILE" \
    nl -s: |              # add line numbers
    sort -t: -k2 -u |     # sort, ignoring the line numbers, and remove duplicates
    sort -t: -k1 -n |     # sort by line number
    cut -d: -f2-          # cut out the line numbers
    

    Oh, you wanted to do this legibly and concisely? Just use awk.

    <"$TMP_FILE" awk '!seen[$0] {++seen[$0]; print}'
    

    If the current line hasn’t been seen yet, mark it as seen, and print it.

    Note that like the sed method, the awk method essentially stores the whole file in memory. The method above using sort has the advantage that only sort needs to keep more than one line of input at a time, and it’s designed for this.

    Of course, if you don’t care about the order of the lines, it’s as simple as sort -u.

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

Why does this produce these results? C:\>(echo a && echo b) | sed 1!G;h;$p
I'm curious, why does sed need 3 \ just to recognize one? I'd understand
Does Google force employees who have offers from Facebook to leave immediately?
I'm writing a sed script part of which requires replacing characters from a certain
Does Jscrollpane wont work with Asp.Net as I am totally blank with a normal
I'm trying to understand $ echo reverse me \ | sed '/\n/!G;s/\(.\)\(.*\n\)/&\2\1/;//D;s/.//' em esrever
I am trying to spread one sed command over several lines in a bash
i'm trying to write a grep/sed/awk for a condition from a file, and at
Have noticed this a fewtimes when I'm piping something into sed which does not
I want to replace TAB s in stdout with semicolons, by running sed from

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.