У мене в тексті багато текстових файлів, і я хочу видалити останній рядок кожного файлу в каталозі.
Як я можу це зробити?
У мене в тексті багато текстових файлів, і я хочу видалити останній рядок кожного файлу в каталозі.
Як я можу це зробити?
Відповіді:
Ви можете використовувати цей хороший oneliner, якщо у вас є GNU sed.
sed -i '$ d' ./*
Він видалить останній рядок кожного не прихованого файлу з поточного каталогу. Перемикач -iдля GNU sedозначає працювати на місці та '$ d'командує sedдля видалення останнього рядка ( $означає останнє та dзначення видалення).
-iщо є GNUism, тому це суперечка, але я втратив би свою стару бороду, якби я не зміг зазначити, що деякі старіші версії sedне дозволяють вам розміщувати пробіли між $і d(або, взагалі між шаблоном і командою).
*(.)для глобальних файлів для звичайних файлів, я не знаю про інші оболонки.
Інші відповіді мають усі проблеми, якщо каталог містить щось інше, ніж звичайний файл або файл з пробілами / новинками у назві файлу. Ось щось працює незалежно:
find "$dir" -type f -exec sed -i '$d' '{}' '+'
find "$dir" -type f: знайдіть файли в каталозі $dir
-type f які є звичайними файлами;-exec виконати команду на кожному знайденому файліsed -i: редагувати файли на місці;'$d': видалити ( d) останній ( $) рядок.'+': каже find, щоб тримати додавання аргументів sed(трохи ефективніше, ніж виконання команди для кожного файлу окремо, завдяки @zwol).Якщо ви не хочете переходити до підкаталогів, тоді ви можете додати аргумент -maxdepth 1до find.
findцього написано більш ефективно find $dir -type f -exec sed -i '$d' '{}' '+'.)
-print0немає в повній команді, навіщо це вводити в пояснення?
-depth 0не працює (findutils 4.4.2), замість цього має бути -maxdepth 1.
-exec.
Використовувати GNU sed -i '$d'означає прочитати повний файл і зробити його копію без останнього рядка, тоді як було б набагато ефективніше просто усікати файл на місці (принаймні для великих файлів).
З GNU truncateви можете:
for file in ./*; do
[ -f "$file" ] &&
length=$(tail -n 1 "$file" | wc -c) &&
[ "$length" -gt 0 ] &&
truncate -s "-$length" "$file"
done
Якщо файлів порівняно мало, це, ймовірно, буде менш ефективним, хоча воно виконує кілька команд на файл.
Зауважте, що для файлів, що містять додаткові байти після останнього символу нового рядка (після останнього рядка) або іншими словами, якщо вони мають не обмежений останній рядок , то, залежно від tailреалізації, tail -n 1повертаються лише ті зайві байти (як GNU tail), або останній (правильно розмежований) рядок і ці зайві байти.
|wc -cв tailвиклику? (або а ${#length})
${#length}не буде працювати, оскільки він рахує символи, а не байти, і $(...)видалить символ нового рядка, так що ${#...}він буде вимкнений одним, навіть якщо всі символи були однобайтовими.
Більш портативний підхід:
for f in ./*
do
test -f "$f" && ed -s "$f" <<\IN
d
w
q
IN
done
Я не думаю, що це не потребує пояснень ... за винятком, можливо, що в цьому випадку dце те саме, що $dоскільки edза замовчуванням вибирає останній рядок.
Це не буде рекурсивно шукати і не обробляє приховані файли (ака dotfiles).
Якщо ви теж бажаєте відредагувати, див. Як зіставити * із прихованими файлами всередині каталогу
[[]]до []цього, він буде повністю сумісним з POSIX. ( [[ ... ]]є башизм.)
[[це не башізм )
POSIX-сумісний однокласник для всіх файлів, які рекурсивно починаються в поточному каталозі, включаючи dot-файли:
find . -type f -exec sh -c 'for f; do printf "\$d\nx\n" | ex "$f"; done' sh {} +
.txtЛише для файлів, не рекурсивно:
find . -path '*/*/*' -prune -o -type f -name '*.txt' -exec sh -c 'for f; do printf "\$d\nx\n" | ex "$f"; done' sh {} +
Також дивіться: