Что делает это выражение sed из todo.sh? - PullRequest
4 голосов
/ 09 мая 2011

Что такое выражение 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
}

Ответы [ 2 ]

7 голосов
/ 09 мая 2011

Хорошо, у вас уже есть история.Напомним, что выражение sed выполняется для каждой строки ввода.Таким образом, G в начале добавляет содержимое области удержания к текущей строке (с новой строкой между).Содержимое пространства удержания изначально пустое, но расширяется командой h в конце каждого цикла ввода.

Затем s/\n/&&/ дублирует только первую новую строку, одну междутекущая строка и то, что было взято из пространства удержания.Это готовится к следующей команде./^\([ -~]*\n\).*\n\1/ действительно соответствует, если текущая строка идентична строке в пространстве удержания:
^\([ -~]*\n\) соответствует строке в начале буфера¹
Обратите внимание, что это соответствует только в том случае, если строка содержит толькопечатаемые символы ASCII.
Если ваша система поддерживает локали, лучше будет ^\([[:print:]]*\n\).
.*\n соответствует хотя бы одной последующей строке
\1 соответствует строкеидентичен первой строке
Дополнительная новая строка, добавленная предыдущей командой s, учитывает случай, когда дубликат является самой первой строкой из пространства удержания.Смысл \n\1 заключается в «закреплении» дубликата в начале строки, в противном случае bar будет считаться дубликатом foobar.Если текущая строка является дубликатом, команда d отбрасывает ее и выполнение переходит к следующей строке.

Если текущая строка не является дубликатом, s/\n// отбрасывает эту дополнительную новую строку (опять же, нет *Модификатор 1031 *, поэтому удаляется только первая новая строка).Затем команда 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

О, ты хотел сделать это разборчиво и кратко?Просто используйте awk.

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

Если текущая строка еще не видна, отметьте ее как видимую и распечатайте.

Обратите внимание, что, как и метод sed, метод awk по существухранит весь файл в памяти.Вышеописанный метод с использованием sort имеет то преимущество, что только sort необходимо хранить более одной строки ввода одновременно, и он предназначен для этого.

Конечно, если вам все равнопорядок строк, это так просто, как sort -u.

2 голосов
/ 09 мая 2011

После того, как Жиль представил свой превосходный ответ, я нашел Объяснения знаменитого Sed One-Liners , который включает в себя это точное выражение sed;добавив сюда для справки:

70.Удалите дублирующиеся непоследовательные строки из файла.

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

Это очень сложная однострочная строка.Он сохраняет уникальные строки в буфере удержания и при каждой новой прочитанной строке проверяет, находится ли новая строка в буфере удержания.Если это так, то новая строка очищается.Если это не так, то он сохраняется в буфере удержания для будущих тестов и печатается.

Более подробное описание - в каждой строке этот однострочник добавляет содержимое буфера удержания в пространство паттернов с помощью команды "G".Добавленная строка отделяется от существующего содержимого пространства шаблона символом "\ n".Затем выполняется замена, которая заменяет символ «\ n» двумя «\ n \ n».Команда замены "s / \ n / && /" делает это.«&» Означает соответствующую строку.Поскольку совпадающей строкой было «\ n», то «&&» является ее двумя копиями «\ n \ n».Затем выполняется проверка "/ ^ ([- ~] \ n). \ n \ 1 /", чтобы определить, повторяется ли содержимое группы захвата группы 1.Группа захвата 1 - это все символы от пробела "" до "~" (которые включают в себя все печатные символы).«[- ~] » соответствует этому.Замена одной "\ n" на две была ключевой идеей.Поскольку "([- ~] \ n)" является жадным (соответствует как можно большему количеству), двойной символ новой строки гарантирует, что он соответствует как можно меньшему количеству текста.Если тест пройден успешно, текущая строка ввода уже видна, и «d» удаляет все пространство шаблона и начинает выполнение скрипта с самого начала.Если тест не был успешным, дублируемый «\ n \ n» заменяется одной «\ n» командой «s / \ n //».Затем «h» копирует всю строку для хранения буфера, а «P» печатает новую строку.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...