Мені потрібно неодноразово видаляти перший рядок з величезного текстового файлу за допомогою скрипту bash.
Зараз я використовую sed -i -e "1d" $FILE
- але для видалення потрібно близько хвилини.
Чи є більш ефективний спосіб досягти цього?
Мені потрібно неодноразово видаляти перший рядок з величезного текстового файлу за допомогою скрипту bash.
Зараз я використовую sed -i -e "1d" $FILE
- але для видалення потрібно близько хвилини.
Чи є більш ефективний спосіб досягти цього?
Відповіді:
Спробуйте хвіст :
tail -n +2 "$FILE"
-n x
: Просто надрукуйте останні x
рядки. tail -n 5
дасть вам останні 5 рядків вводу. Вид +
знаку інвертує аргумент і змушує tail
друкувати все, крім перших x-1
рядків. tail -n +1
надрукував би весь файл, tail -n +2
усе, окрім першого рядка тощо.
GNU tail
набагато швидше, ніж sed
. tail
також доступний на BSD, і -n +2
прапор є послідовним для обох інструментів. Перевірте докладні сторінки FreeBSD або OS X для отримання додаткової інформації.
Однак версія BSD може бути набагато повільнішою, ніж sed
все-таки. Цікаво, як їм це вдалося; tail
слід просто читати файл за рядком, в той час як sed
виконує досить складні операції, пов’язані з інтерпретацією сценарію, застосуванням регулярних виразів тощо.
Примітка: Ви можете спокуситись використовувати
# THIS WILL GIVE YOU AN EMPTY FILE!
tail -n +2 "$FILE" > "$FILE"
але це дасть вам порожній файл . Причина полягає в тому, що оболонка викликає перенаправлення ( >
) раніше tail
:
$FILE
tail
tail
процесу на$FILE
tail
читає з тепер порожнього $FILE
Якщо ви хочете видалити перший рядок із файлу, слід використовувати:
tail -n +2 "$FILE" > "$FILE.tmp" && mv "$FILE.tmp" "$FILE"
&&
Буде переконатися , що файл не буде перезаписан , коли є проблема.
-r
опцією. Можливо, десь у системі є налаштування буфера? Або -n
це 32-розрядний підписаний номер?
tail
вони працюватимуть для будь-якого розміру файлу.
-n N means output the last N lines, instead of the last 10; or use +N to output lines starting with the Nth
Ви можете використовувати -i для оновлення файлу, не використовуючи оператора '>'. Наступна команда видалить перший рядок із файлу та збереже його у файл.
sed -i '1d' filename
unterminated transform source string
sed -i '1,2d' filename
tail -n +2
. Не впевнений, чому це не найкраща відповідь.
Для тих, хто перебуває на SunOS, який не є GNU, допоможе наступний код:
sed '1d' test.dat > tmp.dat
Ні, це приблизно так ефективно, як ви збираєтеся отримати. Ви можете написати програму C, яка могла б виконати роботу трохи швидше (менше часу запуску та обробки аргументів), але вона, ймовірно, буде прагнути до тієї ж швидкості, що і sed, коли файли набувають великих розмірів (і я припускаю, що вони великі, якщо це займе хвилину ).
Але ваше запитання страждає від тієї ж проблеми, що і стільки інших, оскільки воно передбачає вирішення. Якби ви детально розповіли нам, що ви намагаєтесь зробити, а як тоді , ми можемо запропонувати кращий варіант.
Наприклад, якщо це файл A, який обробляє деякі інші програми B, одним із рішень було б не знімати перший рядок, а змінювати програму B, щоб вона обробляла її інакше.
Скажімо, всі ваші програми додаються до цього файлу A, а програма B наразі читає та обробляє перший рядок перед тим, як видалити його.
Ви можете перепрограмувати програму B, щоб вона не намагалася видалити перший рядок, але підтримувала стійкий (можливо, на основі файлу) зсув у файл A, щоб наступного разу, коли він запускається, він міг прагнути до цього зміщення, обробляти рядок там, і оновити зміщення.
Потім, у тихий час (опівночі?), Вона могла б виконати спеціальну обробку файлу А, щоб видалити всі оброблювані нині рядки та встановити зміщення назад на 0.
Звичайно, програма буде швидше відкриватись і шукати файл, а не відкривати та переписувати. Ця дискусія передбачає, що ви, звичайно, маєте контроль над програмою B. Я не знаю, чи так це, але якщо ви надасте додаткову інформацію, можуть бути інші можливі рішення.
awk FNR-1 *.csv
мабуть, швидше.
Ви можете редагувати файли на місці: просто використовуйте -i
прапор Perl , наприклад, такий:
perl -ni -e 'print unless $. == 1' filename.txt
Це змушує перший рядок зникати, як ви просите. Perl потрібно буде прочитати та скопіювати весь файл, але він організує збереження виводу під назвою вихідного файлу.
Як сказав Пакс, ви, мабуть, не збираєтеся швидше отримати це. Причина полягає в тому, що майже немає файлових систем, які підтримують обрізання з початку файлу, тому це буде n
операцією O ( ), де n
розмір файлу. Що можна зробити набагато швидше, хоча це перезаписати перший рядок з однаковою кількістю байтів (можливо, з пробілами чи коментарями), які можуть працювати для вас залежно від того, що саме ви намагаєтесь зробити (що це, до речі?).
sponge
Util дозволяє уникнути необхідності жонглювати тимчасовий файл:
tail -n +2 "$FILE" | sponge "$FILE"
sponge
насправді набагато чистіші та надійніші, ніж прийняте рішення ( tail -n +2 "$FILE" > "$FILE.tmp" && mv "$FILE.tmp" "$FILE"
)
sponge
буферує весь файл у пам'яті? Це не спрацює, якщо це сотні ГБ.
sponge
буде відмочити, оскільки вона використовує файл / tmp як проміжний крок, який потім використовується для заміни оригіналу.
Якщо ви хочете змінити файл в місці, ви завжди можете використовувати оригінал ed
замість його s treaming наступника sed
:
ed "$FILE" <<<$'1d\nwq\n'
ed
Командування оригінальний текстовий редактор UNIX, перш ніж були навіть термінали повноекранні, набагато менше графічних робочих станцій. ex
Редактор, відомий як то , що ви використовуєте , коли набравши в командному рядку в Колоні vi
, є колишній , як правило , версія ed
, так що багато хто з тієї ж роботи команд. Хоча ed
призначений для інтерактивного використання, він також може використовуватися в пакетному режимі, надсилаючи до нього рядок команд, що і робить це рішення.
Послідовність <<<$'1d\nwq\n'
користується підтримкою Bash для тут-рядків ( <<<
) і POSIX лапки ( $'
... '
) для введення подачі в ed
команду , що складається з двох ліній: 1d
, що г eletes вирівнює 1 , а потім wq
, який ж обряди файл назад в диск, а потім q використовує сеанс редагування.
повинні показувати рядки, крім першого рядка:
cat textfile.txt | tail -n +2
Для цього можна використовувати vim:
vim -u NONE +'1d' +'wq!' /tmp/test.txt
Це має бути швидше, оскільки vim не буде читати весь файл під час обробки.
+wq!
якщо ваша оболонка - це башти. Напевно, це не так, оскільки !
це не на початку слова, але звичка цитувати речі, мабуть, добре все навколо. (І якщо ви збираєтеся отримати суперефективність, не цитуючи зайвих зусиль, вам також не потрібні цитати 1d
.)
Оскільки це звучить так, що я не можу прискорити видалення, я думаю, що хорошим підходом може бути обробка файлу такими партіями:
While file1 not empty
file2 = head -n1000 file1
process file2
sed -i -e "1000d" file1
end
Недолік цього полягає в тому, що якщо програма загине в середині (або якщо там є якийсь поганий sql - спричиняючи загибель або замикання частини "процес"), будуть рядки, які або пропускаються, або обробляються двічі .
(файл1 містить рядки коду sql)
Чи вдасться використати хвіст у рядках N-1 і направити його у файл, після чого видалити старий файл та перейменувати новий файл на старе ім'я?
Якби я робив це програмно, я прочитав би файл і запам'ятав зсув файлу, прочитавши кожен рядок, тож я міг би повернутися до цього положення, щоб прочитати файл з одним меншим рядком у ньому.