Linux: Як використовувати файл як вхід, так і вихід одночасно?


55

Я просто запустив наступне в баші:

uniq .bash_history > .bash_history

і мій файл історії закінчився абсолютно порожнім.

Я думаю, мені потрібен спосіб прочитати весь файл, перш ніж писати в нього. Як це робиться?

PS: Я, очевидно, думав використовувати тимчасовий файл, але шукаю більш елегантне рішення.


Це тому, що файли відкриваються справа наліво. Дивіться також stackoverflow.com/questions/146435 / ...
WheresAlice

Ви повинні записати вихід у новий файл у той самий каталог та перейменувати цей файл, що знаходиться над старим файлом. Будь-який інший підхід ризикує втратити ваші дані, якщо вони будуть перервані на півдорозі. Деякі інструменти можуть приховати від вас цей крок.
kasperd

Або bashне вноситимуть послідовні дупи в свою історію, якщо ви встановите HISTCONTROL для включення ігнорування; див. сторінку.
dave_thompson_085

Відповіді:


49

Я рекомендую використовувати spongeз moreutils . На сторінці сторінки:

DESCRIPTION
  sponge  reads  standard  input  and writes it out to the specified file. Unlike
  a shell redirect, sponge soaks up all its input before opening the output file.
  This allows for constructing pipelines that read from and write to the same 
  file.

Щоб застосувати це до своєї проблеми, спробуйте:

uniq .bash_history | sponge .bash_history

6
Це як кішка, але з можливостями смоктання: D
MilliaLover

77

Я просто хотів зробити ще одну відповідь, яка проста і не використовує губку (тому що вона часто не входить у полегшене середовище).

echo "$(uniq .bash_history)" > .bash_history

повинні мати бажаний результат. Підскладок виконується до відкриття .bash_history для запису. Як пояснено у відповіді Філа Р, до моменту, коли .bash_history читається в оригінальній команді, вона вже скорочена оператором '>'.


15
Я, як правило, не прихильник відповідей, які дратують давнє запитання, на яке вже є вагомою, прийнятою відповіддю, - але це елегантно, добре написано та є вагомим аргументом для його необхідності (полегшене середовище); для мене це дійсно щось додає до існуючого набору відповідей. Ласкаво просимо в SF, Харт (ти був тут місяць, але я думаю, що це твоя перша змістовна публікація). Я сподіваюся прочитати більше відповідей від вас, як ця!
MadHatter

4
Це найкраще рішення. Мені довелося використовувати підзаглушку $()замість задніх посилань із-за якихось проблем.
CMCDragonkai

3
Мені цікаво, чи масштабує це рішення великі файли, скажімо ... 20 або 50 ГБ.
Аміт Найду

1
Це дійсно має бути прийнятою відповіддю.
maxywb

1
Цю відповідь я використав echo "$(fmt -p '# ' -w 50 readme.txt)" > readme.txtсьогодні. Довго шукав навколо елегантного рішення. Велике спасибі, @Hart Simha!
shredalert

12

Проблема полягає в тому, що ваша оболонка налаштовує конвеєр команд перед запуском команд. Це не питання "введення та виведення", це те, що вміст файлу вже вийшов до того, як uniq навіть запуститься. Це щось на зразок:

  1. Оболонка відкриває >вихідний файл для запису, обрізаючи його
  2. Оболонка встановлюється для того, щоб файл-дескриптор 1 (для stdout) використовувався для цього виводу
  3. Оболонка виконує uniq, можливо, щось на зразок execlp ("uniq", "uniq", ".bash_history", NULL)
  4. uniq працює, відкриває .bash_history і нічого там не знаходить

Існують різні рішення, включаючи редагування на місці та тимчасове використання файлів, про які згадують інші, але ключовим є розуміння проблеми, що насправді відбувається не так і чому.


9

Ще один трюк для цього, не використовуючи sponge, - це наступна команда:

{ rm .bash_history && uniq > .bash_history; } < .bash_history

Це один із чітів, описаних у чудовій статті редагування файлів на backreference.org «На місці» .

Він в основному відкриває файл для читання, після чого "видаляє" його. Однак насправді це не вилучено: є відкритий дескриптор файлу, який вказує на нього, і поки цей файл залишається відкритим, файл все ще існує. Потім він створює новий файл з такою ж назвою і записує до нього унікальні рядки.

Недолік цього рішення: Якщо uniqз якихось причин не вдасться, ваша історія не залишиться.



3

Цей sedскрипт видаляє сусідні дублікати. За допомогою -iопції вона робить модифікацію на місці. Це з sed infoфайлу:

sed -i 'h;:b;$b;N;/^\(.*\)\n\1$/ {g;bb};$b;P;D' .bash_history

sed досі використовує тимчасовий файл, додав відповідь із straceілюстрацією (не те, що насправді має значення) :-)
Kyle Brandt

3
@Kyle: Правда, але "з поля зору, з розуму". Особисто я би використовував явний тимчасовий файл, оскільки щось на кшталт process input > tmp && mv tmp inputнабагато простіше і читабельніше, ніж використання sedхитрості просто для того, щоб уникнути тимчасового файлу, і він не замінить мій оригінал, якщо не вийде (я не знаю, якщо sed -iвиходить з ладу витончено - я б думаю, що хоч). Крім того, є багато речей, які ви можете зробити з методом output-to-temp-file, який неможливо зробити на місці без чогось навіть більшого залучення, ніж цей sedсценарій. Я знаю, ви все це знаєте, але це може принести користь деякому глядачу.
Денніс Вільямсон,

3

Як цікавий лайдак, sed також використовує темп-файл (це робиться саме для вас):

$ strace sed -i 's/foo/bar/g' foo    
open("foo", O_RDONLY|O_LARGEFILE)       = 3
...
open("./sedPmPv9z", O_RDWR|O_CREAT|O_EXCL|O_LARGEFILE, 0600) = 4
...
read(3, "foo\n"..., 4096)               = 4
write(4, "bar\n"..., 4)                 = 4
read(3, ""..., 4096)                    = 0
close(3)                                = 0
close(4)                                = 0
rename("./sedPmPv9z", "foo")            = 0
close(1)                                = 0
close(2)                                = 0

Опис:
тимчасовий файл ./sedPmPv9zстає FD 4, і fooфайли стає FD 3. Операції читання знаходяться на дескрипторі 3, і запис на дескрипторі 4 (файл TEMP). Потім файл foo перезаписується тимчасовим файлом у виклику перейменування.



0

Тимчасовий файл - це майже все, якщо тільки ця команда не підтримує редагування місця ( uniqне - деякі seds do ( sed -i)).


0

Ви можете використовувати Vim в режимі Ex:

ex -sc '%!uniq' -cx .bash_history
  1. % виберіть усі рядки

  2. ! запустити команду

  3. x зберегти і закрити


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.