Удаление трейлинга/запуск новых строк с помощью sed, awk, tr и друзей
Я хотел бы удалить все пустые строки из файла, но только когда они находятся в конце/начале файла (то есть, если перед ним нет непустых строк, в начале; если после них нет непустых строк, в конце.)
Возможно ли это за пределами полнофункционального языка сценариев, такого как Perl или Ruby? Id предпочитает делать это с помощью sed
или awk
, если это возможно. В принципе, любой легкий и широко доступный инструмент UNIX-y будет в порядке, особенно тот, который я могу узнать о нем быстрее (Perl, следовательно, не включен).
Ответы
Ответ 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'