Geralmente, se você editar um scrpit, todos os usos em execução do script estarão sujeitos a erros.
Pelo que entendi, o bash (outras conchas também?) Lê o script de forma incremental; portanto, se você modificou o arquivo de script externamente, ele começa a ler as coisas erradas. Existe alguma maneira de evitá-lo?
Exemplo:
sleep 20
echo test
Se você executar esse script, o bash lerá a primeira linha (digamos 10 bytes) e entrará no modo de suspensão. Quando ele é reiniciado, pode haver conteúdos diferentes no script, começando no décimo-th byte. Eu posso estar no meio de uma linha no novo script. Assim, o script em execução será quebrado.
\n
? Talvez um subshell()
faça? Não tenho muita experiência com isso, por favor, ajude!sleep 20 ;\n echo test ;\n sleep 20
e eu começar a editá-lo, ele poderá se comportar mal. Por exemplo, o bash pode ler os 10 primeiros bytes do script, entender osleep
comando e dormir. Após o reinício, haveria conteúdos diferentes no arquivo, iniciando em 10 bytes.Respostas:
Sim, os shells e,
bash
em particular, são cuidadosos ao ler o arquivo uma linha de cada vez, para que funcione da mesma maneira que quando você o usa interativamente.Você notará que, quando o arquivo não é procurável (como um pipe), ele
bash
lê um byte de cada vez para ter certeza de não ler além do\n
caractere. Quando o arquivo é procurável, ele otimiza lendo blocos inteiros de cada vez, mas procura novamente após o arquivo\n
.Isso significa que você pode fazer coisas como:
Ou escreva scripts que se atualizem. O que você não seria capaz de fazer se não lhe desse essa garantia.
Agora, é raro que você queira fazer coisas assim e, como você descobriu, esse recurso tende a atrapalhar com mais frequência do que é útil.
Para evitá-lo, você pode tentar garantir que não modifique o arquivo no local (por exemplo, modifique uma cópia e mova a cópia no local (como
sed -i
ouperl -pi
alguns editores fazem, por exemplo)).Ou você pode escrever seu script como:
(observe que é importante que
exit
esteja na mesma linha que}
; embora você também possa colocá-lo dentro do aparelho antes do fechamento).ou:
O shell precisará ler o script até
exit
antes de começar a fazer qualquer coisa. Isso garante que o shell não leia novamente o script.Isso significa que o script inteiro será armazenado na memória.
Isso também pode afetar a análise do script.
Por exemplo, em
bash
:Saída que U + 00E9 codificado em UTF-8. No entanto, se você alterar para:
O
\ue9
será expandido no conjunto de caracteres que estava em vigor no momento em que o comando foi analisado, o que, neste caso, é antes doexport
comando ser executado.Observe também que, se o comando
source
aka.
for usado, com algumas conchas, você terá o mesmo tipo de problema para os arquivos de origem.Não é esse o caso de
bash
cujosource
comando lê o arquivo completamente antes de interpretá-lo. Se você escreverbash
especificamente, poderá usar isso adicionando no início do script:(Eu não confiaria nisso, pois, como você pode imaginar, as versões futuras
bash
podem mudar esse comportamento que atualmente pode ser visto como uma limitação (o bash e o AT&T ksh são os únicos shells do tipo POSIX que se comportam dessa maneira até onde se sabe) e oalready_sourced
truque é um pouco frágil, pois supõe que a variável não está no ambiente, sem mencionar que afeta o conteúdo da variável BASH_SOURCE)fonte
}; exec true
. Dessa forma, não há necessidade de novas linhas no final do arquivo, o que é amigável para alguns editores (como o emacs). Todos os testes com os quais eu consegui pensar funcionavam corretamente}; exec true
}; exit
? Você também está perdendo o status de saída.. script
) seja usado.Você simplesmente precisa excluir o arquivo (ou seja, copiá-lo, excluí-lo, renomear a cópia novamente para o nome original). De fato, muitos editores podem ser configurados para fazer isso por você. Quando você edita um arquivo e salva um buffer alterado, em vez de substituí-lo, ele renomeia o arquivo antigo, cria um novo e coloca o novo conteúdo no novo arquivo. Portanto, qualquer script em execução deve continuar sem problemas.
Ao usar um sistema de controle de versão simples como o RCS, que está prontamente disponível para vim e emacs, você obtém a dupla vantagem de ter um histórico de suas alterações, e o sistema de checkout deve, por padrão, remover o arquivo atual e recriá-lo com os modos corretos. (Evite vincular esses arquivos, é claro).
fonte
Solução mais simples:
Dessa forma, o bash lerá todo o
{}
bloco antes de executá-lo e aexit
diretiva garantirá que nada seja lido fora do bloco de código.Se você não deseja "executar" o script, mas sim "originá-lo", precisa de uma solução diferente. Isso deve funcionar então:
Ou se você deseja controle direto sobre o código de saída:
Voilà! Este script é seguro para editar, fonte e executar. Você ainda precisa ter certeza de que não o modifica nesses milissegundos quando está sendo lido inicialmente.
fonte
Prova de conceito. Aqui está um script que se modifica:
vemos a versão alterada imprimir
Isso ocorre porque o bash load mantém um identificador de arquivo para abrir o script, portanto as alterações no arquivo serão vistas imediatamente.
Se você não deseja atualizar a cópia na memória, desvincule o arquivo original e substitua-o.
Uma maneira de fazer isso é usando sed -i.
prova de conceito
Se você estiver usando um editor para alterar o script, talvez seja necessário ativar o recurso "manter uma cópia de backup" para fazer com que o editor grave a versão alterada em um novo arquivo, em vez de substituir o existente.
fonte
bash
não abre o arquivo commmap()
. É apenas cuidadoso ler uma linha de cada vez, conforme necessário, assim como quando está recebendo os comandos de um dispositivo terminal quando interativo.O agrupamento do script em um bloco
{}
provavelmente é a melhor opção, mas requer a alteração dos scripts.seria a segunda melhor opção (assumindo tmpfs ) a desvantagem é que ele quebra $ 0 se seus scripts o usarem.
usar algo como
F=test.sh; tail -n $(cat "$F" | wc -l) "$F" | bash
é menos ideal porque ele precisa manter o arquivo inteiro na memória e quebra $ 0.tocar no arquivo original deve ser evitado para que, na hora da última modificação, bloqueios de leitura e links físicos não sejam perturbados. Dessa forma, você pode deixar um editor aberto enquanto executa o arquivo e o rsync não faz a soma de verificação desnecessária do arquivo para backups e links físicos, conforme o esperado.
substituir o arquivo na edição funcionaria, mas é menos robusto, porque não é aplicável a outros scripts / usuários / ou pode-se esquecer. E, novamente, ele quebraria os links físicos.
fonte
tac test.sh | tac | bash