it-swarm.com.ru

Как я могу удалить последний символ файла в Unix?

Скажем, у меня есть произвольный многострочный текстовый файл:

sometext
moretext
lastline

Как я могу удалить только последний символ (e, а не символ новой строки или ноль) файла, не делая текстовый файл недействительным?

53
MaxPRafferty

Более простой подход (выводит в stdout, не обновляет входной файл):

sed '$ s/.$//' somefile
  • $ - это адрес Sed, который соответствует только последней строке ввода, поэтому следующий вызов функции (s/.$//) будет выполняться только в последней строке.
  • s/.$// заменяет последний символ в строке (в данном случае last) пустой строкой; т.е. эффективно удаляет последний символ. (до новой строки) на строке.
    . сопоставляет любой символ в строке, а после него $ привязывает совпадение к концу строки; обратите внимание, что использование $ в этом регулярном выражении концептуально связано, но технически отличается от предыдущего использования $ в качестве Sed address.
  • Пример с вводом stdin (предполагает Bash, Ksh или Zsh):

    $ sed '$ s/.$//' <<< $'line one\nline two'
    line one
    line tw
    

Для обновления входного файла тоже (не используйте, если входной файл является символической ссылкой):

sed -i '$ s/.$//' somefile

Замечания:
* В OSX вам придется использовать -i '' вместо просто -i; для обзора ловушек, связанных с -i, смотрите нижнюю половину мой ответ здесь .
* Если вам нужно обрабатывать очень большие входные файлы и/или производительность/использование диска вызывает озабоченность и вы используете утилиты GNU (Linux), см. полезный ответ sorontar .

68
mklement0

truncate

truncate -s-1 file

Удаляет один (-1) символ из конца того же файла. Точно так же, как >> добавит к тому же файлу.

Проблема с этим подходом состоит в том, что он не сохраняет завершающий символ новой строки, если он существует.

Решение:

if     [ -n "$(tail -c1 file)" ]    # if the file has not a trailing new line.
then
       truncate -s-1 file           # remove one char as the question request.
else
       truncate -s-2 file           # remove the last two characters
       echo "" >> file              # add the trailing new line back
fi

Это работает, потому что tail занимает последний байт (не char).

Это занимает почти нет времени даже с большими файлами.

Почему бы не sed

Проблема с решением sed, таким как sed '$ s/.$//' file, заключается в том, что сначала он читает весь файл (что занимает много времени с большими файлами), затем вам нужен временный файл (того же размера, что и оригинал):

sed '$ s/.$//' file  > tempfile
rm file; mv tempfile file

А затем переместите временный файл для замены файла. 

34
sorontar

Вот еще один пример использования ex, который я считаю не таким загадочным, как решение sed:

 printf '%s\n' '$' 's/.$//' wq | ex somefile

$ идет до последней строки, s удаляет последний символ, а wq - это хорошо известная (для пользователей vi) запись + выход.

4
Jens

Если цель состоит в том, чтобы удалить последний символ в последней строке, эта awk должна сделать:

awk '{a[NR]=$0} END {for (i=1;i<NR;i++) print a[i];sub(/.$/,"",a[NR]);print a[NR]}' file
sometext
moretext
lastlin

Он сохраняет все данные в массив, затем распечатывает их и изменяет последнюю строку.

2
Jotne

Просто замечание: sed временно удалит файл . Поэтому, если вы настраиваете файл, вы получите предупреждение «Нет такого файла или каталога», пока вы не повторите команду tail.

1
Tim Chaubet

Отредактированный ответ

Я создал скрипт и разместил ваш текст внутри на рабочем столе. этот тестовый файл сохраняется как "old_file.txt"

sometext
moretext
lastline

После этого я написал небольшой скрипт, чтобы взять старый файл и удалить последний символ в последней строке

#!/bin/bash
no_of_new_line_characters=`wc  '/root/Desktop/old_file.txt'|cut -d ' ' -f2`
let "no_of_lines=no_of_new_line_characters+1"
sed -n 1,"$no_of_new_line_characters"p  '/root/Desktop/old_file.txt' > '/root/Desktop/my_new_file'
sed -n "$no_of_lines","$no_of_lines"p '/root/Desktop/old_file.txt'|sed 's/.$//g' >> '/root/Desktop/my_new_file'

открывая созданный мной новый_файл, показал вывод следующим образом:

sometext
moretext
lastlin

Я прошу прощения за мой предыдущий ответ (не читал внимательно)

1
repzero

После целого ряда игр с разными стратегиями (и избегая sed -i или Perl), лучший способ, который я нашел для этого, был с:

sed '$! { P; D; }; s/.$//' somefile
1
MaxPRafferty

sed 's/.$//' filename | tee newFilename

Это должно сделать вашу работу.

0
karthik339