среда, 2 января 2019 г.

perl: Text file busy, skipping file

В процессе прикручивания pre-commit хуков к репозитарию terraform модуля столкнулся с очередным приколом shared folders в Virtualbox (vboxsf). Долго не мог понять почему хук terraform_docs отрабатывает без ошибок, но в README.md нет никаких изменений. Покопался в terraform_docs.sh и нашел место где выполняется подстановка вывода terraform-docs в разрыв маркеров в README.md.

После подстановки значения "$text_file" получается такая команда

perl -i -ne 'if (/BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK/../END OF PRE-COMMIT-TERRAFORM DOCS HOOK/) { print $_ if /BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK/; print "I_WANT_TO_BE_REPLACED\n$_" if /END OF PRE-COMMIT-TERRAFORM DOCS HOOK/;} else { print $_ }' README.md

Я попробовал запустить ее в терминале и увидел ошибку

$ perl -i -ne 'if (/BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK/../END OF PRE-COMMIT-TERRAFORM DOCS HOOK/) { print $_ if /BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK/; print "I_WANT_TO_BE_REPLACED\n$_" if /END OF PRE-COMMIT-TERRAFORM DOCS HOOK/;} else { print $_ }' README.md
Can't remove README.md: Text file busy, skipping file.

Если убрать опцию -i, то команда отработает без ошибок и выдаст на STDOUT содержимое файла README.md с добавленной строкой "I_WANT_TO_BE_REPLACED" между маркерами. Выходит perl не может обновить файл при использовании опции -i.

Я уже сталкивался с особенностями работы virtualbox shared folders и первым делом попробовал эту команду вне точки монтирования vboxsf - отработала без ошибок. Ради интереса запустил под strace

$ strace perl -i -ne 'if (/BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK/../END OF PRE-COMMIT-TERRAFORM DOCS HOOK/) { print $_ if /BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK/; print "I_WANT_TO_BE_REPLACED\n$_" if /END OF PRE-COMMIT-TERRAFORM DOCS HOOK/;} else { print $_ }' README.md
[skipped]
open("README.md", O_RDONLY)             = 3
ioctl(3, TCGETS, 0x7ffe4b254eb0)        = -1 ENOTTY (Inappropriate ioctl for device)
lseek(3, 0, SEEK_CUR)                   = 0
fstat(3, {st_mode=S_IFREG|0744, st_size=897, ...}) = 0
fcntl(3, F_SETFD, FD_CLOEXEC)           = 0
unlink("README.md")                     = -1 ETXTBSY (Text file busy)
write(2, "Can't remove README.md: Text fil"..., 55Can't remove README.md: Text file busy, skipping file.
) = 55
close(3)                                = 0
[skipped]

А вот и ограничение файловых систем Windows - нельзя удалить открытый файл. Если попробовать использовать переименование (-i.bak), то ошибка поменяется

$ perl -i.bak -ne 'if (/BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK/../END OF PRE-COMMIT-TERRAFORM DOCS HOOK/) { print $_ if /BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK/; print "I_WANT_TO_BE_REPLACED\n$_" if /END OF PRE-COMMIT-TERRAFORM DOCS HOOK/;} else { print $_ }' README.md
Can't rename README.md to README.md.bak: Text file busy, skipping file.

$ strace perl -i.bak -ne 'if (/BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK/../END OF PRE-COMMIT-TERRAFORM DOCS HOOK/) { print $_ if /BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK/; print "I_WANT_TO_BE_REPLACED\n$_" if /END OF PRE-COMMIT-TERRAFORM DOCS HOOK/;} else { print $_ }' README.md
[skipped]
open("README.md", O_RDONLY)             = 3
ioctl(3, TCGETS, 0x7fffc8ca5c50)        = -1 ENOTTY (Inappropriate ioctl for device)
lseek(3, 0, SEEK_CUR)                   = 0
fstat(3, {st_mode=S_IFREG|0744, st_size=897, ...}) = 0
fcntl(3, F_SETFD, FD_CLOEXEC)           = 0
rename("README.md", "README.md.bak")    = -1 ETXTBSY (Text file busy)
write(2, "Can't rename README.md to README"..., 72Can't rename README.md to README.md.bak: Text file busy, skipping file.
) = 72
close(3)                                = 0
[skipped]

Переименовать открытый файл Windows тоже не дает - отсюда и постоянные требования перезагрузок при установке/обновлении/удалении софта. Примечательно что sed в такой конфигурации работает без ошибок

$ echo 123 > test
$ strace sed -i -e 's/123/234/' test
[skipped]
open("test", O_RDONLY)                  = 3
ioctl(3, TCGETS, 0x7fff82ffc3a0)        = -1 ENOTTY (Inappropriate ioctl for device)
fstat(3, {st_mode=S_IFREG|0744, st_size=4, ...}) = 0
umask(0700)                             = 022
getpid()                                = 13309
open("./sedAkH6bM", O_RDWR|O_CREAT|O_EXCL, 0600) = 4
umask(022)                              = 0700
fcntl(4, F_GETFL)                       = 0x8002 (flags O_RDWR|O_LARGEFILE)
fstat(3, {st_mode=S_IFREG|0744, st_size=4, ...}) = 0
read(3, "123\n", 4096)                  = 4
fstat(4, {st_mode=S_IFREG|0744, st_size=0, ...}) = 0
write(4, "234\n", 4)                    = 4
read(3, "", 4096)                       = 0
fchown(4, 1000, 1000)                   = 0
fchmod(4, 0100744)                      = 0
close(3)                                = 0
close(4)                                = 0
rename("./sedAkH6bM", "test")           = 0
[skipped]

Вместо удаления открытого файла sed делает копию открытого исходного файла, обрабатывает ее, закрывает оба файла и заменяет оригинал копией. Нужно будет попробовать пожить с WSL и посмотреть какие грабли выплывут.

Комментариев нет:

Отправить комментарий