这个来自todo.sh的sed表达式是做什么的?

Lef*_*ium 4 regex shell sed

sed表达式G; s/\n/&&/; /^\([ ~-]*\n\).*\n\1/d; s/\n//; h; P是什么:做什么?究竟它匹配什么,它是如何匹配的?

这是从todo.sh.在上下文中:

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
}
Run Code Online (Sandbox Code Playgroud)

Gil*_*il' 7

好的,你已经有了一些故事.回想一下,为每个输入行执行sed表达式.因此,G在开头将保留空间的内容附加到当前行(中间有换行符).保持空间的内容最初为空,但h在每个输入周期结束时由命令扩展.

然后仅s/\n/&&/复制第一个换行符,即当前行与从保留空间中抓取的换行符之间的换行符.这是为下一个命令做准备./^\([ -~]*\n\).*\n\1/如果当前行与保留空间
    ^\([ -~]*\n\)中的行相同,则确实匹配:匹配缓冲区开头的行¹
        请注意,仅当行仅包含可打印的ASCII字符时才匹配.
        如果您的系统支持区域设置,^\([[:print:]]*\n\)那就更好了.
    .*\n匹配至少一个后续行
    \1匹配与第一行相同的行
.上一个s命令添加的额外换行处理当副本是保留空间的第一行时的情况.要点\n\1是在一行的开头"锚定"副本,否则bar将被视为副本foobar.如果当前行是重复行,则该d命令将丢弃它并执行分支到下一行.

如果当前行不是重复行,则s/\n//丢弃该额外换行符(同样,没有g修饰符,因此仅删除第一个换行符).然后该h命令导致保持空间包含之前包含的内容,并且当前行前置.最后P打印当前输入行.

好的,现在保持空间包含什么?它开始为空,然后将每个连续的行前置,除非它是重复的.因此,保持空间包含输入行,按相反顺序减去重复项.

¹ 呃,我不知道你是怎么做到的,但那应该是[ -~],不是[ ~-]没有任何意义的.


如果你有一套符合POSIX标准的工具(Single Unix v2足够好),这是另一种方法.

<"$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
Run Code Online (Sandbox Code Playgroud)

哦,你想要清晰简洁地做到这一点?只需使用awk.

<"$TMP_FILE" awk '!seen[$0] {++seen[$0]; print}'
Run Code Online (Sandbox Code Playgroud)

如果尚未显示当前行,请将其标记为已显示,然后将其打印出来.

请注意,与sed方法一样,awk方法实质上将整个文件存储在内存中.上面使用的方法sort具有以下优点:一次只sort需要保留多行输入,并且它是为此而设计的.

当然,如果你不关心线的顺序,那就简单了sort -u.