Проблема с перенаправлением вывода Bash

Расположение упаковки и байт, как описано в C FAQ здесь :

Это для выравнивания. Многие процессоры не могут получить доступ к 2- и 4-байтным количествам (например, ints и long ints), если они переполнены каждым способом.

Предположим, что у вас есть эта структура:

struct {
    char a[3];
    short int b;
    long int c;
    char d[3];
};

Теперь вы можете подумать, что это возможно, чтобы упаковать эту структуру в память следующим образом:

+-------+-------+-------+-------+
|           a           |   b   |
+-------+-------+-------+-------+
|   b   |           c           |
+-------+-------+-------+-------+
|   c   |           d           |
+-------+-------+-------+-------+

Но на процессоре намного проще, если компилятор упорядочивает его как это:

+-------+-------+-------+
|           a           |
+-------+-------+-------+
|       b       |
+-------+-------+-------+-------+
|               c               |
+-------+-------+-------+-------+
|           d           |
+-------+-------+-------+

В упакованной версии обратите внимание на то, что вам и мне, как минимум, немного сложно понять, как обтекают поля b и c? В двух словах, это тоже сложно для процессора. Поэтому большинство компиляторов будут заполнять структуру (как будто с дополнительными невидимыми полями) следующим образом:

+-------+-------+-------+-------+
|           a           | pad1  |
+-------+-------+-------+-------+
|       b       |     pad2      |
+-------+-------+-------+-------+
|               c               |
+-------+-------+-------+-------+
|           d           | pad3  |
+-------+-------+-------+-------+
blockquote>

10
задан Mark Biek 23 September 2008 в 19:31
поделиться

11 ответов

Перенаправление с файла на конвейер назад в тот же файл небезопасно; если file.txt перезаписывается оболочкой при установке последней стадии конвейера прежде tail начинает прочитывать первую стадию, Вы заканчиваете с пустым выводом.

Сделайте следующее вместо этого:

tail -1 file.txt >file.txt.new && mv file.txt.new file.txt

... хорошо, на самом деле, не делайте этого в производственном коде; особенно, если Вы находитесь в чувствительной к безопасности среде и работающий как корень, следующее является более соответствующим:

tempfile="$(mktemp file.txt.XXXXXX)"
chown --reference=file.txt -- "$tempfile"
chmod --reference=file.txt -- "$tempfile"
tail -1 file.txt >"$tempfile" && mv -- "$tempfile" file.txt

Другой подход (избегающий временных файлов, если <<< неявно создает их на Вашей платформе), следующее:

lastline="$(tail -1 file.txt)"; cat >file.txt <<<"$lastline"

(Вышеупомянутая реализация является определенной для удара, но работает в случаях, где эхо не делает - такой как тогда, когда последняя строка содержит "-версия", например).

Наконец, можно использовать губку от moreutils:

tail -1 file.txt | sponge file.txt
19
ответ дан 3 December 2019 в 14:12
поделиться

Можно использовать sed для удаления всех строк, но последнего из файла:

sed -i '$!d' file
  • - я говорю sed заменять файл на месте; иначе записал бы результат в STDOUT.
  • $ является адресом, который соответствует последней строке файла.
  • d является удалить командой. В этом случае это отрицается!, таким образом, все строки, не соответствующие адресу, будут удалены.
5
ответ дан 3 December 2019 в 14:12
поделиться

Прежде чем 'кошка' выполняется, Bash уже открыл 'file.txt' для записи, убрав ее содержание.

В целом не пишите в файлы, из которых Вы читаете в том же операторе. Это может работаться вокруг путем записи в другой файл, как выше:

$cat file.txt | tail -1 >anotherfile.txt
$mv anotherfile.txt file.txt
или при помощи утилиты как губка от moreutils:
$cat file.txt | tail -1 | sponge file.txt
Это работает, потому что губка ожидает, пока ее входной поток не закончился прежде, чем открыть ее выходной файл.
3
ответ дан 3 December 2019 в 14:12
поделиться

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

  1. Создает канал ввода-вывода.
  2. Запускается "/usr/bin/tail-1", читая из канала, и пишущий в file.txt.
  3. Запускает "/usr/bin/cat file.txt", пишущий в канал.

К тому времени, когда 'кошка' начинает читать, 'file.txt' уже был усеченным 'хвостом'.

Это - вся часть дизайна Unix и среды оболочки, и возвращается полностью к исходной Оболочке Bourne. 'Это функция, не ошибка.

2
ответ дан 3 December 2019 в 14:12
поделиться

Как Lewis Baumstark говорит, этому не нравится он, что Вы пишете в то же имя файла.

Это вызвано тем, что оболочка открывает "file.txt" и усекает его, чтобы сделать перенаправление перед "кошка, file.txt" выполняется. Так, Вы имеете к

tail -1 file.txt > file2.txt; mv file2.txt file.txt
1
ответ дан 3 December 2019 в 14:12
поделиться

$ tmp= (хвост-1 file.txt); $tmp эха> file.txt;

2
ответ дан 3 December 2019 в 14:12
поделиться

Этому, кажется, не нравится факт, Вы записываете его обратно к тому же имени файла. Если Вы делаете следующий, это работает:

$cat file.txt | tail -1 > anotherfile.txt
0
ответ дан 3 December 2019 в 14:12
поделиться

tail -1 > file.txt перезапишет Ваш файл, заставляя кошку считать пустой файл, потому что переписывание произойдет, прежде чем любая из команд в Вашем конвейере выполняется.

0
ответ дан 3 December 2019 в 14:12
поделиться
echo "$(tail -1 file.txt)" > file.txt
1
ответ дан 3 December 2019 в 14:12
поделиться

Только для этого случая можно использовать

cat < file.txt | (rm file.txt; tail -1 > file.txt)
, который откроет файл "file.txt" непосредственно перед соединением "cat" с подоболочкой в ​​"(.. .) ". "rm file.txt" удалит ссылку с диска до того, как подоболочка откроет его для записи для "tail", но содержимое будет по-прежнему доступно через открытый дескриптор, который передается в "cat", пока он не закроет stdin. Так что будьте уверены, что эта команда завершится, иначе содержимое файла "file.txt" будет потеряно
1
ответ дан 3 December 2019 в 14:12
поделиться

Это прекрасно работает в оболочке Linux:

replace_with_filter() {
  local filename="$1"; shift
  local dd_output byte_count filter_status dd_status
  dd_output=$("$@" <"$filename" | dd conv=notrunc of="$filename" 2>&1; echo "${PIPESTATUS[@]}")
  { read; read; read -r byte_count _; read filter_status dd_status; } <<<"$dd_output"
  (( filter_status > 0 )) && return "$filter_status"
  (( dd_status > 0 )) && return "$dd_status"
  dd bs=1 seek="$byte_count" if=/dev/null of="$filename"
}

replace_with_filter file.txt tail -1

dd параметр notrunc используется для записи отфильтрованного содержимого обратно на место , в то время как dd снова требуется (со счетчиком байтов) для фактического усечения файла. Если размер нового файла больше или равен размеру старого файла, второй вызов dd не требуется.

Преимущества этого метода по сравнению с методом копирования файлов: 1) отсутствие необходимости в дополнительном дисковом пространстве, 2) более высокая производительность для больших файлов и 3) чистая оболочка (кроме dd).

2
ответ дан 3 December 2019 в 14:12
поделиться
Другие вопросы по тегам:

Похожие вопросы: