No Unix, a maioria dos editores trabalha criando um novo arquivo temporário contendo o conteúdo editado. Quando o arquivo editado é salvo, o arquivo original é excluído e o arquivo temporário é renomeado para o nome original. (Existem, é claro, várias salvaguardas para evitar perda de dados.) Esse é, por exemplo, o estilo usado por sed
ou perl
quando invocado com o sinalizador -i
("in-place"), que não é realmente "no local" em tudo. Deveria ter sido chamado "novo lugar com nome antigo".
Isso funciona bem porque o unix assegura (pelo menos para sistemas de arquivos locais) que um arquivo aberto continua a existir até ser fechado, mesmo que seja "excluído" e um novo arquivo com o mesmo nome seja criado. (Não é coincidência que a chamada do sistema unix para "excluir" um arquivo seja na verdade chamada de "unlink".) Assim, em geral, se um interpretador de shell tiver algum arquivo de origem aberto, você "editará" o arquivo da maneira descrita acima , o shell nem verá as alterações, pois ainda tem o arquivo original aberto.
[Nota: como com todos os comentários baseados em padrões, o acima está sujeito a múltiplas interpretações e há vários casos-chave, como o NFS. Pedantes são bem-vindos para preencher os comentários com exceções.]
É claro que é possível modificar arquivos diretamente; simplesmente não é muito conveniente para fins de edição, porque enquanto você pode sobregravar dados em um arquivo, você não pode excluir ou inserir sem alterar todos os dados a seguir, o que implicaria bastante reescrita. Além disso, enquanto você estava fazendo essa mudança, o conteúdo do arquivo seria imprevisível e os processos que tinham o arquivo aberto sofreriam. Para fugir disso (como acontece com os sistemas de banco de dados, por exemplo), você precisa de um conjunto sofisticado de protocolos de modificação e bloqueios distribuídos; coisas que estão além do escopo de um utilitário típico de edição de arquivos.
Então, se você quiser editar um arquivo enquanto ele está sendo processado por um shell, você tem duas opções:
-
Você pode acrescentar ao arquivo. Isso deve sempre funcionar.
-
Você pode sobrescrever o arquivo com novos conteúdos de exatamente o mesmo tamanho . Isso pode ou não funcionar, dependendo se o shell já leu essa parte do arquivo ou não. Como a maioria das I / O de arquivo envolve buffers de leitura, e como todos os shells que conheço lêem um comando composto inteiro antes de executá-lo, é bem improvável que você consiga fugir disso. Certamente não seria confiável.
Eu não conheço nenhum texto no padrão Posix que realmente exija a possibilidade de anexar a um arquivo de script enquanto o arquivo está sendo executado, então ele pode não funcionar com todos os shell compatíveis com Posix, muito menos com o atual de conchas quase e às vezes posix-compliant. Então YMMV. Mas, tanto quanto sei, funciona de forma confiável com o bash.
Como evidência, aqui está uma implementação "loop-free" do infame programa 99 bottles of beer no bash, que usa dd
para sobrescrever e anexar (a sobrescrita é presumivelmente segura porque substitui a linha atualmente em execução, que é sempre a última linha do arquivo, com um comentário exatamente do mesmo tamanho, eu fiz isso para que o resultado final pudesse ser executado sem o comportamento de auto-modificação.)
#!/bin/bash
if [[ $1 == reset ]]; then
printf "%s\n%-16s#\n" '####' 'next ${1:-99}' |
dd if=/dev/stdin of=$0 seek=$(grep -bom1 ^#### $0 | cut -f1 -d:) bs=1 2>/dev/null
exit
fi
step() {
s=s
one=one
case $beer in
2) beer=1; unset s;;
1) beer="No more"; one=it;;
"No more") beer=99; return 1;;
*) ((--beer));;
esac
}
next() {
step ${beer:=$(($1+1))}
refrain |
dd if=/dev/stdin of=$0 seek=$(grep -bom1 ^next\ $0 | cut -f1 -d:) bs=1 conv=notrunc 2>/dev/null
}
refrain() {
printf "%-17s\n" "# $beer bottles"
echo echo ${beer:-No more} bottle$s of beer on the wall, ${beer:-No more} bottle$s of beer.
if step; then
echo echo Take $one down, pass it around, $beer bottle$s of beer on the wall.
echo echo
echo next abcdefghijkl
else
echo echo Go to the store, buy some more, $beer bottle$s of beer on the wall.
fi
}
####
next ${1:-99} #