Eu tenho uma pergunta geral, que pode ser o resultado de um mal-entendido de como os processos são tratados no Linux.
Para meus propósitos, vou definir um 'script' como um trecho de código de bash salvo em um arquivo de texto com as permissões de execução ativadas para o usuário atual.
Eu tenho uma série de scripts que se chamam em conjunto. Por uma questão de simplicidade, eu os chamarei de scripts A, B e C. O script A executa uma série de instruções e faz uma pausa, depois executa o script B, depois faz uma pausa e depois executa o script C. Em outras palavras, a série de etapas é algo como isto:
Execute o Script A:
- Série de declarações
- Pausa
- Executar script B
- Pausa
- Executar script C
Sei por experiência própria que, se eu executar o script A até a primeira pausa, e fizer edições no script B, essas edições serão refletidas na execução do código quando eu permitir que ele seja retomado. Da mesma forma, se eu fizer edições no script C enquanto o script A ainda estiver em pausa e permitir que continue após salvar as alterações, essas alterações serão refletidas na execução do código.
Aqui está a verdadeira questão: existe alguma maneira de editar o Script A enquanto ele ainda está em execução? Ou é impossível editar uma vez que sua execução começa?
fonte
Respostas:
No Unix, a maioria dos editores trabalha criando um novo arquivo temporário que contém 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 impedir o dataloss.) Esse é, por exemplo, o estilo usado por
sed
ouperl
quando chamado com a-i
bandeira ("in-place"), que não é realmente "in-place". Deveria ter sido chamado de "novo local com nome antigo".Isso funciona bem porque o unix garante (pelo menos para sistemas de arquivos locais) que um arquivo aberto continue existindo até que seja 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 chamada de "desvincular".) Portanto, de um modo geral, se um interpretador de shell tiver algum arquivo de origem aberto e você "editar" o arquivo da maneira descrita acima , o shell nem verá as alterações, pois ainda possui o arquivo original aberto.
[Nota: como em todos os comentários baseados em padrões, os itens acima estão sujeitos a várias interpretações e existem vários casos de canto, como o NFS. Os pedestres são convidados a 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, embora você possa sobrescrever dados em um arquivo, não é possível excluir ou inserir sem alterar todos os dados a seguir, o que implicaria muita reescrita. Além disso, enquanto você fazia essa troca, o conteúdo do arquivo seria imprevisível e os processos que tinham o arquivo aberto seriam prejudicados. Para se livrar disso (como nos 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 muito além do escopo de um utilitário típico de edição de arquivos.
Portanto, se você deseja editar um arquivo enquanto este está sendo processado por um shell, você tem duas opções:
Você pode anexar ao arquivo. Isso sempre deve funcionar.
Você pode sobrescrever o arquivo com novos conteúdos exatamente do mesmo comprimento . Isso pode ou não funcionar, dependendo se o shell já leu essa parte do arquivo ou não. Como a maioria das E / S de arquivos envolve buffers de leitura, e como todos os shells que conheço leem um comando composto inteiro antes de executá-lo, é muito improvável que você possa se safar disso. Certamente não seria confiável.
Não conheço nenhuma redação no padrão Posix que realmente exija a possibilidade de anexar a um arquivo de script enquanto o arquivo está sendo executado, portanto, pode não funcionar com todos os shell compatíveis com Posix, muito menos com a oferta atual de quase e conchas às vezes compatíveis com posix. Então YMMV. Mas, tanto quanto eu sei, ele funciona de maneira confiável com o bash.
Como evidência, aqui está uma implementação "sem loop" do infame programa de 99 garrafas de cerveja no bash, que
dd
substitui e acrescenta (a substituição é presumivelmente segura porque substitui a linha em execução no momento, que é sempre a última linha do arquivo, com um comentário exatamente do mesmo comprimento; fiz isso para que o resultado final possa ser executado sem o comportamento de modificação automática.)fonte
export beer=100
antes de executar o script, ele funcionará conforme o esperado.bash
percorre um longo caminho para garantir que ele leia comandos antes de executá-los.Por exemplo, em:
O shell lerá o script por blocos, provavelmente lerá os dois comandos, interpretará o primeiro e, em seguida, procurará o final do
cmd1
script e lerá o script novamente para lêcmd2
-lo e executá-lo.Você pode verificar facilmente:
(embora olhando para a
strace
saída disso, parece que ele faz algumas coisas mais sofisticadas (como ler os dados várias vezes, procurar de volta ...) do que quando tentei o mesmo há alguns anos, então minha declaração acima sobre a busca por retorno pode não se aplica mais em versões mais recentes).Se, no entanto, você escrever seu script como:
O shell terá que ler até o fechamento
}
, armazenar na memória e executá-lo. Por causa dissoexit
, o shell não lê o script novamente, para que você possa editá-lo com segurança enquanto o shell o estiver interpretando.Como alternativa, ao editar o script, certifique-se de escrever uma nova cópia do script. O shell continuará lendo o original (mesmo que seja excluído ou renomeado).
Para fazer isso, mudar o nome
the-script
parathe-script.old
e copiarthe-script.old
parathe-script
e editá-lo.fonte
Não há realmente nenhuma maneira segura de modificar o script enquanto estiver em execução, porque o shell pode usar buffer para ler o arquivo. Além disso, se o script for modificado substituindo-o por um novo arquivo, os shells normalmente apenas lerão o novo arquivo após executar determinadas operações.
Geralmente, quando um script é alterado durante a execução, o shell acaba relatando erros de sintaxe. Isso ocorre porque, quando o shell fecha e reabre o arquivo de script, ele usa o deslocamento de bytes no arquivo para se reposicionar no retorno.
fonte
Você pode contornar isso definindo uma armadilha no seu script e usando
exec
para selecionar o novo conteúdo do script. Observe, no entanto, que aexec
chamada inicia o script do zero e não de onde chegou no processo em execução; portanto, o script B será chamado (e assim por diante).Isso continuará exibindo a data na tela. Eu poderia editar meu script e mudar
date
paraecho "Date: $(date)"
. Ao escrever isso, o script em execução ainda exibe a data. No entanto, se eu enviar o sinal que definitrap
para capturar, o scriptexec
(substitui o processo atual em execução pelo comando especificado), que é o comando$CMD
e os argumentos$@
. Você pode fazer isso emitindokill -1 PID
- onde PID é o PID do script em execução - e a saída muda para mostrarDate:
antes dadate
saída do comando.Você pode armazenar o "estado" do seu script em um arquivo externo (em / tmp) e ler o conteúdo para saber onde "retomar" quando o programa for reexecutado. Você pode então adicionar uma terminação de traps adicionais (SIGINT / SIGQUIT / SIGKILL / SIGTERM) para limpar esse arquivo tmp, portanto, quando você reiniciar após interromper o "Script A", ele começará do início. Uma versão com estado seria algo como:
fonte
$0
e$@
no início do script e usando essas variáveis noexec
lugar.