Читання та запис файлу: команда tee


10

Добре відомо, що така команда:

cat filename | some_sed_command >filename

стирає ім'я файлу, оскільки перенаправлення виводу, виконане перед командою, призводить до врізання імені файлу.

Вирішити проблему можна наступним чином:

cat file | some_sed_command | tee file >/dev/null

але я не впевнений, що це спрацює в будь-якому випадку: що станеться, якщо файл (і результат команди sed) дуже великий? Як операційна система може уникати перезапису контенту, який досі не читається? Я бачу, що є також команда губки, яка повинна працювати в будь-якому випадку: це "безпечніше", ніж трійник?


Яка ваша головна мета? (простіше кажучи)
Сергій Колодяжний

@Serg просто зрозумів, як все працює ... Відповідь, яку kos пояснює,
VeryHardCoder

Відповіді:


10

Вирішити проблему можна наступним чином:

cat file | some_sed_command | tee file >/dev/null

Ні .

Шанси fileбудуть укорочені, але жодної гарантії cat file | some_sed_command | tee file >/dev/nullне скоротить file.

Все залежить від того, яка команда буде оброблена спочатку, на відміну від того, що можна очікувати, команди в трубі не обробляються зліва направо . Немає гарантії того, яка команда буде обрана першою, тому можна просто вважати її як випадково вибрану і ніколи не покладатися на оболонку, не вибираючи ображуючу.

Оскільки шанси на те, що ображаюча команда буде обрана першою між трьома командами, нижча, ніж шанси на те, що ображаюча команда буде обрана першою між двома командами, менша ймовірність, що fileвона буде усічена, але це все одно відбудеться .

script.sh:

#!/bin/bash
for ((i=0; i<100; i++)); do
    cat >file <<-EOF
    foo
    bar
    EOF
    cat file |
        sed 's/bar/baz/' |
        tee file >/dev/null
    [ -s file ] &&
        echo 'Not truncated' ||
        echo 'Truncated'
done |
    sort |
    uniq -c
rm file
% bash script.sh
 93 Not truncated
  7 Truncated
% bash script.sh
 98 Not truncated
  2 Truncated
% bash script.sh
100 Not truncated

Тому ніколи не використовуйте щось подібне cat file | some_sed_command | tee file >/dev/null. Використовуйте spongeяк запропонував Олі.

Як альтернатива, для більш високих середовищ та / або відносно невеликих файлів можна використовувати рядок тут і заміну команди, щоб прочитати файл перед виконанням будь-якої команди:

$ cat file
foo
bar
$ for ((i=0; i<100; i++)); do <<<"$(<file)" sed 's/bar/baz/' >file; done
$ cat file
foo
baz

9

Для sedконкретно, ви можете використовувати його -iна місці аргументу. Він просто зберігає файл у відкритому файлі, наприклад:

sed -i 's/ /-/g' filename

Якщо ви хочете щось зробити, то припускаючи, що ви робите більше sed, так, так, ви можете згорнути всю цю справу за допомогою spongemoreutilsпакета), який буде "намочити" весь stdin перед тим, як виписати у файл. Це як, teeале з меншим функціоналом. Для основного використання, однак, це значною мірою заміна, що випадає:

cat file | some_sed_command | sponge file >/dev/null

Це безпечніше? Безумовно. Можливо, це має обмеження, тому якщо ви робите щось колосальне (і не можете змінити місце за допомогою sed), ви можете зробити свої зміни до другого файлу, а потім mvповернути його до початкового імені. Це повинно бути атомним (тому все, що залежить від цих файлів, не зламається, якщо їм потрібен постійний доступ)


0

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

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

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

  3. x Збережіть і закрийте


0

О, але spongeце не єдиний варіант; вам не доведеться отримувати moreutilsдля того, щоб це працювало належним чином. Будь-який механізм працюватиме до тих пір, поки він задовольняє двом наступним вимогам:

  1. Він приймає ім'я вихідного файла як параметр.
  2. Він створює вихідний файл лише після того, як буде оброблено весь вхід.

Розумієте, загальновідома проблема, на яку йдеться в ОП, полягає в тому, що оболонка створить усі файли, необхідні для роботи труб, перш ніж навіть почати виконувати команди в конвеєрі, тому саме оболонка насправді скорочується вихідний файл (який, на жаль, також є вхідним файлом) перед будь-якою з команд, навіть мав шанс почати виконання.

teeКоманда не працює, навіть якщо вона задовольняє першу вимогу, оскільки вона не задовольняє другій вимозі: воно завжди буде створювати вихідний файл відразу після запуску, так що, по суті , так погано , як створити трубу прямо в вихідний файл. (Це насправді гірше, тому що його використання вводить недетерміновану випадкову затримку до того, як вихідний файл обрізається, тож ви можете подумати, що він працює, а насправді це не так.)

Отже, все, що нам потрібно для вирішення цієї проблеми, - це якась команда, яка буде буферувати весь свій вхід, перш ніж виробляти будь-який вихід, і яка здатна приймати назву вихідного файла як параметр, так що нам не доведеться передавати його вихід у вихідний файл. Одна з таких команд shuf. Отже, наступне здійснить те саме, що spongeі:

    shuf --output=file --random-source=/dev/zero 

Ці --random-source=/dev/zeroприйоми частини shufв робити свою справу , не роблячи перестановку на всіх, так що це буде буфер введення , не зраджуючи його.

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