Edite o script de shell durante a execução

91

Você pode editar um script de shell enquanto ele está sendo executado e as alterações afetam o script em execução?

Estou curioso sobre o caso específico de um script csh. Eu tenho aquele lote que executa vários tipos de compilação diferentes e executa a noite toda. Se algo me ocorrer no meio da operação, gostaria de entrar e adicionar comandos adicionais ou comentar os não executados.

Se não for possível, existe algum shell ou mecanismo de lote que me permita fazer isso?

Claro que tentei, mas demorarei horas antes de ver se funcionou ou não, e estou curioso para saber o que está acontecendo ou não nos bastidores.

ack
fonte
1
Eu vi dois resultados ao editar o arquivo de script para um script em execução: 1) as alterações são ignoradas como se ele tivesse lido tudo na memória ou 2) o script trava com um erro como se tivesse lido parte do comando. Não sei se isso depende do tamanho do script. De qualquer forma, eu não tentaria.
Paul Tomblin
Resumindo: não, a menos que seja autorreferencial / chamativo, caso em que o script principal ainda seria o antigo.
Wrikken
Existem duas questões importantes aqui. 1) Como posso adicionar comandos de forma correta e segura a um script em execução? 2) Quando eu modifico um script em execução, o que acontece?
Chris Quenelle de
3
A questão é se um shell executa um script lendo todo o arquivo de script e, em seguida, executando-o, ou lendo-o parcialmente durante a execução. Eu não sei o que é; pode nem mesmo ser especificado. Você deve evitar depender de qualquer um dos comportamentos.
Keith Thompson

Respostas:

-67

Os scripts não funcionam dessa maneira; a cópia em execução é independente do arquivo de origem que você está editando. Da próxima vez que o script for executado, ele será baseado na versão salva mais recentemente do arquivo de origem.

Pode ser sábio dividir esse script em vários arquivos e executá-los individualmente. Isso reduzirá o tempo de execução até a falha. (ou seja, divida o lote em um script de tipo de compilação, executando cada um individualmente para ver qual está causando o problema).

vau
fonte
66
Eu observei o oposto. A execução de scripts bash que são editados pode fazer com que o script em execução trave porque o arquivo parece se mover sob a posição do arquivo de leitura do script do bash.
Tilman Vogel
10
Em minha experiência em vários sistemas, a cópia em execução NÃO é independente do arquivo de disco, é por isso que esse problema é tão surpreendente e importante na programação de script de shell.
Chris Quenelle de
6
Definitivamente, não é independente do arquivo do disco. O shell geralmente apenas lê os scripts em blocos de, por exemplo, 128 bytes ou 4096 bytes ou 16384 bytes, e só lê o próximo bloco quando precisa de uma nova entrada. (Você pode fazer coisas como lsof em um shell executando um script e ver que o arquivo ainda está aberto.)
mirabilos
5
Não. Na verdade, se você editar um script, o processo falhará.
Erik Aronesty
8
Você não está correto. Ele é armazenado em buffer dependendo da implementação e do comando real sendo chamado no script, se stdout for redirecionado para um arquivo, existem muitos fatores e sua resposta não está simplesmente correta.
GL2014
50

Ele não afeta, pelo menos, o bash no meu ambiente, mas na maneira muito desagradável . Veja esses códigos. Primeiro a.sh:

#!/bin/sh

echo "First echo"
read y

echo "$y"

echo "That's all."

b.sh:

#!/bin/sh

echo "First echo"
read y

echo "Inserted"

echo "$y"

# echo "That's all."

Faz

$ cp a.sh run.sh
$ ./run.sh
$ # open another terminal
$ cp b.sh run.sh  # while 'read' is in effect
$ # Then type "hello."

No meu caso, a saída é sempre:

Olá
Olá
Isso é tudo.
Isso é tudo.

(Claro que é muito melhor automatizá-lo, mas o exemplo acima é legível.)

[editar] Isso é imprevisível, portanto perigoso. A melhor solução alternativa é , conforme descrito aqui, colocar tudo em uma chave e, antes da chave de fechamento, colocar "sair" . Leia bem a resposta do link a para evitar armadilhas.

[adicionado] O comportamento exato depende de uma nova linha extra, e talvez também de seu tipo de Unix, sistema de arquivos, etc. Se você simplesmente deseja ver algumas influências, simplesmente adicione "echo foo / bar" a b.sh antes e / ou depois a linha "ler".

teika kazura
fonte
3
Mh, eu não vejo o carinho. Estou esquecendo de algo?
usuário desconhecido
O comportamento exato depende de uma nova linha extra , e talvez também do tipo Unix, sistema de arquivos, etc, embora não tenhamos certeza de nada. Se você simplesmente deseja ver quaisquer influências, simplesmente amplie b.shadicionando 10 linhas de echo foo / bar / baz. A essência das respostas de dave4220 e de mim é que o efeito não é fácil de prever. (Aliás, o substantivo "afeto" significa "amor" =)
teika kazura de
sim, está muito quebrado. eu tenho uma solução (abaixo). o que é ainda mais perigoso são as atualizações de svn / rsync / git
Erik Aronesty
39

Tente isto ... crie um arquivo chamado bash-is-odd.sh:

#!/bin/bash
echo "echo yes i do odd things" >> bash-is-odd.sh

Isso demonstra que o bash está, de fato, interpretando o script "conforme você avança". De fato, editar um script de longa execução tem resultados imprevisíveis, inserindo caracteres aleatórios etc. Por quê? Como o bash lê a partir da posição do último byte, a edição muda a localização do caractere atual que está sendo lido.

O Bash é, em uma palavra, muito, muito inseguro por causa desse "recurso". O svn e rsyncquando usado com scripts bash são particularmente problemáticos, porque por padrão eles "mesclam" os resultados ... editando no local. rsynctem um modo que corrige isso. svn e git não.

Eu apresento uma solução. Crie um arquivo chamado /bin/bashx:

#!/bin/bash
source "$1"

Agora use #!/bin/bashxem seus scripts e sempre execute-os com em bashxvez de bash. Isso corrige o problema - você pode garantir rsyncseus scripts.

Solução alternativa (em linha) proposta / testada por @ AF7:

{
   # your script
} 
exit $?

Os suspensórios encaracolados protegem contra edições e a saída protege contra anexos. Claro, estaríamos todos muito melhor se o bash viesse com uma opção, como -w(arquivo inteiro), ou algo que fizesse isso.

Erik Aronesty
fonte
1
Btw; aqui está um ponto positivo para combater o ponto negativo e porque gosto da sua resposta editada.
Andrew Barber
1
Eu não posso recomendar isso. Nesta solução alternativa, os parâmetros posicionais são alterados em um. Lembre-se também de que você não pode atribuir um valor a $ 0. Isso significa que se você simplesmente alterar "/ bin / bash" para "/ bin / bashx", muitos scripts falharão.
teika kazura
1
Diga-me que essa opção já foi implementada!
AF7
10
Uma solução simples, sugerida pelo meu amigo Giulio (créditos quando devidos) é inserir {no início e} no final do scritp. Bash é forçado a ler tudo na memória.
AF7
1
@ AF7 melhorando a solução do seu amigo: {your_code; } && Saída; irá evitar que as linhas anexadas ao final sejam executadas também.
korkman
17

Divida seu script em funções e, cada vez que uma função for chamada, sourceela será feita em um arquivo separado. Em seguida, você pode editar os arquivos a qualquer momento e o script em execução pegará as alterações na próxima vez que for originado.

foo() {
  source foo.sh
}
foo
glenn jackman
fonte
Eu tenho usado essa técnica de forma eficaz por um tempo para atualizar meus scripts de compilação de longa execução enquanto eles estão em execução. Adoraria aprender uma técnica para fazer com que o arquivo atual seja lido até o final do arquivo, de forma que não precise ter dois arquivos para implementar cada script de shell.
Chris Quenelle de
3

Boa pergunta! Espero que este script simples ajude

#!/bin/sh
echo "Waiting..."
echo "echo \"Success! Edits to a .sh while it executes do affect the executing script! I added this line to myself during execution\"  " >> ${0}
sleep 5
echo "When I was run, this was the last line"

Parece no Linux que as alterações feitas em um .sh em execução são realizadas pelo script em execução, se você puder digitar rápido o suficiente!

Will Turner
fonte
2

Uma observação interessante - se você está executando um script Python, ele não muda. (Isso provavelmente é flagrantemente óbvio para qualquer pessoa que entende como o shell executa scripts Python, mas pensou que poderia ser um lembrete útil para quem procura por essa funcionalidade.)

Eu criei:

#!/usr/bin/env python3
import time
print('Starts')
time.sleep(10)
print('Finishes unchanged')

Então, em outro shell, enquanto ele está dormindo, edite a última linha. Quando isso é concluído, ele exibe a linha inalterada, provavelmente porque está executando um .pyc? O mesmo acontece no Ubuntu e no macOS.

Chris
fonte
1

Não tenho csh instalado, mas

#!/bin/sh
echo Waiting...
sleep 60
echo Change didn't happen

Execute isso, edite rapidamente a última linha para ler

echo Change happened

A saída é

Waiting...
/home/dave/tmp/change.sh: 4: Syntax error: Unterminated quoted string

Hrmph.

Eu acho que as edições nos scripts de shell não têm efeito até que sejam executados novamente.

dave4420
fonte
2
você deve colocar a string que deseja exibir entre aspas.
user1463308
2
na verdade, prova que seu editor não funciona da maneira que você pensa. muitos, muitos editores (incluindo vim, emacs) operam em um arquivo "tmp", e não no arquivo live. Tente usar "echo 'echo uh oh' >> myshell.sh" em vez de vi / emacs ... e observe como ele mostra as novas coisas. Pior ... svn e rsync também editam desta forma!
Erik Aronesty,
3
-1. Esse erro não está relacionado ao arquivo que está sendo editado: é porque você está usando um apóstrofo! Isso funciona como uma aspa simples, causando o erro. Coloque toda a string entre aspas duplas e tente novamente.
Anonymous Penguin
5
O fato de o erro ter ocorrido mostra que a edição não teve o efeito desejado.
danmcardle
@danmcardle Quem sabe? Talvez bash viu Change didn'ned.
Kirill Bulygin
1

Se tudo isso estiver em um único script, não, não funcionará. No entanto, se você configurá-lo como um script de driver chamando subscripts, poderá alterar um subscript antes de ser chamado ou antes de ser chamado novamente se você estiver em loop e, nesse caso, acredito que essas alterações seria refletido na execução.

Ethan Shepherd
fonte
0

Estou ouvindo não ... mas e com alguma indireção:

BatchRunner.sh

Command1.sh
Command2.sh

Command1.sh

runSomething

Command2.sh

runSomethingElse

Então você deve ser capaz de editar o conteúdo de cada arquivo de comando antes que o BatchRunner o faça certo?

OU

Uma versão mais limpa faria com que o BatchRunner olhasse para um único arquivo onde seria executado consecutivamente uma linha por vez. Então você deve ser capaz de editar este segundo arquivo enquanto o primeiro está rodando, certo?

ack
fonte
Eu me pergunto se ele os carrega na memória para executá-los e uma mudança não importa quando o processo principal é iniciado ...
Eric Hodonsky
0

Use Zsh ao invés para seu script.

AFAICT, Zsh não exibe esse comportamento frustrante.

Micah Elliott
fonte
Esta é a razão # 473 para preferir o Zsh ao bash. Recentemente, estive trabalhando em um script bash antigo que leva 10 minutos para ser executado e não posso editá-lo enquanto espero sua conclusão!
Micah Elliott
-5

geralmente, é incomum editar seu script durante sua execução. Tudo que você precisa fazer é colocar na verificação de controle para suas operações. Use instruções if / else para verificar as condições. Se algo falhar, faça isso, senão aquilo. Esse é o caminho a percorrer.

ghostdog74
fonte
Na verdade, trata-se menos de falha de scripts do que de decisão de modificar o trabalho em lote durante a operação. IE percebendo que há mais coisas que eu quero compilar ou que não preciso de certos trabalhos que já estão na fila.
ack
1
Se você anexar estritamente a scripts, o bash fará o que você espera!
Erik Aronesty,