Хорошо, у вас уже есть история.Напомним, что выражение 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
.