У меня была та же проблема, и я быстро нашел простое решение, используя функцию awk
log()
:
awk '
BEGIN {
split("B,kiB,MiB,GiB", suff, ",")
}
{
size=$1;
rank=int(log(size)/log(1024));
printf "%.4g%s\n", size/(1024**rank), suff[rank+1]
}
'
И потеря точности при использовании чисел с плавающей запятой не так уж плоха, поскольку эта точность все равно будет потеряна.
Это зависит от используемой файловой системы и устройства хранения. Как правило, файловые системы будут перезаписываться, когда вы им это скажете, если только вы не используете файловую систему, оптимизированную для необработанной флэш-памяти; эти специальные файловые системы могут реализовывать выравнивание износа на уровне FS и, следовательно, записывать в другое место.
Но если устройство хранения представляет собой обычный твердотельный накопитель SATA или NVMe, оно все равно может выполнять внутреннее выравнивание износа, и реальные физические блоки хранения будут отличаться от того, что покажет даже необработанное блочное устройство.Таким образом, «перезапись» в конечном итоге будет происходить в другом физическом месте, даже если файловая система считает, что она определенно перезаписывает определенный блок #.
Но преодоление системы выравнивания износа SSD и осмысленное чтение необработанного хранилища должно быть нетривиальной -технической проблемой, требующей специальных знаний и, возможно, специальных аппаратных средств. И SSD, вероятно, предварительно -предусмотрительно сотрет любые «перезаписанные» блоки, как только это станет практически возможным, чтобы иметь как можно больше стертых блоков, доступных для записи, поскольку стирание, как правило, является ограничивающим фактором производительности SSD.
Я подозреваю, что когда вы открываете файл, чтобы записать его с самого начала, все существующие блоки будут немедленно освобождены (, все еще содержащие их существующее содержимое ), и ваш cp обнулит новые блоки -. ] наполнять.
Кроме того, ваш файл будет расширен до тех пор, пока не заполнит весь раздел :первоначальный размер не будет учитываться.
Команда dd
имеет параметр conv=notrunc
, и вы сможете найти исходный размер с помощью stat, округлить его до целых блоков и использовать параметры count
и bs
для определения размера нулей. в те же блоки, что и оригинал.
Изменить :Тестом подтверждено, что оболочка > перенаправление сохраняет тот же номер инода, но немедленно уменьшает размер файла до нулевых блоков и освобождает их для доступного пространства.
strace на cp показывает ванильную перезапись файла через область mmap:
open("foo.tiny", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=8192,...}) = 0
open("foo.copy", O_WRONLY|O_CREAT|O_EXCL, 0644) = 4
fstat(4, {st_mode=S_IFREG|0644, st_size=0,...}) = 0
fadvise64(3, 0, 0, POSIX_FADV_SEQUENTIAL) = 0
mmap(NULL, 139264, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc295d30000
read(3, "\0\0\0\0"..., 131072) = 8192
write(4, "\0\0\0\0"..., 8192) = 8192
read(3, "", 131072) = 0
close(4) = 0
close(3) = 0
munmap(0x7fc295d30000, 139264) = 0
lseek(0, 0, SEEK_CUR) = -1 ESPIPE (Illegal seek)
close(0) = 0
close(1) = 0
close(2) = 0
Вывод должен заключаться в том, что cp
удалит ваши конфиденциальные данные в свободный список и перезапишет любые блоки, которые будут выделены впоследствии.
Этот сценарий демонстрирует, что dd обнуляет часть или все существующие блоки в файле, не освобождая блоки и не изменяя индексный дескриптор. Возможно, вам потребуется получить фактический BLKSZ и размер файла из команды stat,и использовать арифметику bash для округления размера до целого числа блоков. Также показывает, что dd расширит файл и запишет разреженные данные.
В результате получается около 100 строк вывода, поэтому я не буду это публиковать. Это доброкачественно. Функция Zero - это мясо.
#! /bin/bash
FN='./Erase.data'
BLKSZ=4096
#.. Zeroise a specified range of blocks (zero-based).
Zero () { #:: (from, to)
dd 2>&1 ibs="${BLKSZ}" obs="${BLKSZ}" \
seek="${1}" count="$(( $2 - $1 + 1 ))" \
conv=notrunc if="/dev/zero" of="${FN}"
}
#.. Create a file of 8 * 4096B blocks, each labelled in every character.
Make () { #:: (void)
AWK='
function Block (sz, id, Local, buf) {
buf = sprintf ("%*s", sz, "");
gsub (/./, id, buf);
printf ("%s", buf);
}
{ for (f = 2; f <= NF; ++f) Block( $1, $(f)); }
'
echo "${BLKSZ}" {A..H} | awk "${AWK}" > "${FN}"
}
#.. Reveal the file.
Show () {
echo; ls -l "${FN}"; stat "${FN}"; od -A d -t a "${FN}"; sleep 2
}
#### Script Body Starts Here.
#.. Make the file and prove its contents.
Make > "${FN}" && Show
Zero 3 6 && Show
Zero 0 1 && Show
Zero 0 7 && Show
Zero 220 231 && Show
Это приближение к производственной версии.
#! /bin/bash
Usage () { expand -t 4 <<'EOF'
Usage: ZeroAllBlocks [-h] [files...]
Warning: this command is as brutal as rm -f.
-h: shows this message.
Zeroises (binary zero) all blocks of all the files named.
Sparse blocks will then consume real disk space.
EOF
}
#.. Zeroise a specified range of blocks (zero-based).
Zero () { #:: (Fn, blksz, seek, count)
local Fn="${1}" blksz="${2}" seek="${3}" count="${4}"
dd status=none ibs="${blksz}" obs="${blksz}" \
seek="${seek}" count="${count}" \
conv=notrunc if="/dev/zero" of="${Fn}"
}
#.. Process a file.
File () { #:: (filename)
local Fn="${1}" szFile szBlock nBlock
[[ -f "${Fn}" ]] || { printf '%s: No such file\n' "${Fn}"; return; }
[[ -w "${Fn}" ]] || { printf '%s: Not writable\n' "${Fn}"; return; }
read -r szFile szBlock <<<$( stat --printf='%s %o\n' "${Fn}" )
nBlock="$(( (szFile + szBlock - 1) / szBlock ))"
Zero "${Fn}" "${szBlock}" 0 "${nBlock}"
}
#### Script Body Starts Here.
[[ "${1}" = "-h" ]] && { Usage; exit 2; }
for Fn in "${@}"; do File "${Fn}"; done
Почему вы хотите скопировать /dev/zero
в какой-то файл?
Вы хотите безопасно удалить a
? Тогда cp
— неправильный инструмент. Посмотрите shred
, если вы используете жесткий диск, и fstrim
, если вы используете SSD. (Скорее всего, есть и другие инструменты, о которых я не знаю, поэтому было бы неплохо погуглить «безопасное удаление».)
Вы понимаете, что /dev/zero
бесконечен в размерах? Он будет бесконечно возвращать нули, поэтому cp
никогда не завершится, пока файловая система, содержащая a
, не заполнится.
Но этого может и не произойти, потому что cp
,в рамках обнаружения разреженных файлов подавляет запись всех -нулевых страниц.
Вы хотите создать файл заданного размера, полный нулей? cp
все еще не тот инструмент. Вам нужно будет использовать dd
, чтобы указать, сколько байт копировать из /dev/zero
.
В общем, используйте cp
для копирования обычного файла в другой обычный файл, когда все, о чем вы заботитесь, это то, что копия логически эквивалентна.
Если вы работаете с файлами устройств или пытаетесь управлять удалением блоков исходного файла, вам следует использовать другую программу.