Я хочу добраться, удаляют все пустые строки и строки с пробелами (если кто-либо существует (ТОЛЬКО от нижней части файла)) и затем удалить еще одну строку (также ТОЛЬКО от нижней части файла).
У меня есть этот код:
while [[ "$last_line" =~ $ ]] || [[ "$last_line" =~ ^[[:space:]]+$ ]]
do
sed -i -e '${/$/d}' "./file.txt"
done
sed -i -e '${/$/d}' "./file.txt"
По некоторым причинам цикл не останавливается, и он удаляет все в файле.В чем дело?
Ваш сценарий должен работать, если зафиксировано как так:
while
last_line=$(tail -1 "./file.txt")
[[ "$last_line" =~ ^$ ]] || [[ "$last_line" =~ ^[[:space:]]+$ ]]
do
sed -i '$d' "./file.txt"
done
Ваш сценарий имел две основных проблемы: (1) Вы никогда не обновляли $last_line
, таким образом, защита цикла будет всегда оценивать то же самое; (2) Ваш [[ "$last_line" =~ $ ]]
тест соответствовал любой строка, так как любая строка имеет конец. (Это - причина, почему Ваш сценарий освободил Ваш файл полностью.) Вы, вероятно, хотите соответствовать против ^$
вместо этого, который соответствует только пустым строкам. Кроме того, я упростил эти sed
команда для удаления последней строки в теле цикла (просто $d
, делает задание).
Однако этот сценарий является излишне сложным. sed
там для просто такой вещи! Эта острота сделает то же самое как вышеупомянутый сценарий:
sed -i ':a;/^[ \n]*$/{$d;N;ba}' ./file.txt
Примерно,
^[ \n]*$
. (т.е., может только содержать пробелы и новые строки) существует много потрясающих sed
учебные руководства в Интернете. Например, я могу рекомендовать этот . Счастливое изучение! :-)
Обновление: И конечно, если Вы дополнительно хотите удалить последнюю (непустую) строку файла после наличия усеченного строки конечного пробела, можно просто использовать другой sed -i '$d' ./file.txt
или после сценария или после вышеупомянутой остроты. Я намеренно не хотел включать это в sed
острота, так как я думал, что удаление строк конечного пробела является вполне допускающей повторное использование частью кода, который может быть интересным для других людей; но удаление последней непустой строки действительно характерно для Вашего варианта использования и тривиально так или иначе, после того как Вы удалили строки конечного пробела.
Как я понимаю
Используя awk
и tac
tac foo | awk '! non_empty && ! /^$/ && ! /[ \t]/ {non_empty = 1} non_empty {skip++} skip > 1 {print}' | tac
[еще 1112] изменения ниже примера †¦ <час>
Пример
% cat -n foo
1 line1
2 line2
3 line3
4
5
6 line6
7 line 7
8
% tac foo | awk '! non_empty && ! /^$/ && ! /[ \t]/ {non_empty = 1} non_empty {skip++} skip > 1 {print}' | tac > bar
% cat -n bar
1 line1
2 line2
3 line3
4
5
<час> Для удаления только пустых строк в конце файла
tac foo | awk '! non_empty && ! /^$ {non_empty = 1} non_empty {print}' | tac
<час> Для удаления пустых строк в конце и еще одной строке
tac foo | awk '! non_empty && ! /^$/ && {non_empty = 1} non_empty {skip++} skip > 1 {print}' | tac
Этой задачей является путь, более легко выполненный путем обработки строк файла в противоположном порядке.
tac infile | awk 'flag {print} {if(NF) flag=1}' | tac | sponge infile
Как указано в комментариях Malte Skoruppa и zwets, Ubuntu не идет moreutils
пакет предварительно установил, который содержит sponge
; альтернативное решение состоит в том, чтобы использовать замену команды в herestring для чтения входного файла, так, чтобы, будучи заменой команды, обработанной сначала, файл был безопасен быть усеченным вторым tac
команда:
<<<"$(< infile)" tac | awk 'flag {print} {if(NF) flag=1}' | tac > infile
tac infile
:... делает противоположность cat infile
(!): печатает файл к stdout
инвертирование порядка строки;awk [...]
: обрабатывает файл;tac
:... делает противоположность cat
(!): печатает файл к stdout
инвертирование порядка строки;sponge infile
: выводы к infile
только, когда левая сторона канала завершила свое выполнение, чтобы не усекать infile
прежде чем это будет считано первым tac
команда;awk
разбивка команды:
flag {print}
: если flag
установлен, это распечатает строку; flag
не будет установлен до записи чей NF
оцените соответствует числу, больше, чем 0
обрабатывается, поэтому до записи чей NF
оцените соответствует числу, больше, чем 0
не найден, print
команда будет пропущена;{if(NF) flag=1}
: если, в то время как flag
все еще сброшен запись чей NF
оцените соответствует числу, больше, чем 0
обрабатывается, это не будет распечатано и flag
будет установлен на 1
, так первая запись, чей NF
оцените соответствует числу, больше, чем 0
не будет распечатан;Тест на тестовом файле (ум, что строка 4 и строка 7 содержат 5 пробелов, в то время как строка 5 и строка 8 пусты):
user@debian ~ % cat infile
line1
line2
line3
line6
user@debian ~ % tac infile | awk 'flag {print} {if(NF) flag=1}' | tac
line1
line2
line3
user@debian ~ %
Строка 7 и строка 8 были удалены, потому что они были оба в конце файла, содержание только располагает с интервалами (строка 7) или содержащий ничто (строка 8); строка 6 была удалена, потому что это было первое, читая строки файла в противоположном порядке, чтобы иметь по крайней мере 1 поле (следовательно не являющийся пустым или содержащий только располагает с интервалами),
Править
Так первоначально я упустил суть, что OP хочет удалить только последнюю пустую строку, которую мое исходное решение делает также. Тем не менее, вот версия, которая удаляет только последний, и если его пробел.
awk -v numlines=$(wc -l file2|cut -f1 -d' ') 'NR < numlines; END {if (NF) print }' file2
То, что делает код, довольно просто - получают количество строк и печатают каждую строку, до последней. На последнем мы проверяем, содержит ли строка какие-либо поля; если существует какой-либо текст, NF оценивает к целому числу (верному) таким образом печатающая последняя строка, и если нет ничего или не располагает с интервалами только - NF оценивает для обнуления (ложь) и не печатает ничто больше.
Что касается удаления еще одной строки, head -n -1
сделаю.
Рев является маленькой демонстрацией. Запаздывающая новая строка определяется с $
подсказка *$
*$ cat -A file2
212$
1231$
$
324234$
213$
$
*$ awk -v numlines=$(wc -l file2|cut -f1 -d' ') 'NR < numlines ; END {if ( NF ) print }' file2 | head -n -1
212
1231
324234
Исходный
awk
решение.
awk 'NF' file1 > /tmp/tmpfile && cat /tmp/tmpfile > file1
Здесь мы используем Количество Полевой переменной как тест для печати. Для пустых строк количество полей является нулем, следовательно это, оценка пустых строк ко лжи не будет распечатана. Теперь, если Ваш awk
встроенное редактирование поддержки версии (который является gnu awk or gawk
, Я думаю), необходимо перенаправить вывод к временному выводу и назад в исходный файл с cat
, как я сделал здесь
Вариация на тему должна была бы использовать regex, чтобы протестировать, если строки содержат некоторые конкретные данные, как цифры или алфавитно-цифровые символы, например.
awk '$0~/[[:digit:]]||[[:alpha:]]/ ' file1 > /tmp/tmpfile && cat /tmp/tmpfile > file1