Подтвердить что ты не робот

Удаление трейлинга/запуск новых строк с помощью sed, awk, tr и друзей

Я хотел бы удалить все пустые строки из файла, но только когда они находятся в конце/начале файла (то есть, если перед ним нет непустых строк, в начале; если после них нет непустых строк, в конце.)

Возможно ли это за пределами полнофункционального языка сценариев, такого как Perl или Ruby? Id предпочитает делать это с помощью sed или awk, если это возможно. В принципе, любой легкий и широко доступный инструмент UNIX-y будет в порядке, особенно тот, который я могу узнать о нем быстрее (Perl, следовательно, не включен).

4b9b3361

Ответ 1

От Полезные однострочные скрипты для sed:

# Delete all leading blank lines at top of file (only).
sed '/./,$!d' file

# Delete all trailing blank lines at end of file (only).
sed -e :a -e '/^\n*$/{$d;N;};/\n$/ba' file

Поэтому, чтобы удалить как ведущие, так и завершающие пустые строки из файла, вы можете объединить приведенные выше команды в:

sed -e :a -e '/./,$!d;/^\n*$/{$d;N;};/\n$/ba' file

Ответ 2

Итак, я собираюсь взять часть ответа @dogbane для этого, так как эта строка sed для удаления ведущих пустых строк настолько коротка...

tac является частью coreutils и меняет направление файла. Сделайте это дважды:

tac file | sed -e '/./,$!d' | tac | sed -e '/./,$!d'

Это, конечно, не самый эффективный, но если вам не нужна эффективность, я считаю его более читаемым, чем все остальное.

Ответ 3

здесь однопроходное решение в awk: он не запускает печать до тех пор, пока не увидит непустую строку, и когда она увидит пустую строку, она запоминает ее до следующей непустой строки

awk '
    /[[:graph:]]/ {
        # a non-empty line
        # set the flag to begin printing lines
        p=1      
        # print the accumulated "interior" empty lines 
        for (i=1; i<=n; i++) print ""
        n=0
        # then print this line
        print
    }
    p && /^[[:space:]]*$/ {
        # a potentially "interior" empty line. remember it.
        n++
    }
' filename

Обратите внимание, что из-за механизма, который я использую для рассмотрения пустых/непустых строк (с [[:graph:]] и /^[[:space:]]*$/), внутренние строки только с пробелами будут усечены, чтобы стать действительно пустыми.

Ответ 4

с помощью awk:

awk '{a[NR]=$0;if($0 && !s)s=NR;}
    END{e=NR;
        for(i=NR;i>1;i--) 
            if(a[i]){ e=i; break; } 
        for(i=s;i<=e;i++)
            print a[i];}' yourFile

Ответ 5

Как упоминалось в другом ответе, tac является частью coreutils и меняет направление на файл. Объединив идею сделать это дважды с тем фактом, что подстановка команд будет пресекать новые строки, мы получим

echo "$(echo "$(tac "$filename")" | tac)"

которая не зависит от sed. Вы можете использовать echo -n, чтобы удалить оставшуюся конечную новую строку.

Ответ 6

Здесь адаптированная версия sed, которая также считает "пустыми" эти строки только пробелами и вкладками на ней.

sed -e :a -e '/[^[:blank:]]/,$!d; /^[[:space:]]*$/{ $d; N; ba' -e '}'

В основном это принятая версия ответа (учитывая комментарий BryanH), но точка . в первой команде была изменена на [^[:blank:]] (ничего не пустое), а \n внутри второго адреса команды было изменено на [[:space:]], чтобы разрешить символы новой строки, пробелы в закладках.

Альтернативная версия без использования классов POSIX, но ваш sed должен поддерживать вставку \t и \n внутри […]. GNU sed делает, BSD sed не делает.

sed -e :a -e '/[^\t ]/,$!d; /^[\n\t ]*$/{ $d; N; ba' -e '}'

Тестирование:

prompt$ printf '\n \t \n\nfoo\n\nfoo\n\n \t \n\n' 



foo

foo



prompt$ printf '\n \t \n\nfoo\n\nfoo\n\n \t \n\n' | sed -n l
$
 \t $
$
foo$
$
foo$
$
 \t $
$
prompt$ printf '\n \t \n\nfoo\n\nfoo\n\n \t \n\n' | sed -e :a -e '/[^[:blank:]]/,$!d; /^[[:space:]]*$/{ $d; N; ba' -e '}'
foo

foo
prompt$

Ответ 7

В bash, используя cat, wc, grep, sed, tail и head:

# number of first line that contains non-empty character
i=`grep -n "^[^\B*]" <your_file> | sed -e 's/:.*//' | head -1`
# number of hte last one
j=`grep -n "^[^\B*]" <your_file> | sed -e 's/:.*//' | tail -1`
# overall number of lines:
k=`cat <your_file> | wc -l`
# how much empty lines at the end of file we have?
m=$(($k-$j))
# let strip last m lines!
cat <your_file> | head -n-$m
# now we have to strip first i lines and we are done 8-)
cat <your_file> | tail -n+$i

Человек, безусловно, стоит изучить "настоящий" язык программирования, чтобы избежать этого безобразия!

Ответ 8

Используя bash

$ filecontent=$(<file)
$ echo "${filecontent/$'\n'}"

Ответ 9

Для эффективной нерекурсивной версии конечной строки новой строки (включая "белые" символы) я разработал этот sed script.

sed -n '/^[[:space:]]*$/ !{x;/\n/{s/^\n//;p;s/.*//;};x;p;}; /^[[:space:]]*$/H'

Он использует буфер удержания для хранения всех пустых строк и распечатывает их только после того, как находит непустую строку. Если кто-то хочет только новые строки, этого достаточно, чтобы избавиться от двух частей [[:space:]]*:

sed -n '/^$/ !{x;/\n/{s/^\n//;p;s/.*//;};x;p;}; /^$/H'

Я пробовал простое сравнение производительности с хорошо известным рекурсивным script

sed -e :a -e '/^\n*$/{$d;N;};/\n$/ba'

в 3 МБ файле с 1 МБ случайных пустых строк вокруг случайного текста base64.

shuf -re 1 2 3 | tr -d "\n" | tr 123 " \t\n" | dd bs=1 count=1M > bigfile
base64 </dev/urandom | dd bs=1 count=1M >> bigfile
shuf -re 1 2 3 | tr -d "\n" | tr 123 " \t\n" | dd bs=1 count=1M >> bigfile

Потоковая передача script заняла примерно 0,5 секунды для завершения, рекурсивный не закончился через 15 минут. Win:)

Для полноты ответа, ведущие линии, разделяющие sed script, уже отлично передают потоки. Используйте наиболее подходящий для вас вариант.

sed '/[^[:blank:]]/,$!d'
sed '/./,$!d'

Ответ 10

A bash решение.

Примечание. Только полезно , если файл достаточно мал для чтения в память сразу.

[[ $(<file) =~ ^$'\n'*(.*)$ ]] && echo "${BASH_REMATCH[1]}"
  • $(<file) читает весь файл и обрезает последние строки, потому что подстановка подстановки ($(....)) неявно делает это.
  • =~ является bash оператором сопоставления регулярных выражений, а =~ ^$'\n'*(.*)$ необязательно совпадает с любыми ведущими символами новой строки (жадно) и фиксирует все, что приходит после. Обратите внимание на потенциально запутывающий $'\n', который вставляет литеральную новую строку, используя цитирование ANSI C, поскольку escape-последовательность \n не поддерживается.
  • Обратите внимание, что это конкретное регулярное выражение всегда совпадает, поэтому всегда выполняется команда после &&.
  • Специальная переменная массива BASH_REMATCH rematch содержит результаты самого последнего совпадения в регулярном выражении, а элемент массива [1] содержит то, что захвачено (первое и единственное) вложенное подвыражение (группа захвата), которое является входной строкой с любым ведущим новые строки лишены. Чистый эффект заключается в том, что ${BASH_REMATCH[1]} содержит содержимое входного файла с разделенными как верхними, так и конечными символами новой строки.
  • Обратите внимание, что печать с помощью echo добавляет одну конечную новую строку. Если вы хотите этого избежать, используйте echo -n вместо (или используйте более портативный printf '%s').

Ответ 11

Я хотел бы ввести еще один вариант для gawk v4.1 +

result=($(gawk '
    BEGIN {
        lines_count         = 0;
        empty_lines_in_head = 0;
        empty_lines_in_tail = 0;
    }
    /[^[:space:]]/ {
        found_not_empty_line = 1;
        empty_lines_in_tail  = 0;
    }
    /^[[:space:]]*?$/ {
        if ( found_not_empty_line ) {
            empty_lines_in_tail ++;
        } else {
            empty_lines_in_head ++;
        }
    }
    {
        lines_count ++;
    }
    END {
        print (empty_lines_in_head " " empty_lines_in_tail " " lines_count);
    }
' "$file"))

empty_lines_in_head=${result[0]}
empty_lines_in_tail=${result[1]}
lines_count=${result[2]}

if [ $empty_lines_in_head -gt 0 ] || [ $empty_lines_in_tail -gt 0 ]; then
    echo "Removing whitespace from \"$file\""
    eval "gawk -i inplace '
        {
            if ( NR > $empty_lines_in_head && NR <= $(($lines_count - $empty_lines_in_tail)) ) {
                print
            }
        }
    ' \"$file\""
fi

Ответ 12

@dogbane имеет простой простой ответ для удаления ведущих пустых строк. Здесь простая команда awk, которая удаляет только завершающие строки. Используйте это с командой @dogbane sed, чтобы удалить как ведущие, так и завершающие пробелы.

awk '{ LINES=LINES $0 "\n"; } /./ { printf "%s", LINES; LINES=""; }'

Это довольно просто в работе.

  • Добавьте каждую строку в буфер, когда мы ее прочитаем.
  • Для каждой строки, содержащей символ, распечатайте содержимое буфера, а затем очистите его.

Таким образом, единственные вещи, которые буферизуются и никогда не отображаются, - это любые завершающие пробелы.

Я использовал printf вместо печати, чтобы избежать автоматического добавления новой строки, поскольку я использую символы новой строки для разделения строк в буфере уже.

Ответ 13

Этот сценарий AWK поможет:

BEGIN {
    ne=0;
}

/^[[:space:]]*$/ {
    ne++;
}

/[^[:space:]]+/ {
    for(i=0; i < ne; i++)
        print "";
    ne=0;
    print
}

Идея проста: пустые строки не отражаются сразу. Вместо этого мы ждем, пока не получим непустую строку, и только затем мы сначала выводим столько пустых строк, сколько было видно перед ней, и только затем выводим новую непустую строку.

Ответ 14

perl -0pe 's/^\n+|\n+(\n)$/\1/gs'