Muitas vezes acontece que você chega à máquina e encontra algum tipo de script em execução no usuário do sistema há uma semana. Quem o lançou? Onde procurar este run.php? Ou você adiciona uma entrada ao / etc / crontab e o script trava ali com o erro "comando não encontrado". Porque E o que fazer?
Eu tenho as respostas para essas perguntas.

Variáveis de ambiente
Em quase todos os sistemas operacionais modernos, os processos têm variáveis de ambiente. Tecnicamente, eles são uma coleção de cadeias nomeadas. Se um subprocesso for iniciado, ele herdará automaticamente uma cópia do ambiente do pai.
Entre outras, há a variável PATH, que indica os caminhos para procurar arquivos executáveis, a variável HOME, que indica o diretório inicial do usuário, as variáveis responsáveis pelas preferências de idioma do usuário e muitas outras.
Existem muitas revisões descrevendo o significado dessas variáveis, mas praticamente não existem artigos sobre como investigar problemas. Preencha esta lacuna.
Quem iniciou o processo?
Então, encontramos um script em execução no usuário do sistema há uma semana. Quem o lançou? Porque Talvez eles tenham esquecido dele? Potencialmente entre 10 e 15 pessoas podem lançá-lo, você não entrevista todos. Como descobrir quem era? E onde esse run.php está?
$ ps x | grep run.php 10684 ? Ss 472:25 /local/php/bin/php run.php
As variáveis de ambiente do processo e o recurso sudo são úteis. Existe uma variável PWD na qual o shell armazena o diretório de trabalho atual; esse valor, de fato, salva informações sobre o diretório atual no momento em que o comando é executado. Além disso, o utilitário sudo, por padrão, deixa informações na variável de ambiente do processo sobre qual usuário ele foi iniciado.
Variáveis de ambiente (e muito mais) para qualquer processo em execução podem ser encontradas em / proc. Voila:
$ cat /proc/10684/environ | tr '\0' '\n' | grep SUDO_USER SUDO_USER=alexxz $ cat /proc/10684/environ | tr '\0' '\n' | grep PWD PWD=/home/etlmaster
Ahem, eu mesmo a lancei. Bem, quem não acontece?
Em geral, usando um método tão simples em situações simples, você pode encontrar informações sobre o processo, que geralmente não está disponível.
O script funciona na linha de comando, mas não funciona no cron
Um dos casos em que você precisa pensar em variáveis de ambiente é quando um script adicionado ao / etc / crontab trava com um erro. Você vai ao servidor via SSH, executa o comando, tudo parece funcionar como deveria. E quando é iniciado automaticamente, mostra algo como "hive: command not found".
Em geral, é uma boa prática escrever o caminho completo para comandos executáveis, mas isso nem sempre é possível. Nesses casos, os desenvolvedores saem como qualquer um pode. Alguém adiciona o caminho desejado no PATH como parte da equipe do crontab. Os mais experientes envolvem seu comando no bash -l. E as bombas corvos ensinadas por uma experiência amarga ainda não esquecem de se reunir. Tudo é assim: feito, adicionado ao monitoramento e esquecido.
Após essas manipulações, um sedimento permanece na alma de um verdadeiro engenheiro. Sim, o problema está resolvido. Mas eu não entendi o que estava acontecendo! Como uma abordagem é melhor que outra? Onde todas essas configurações são armazenadas e por quem elas são alteradas?
Vamos comparar as variáveis de ambiente que o processo possui quando é iniciado a partir da coroa e as variáveis de ambiente que temos na linha de comando. Registramos a saída do comando env da coroa e nosso ambiente atual:
$ echo "* * * * * env > ~/crontab.env" | crontab; sleep 60; echo "" | crontab; $ env > my.env
Veja o que está na variável PATH:
> grep ^PATH= crontab.env my.env Crontab.env: PATH=/usr/bin:/bin My.env: PATH=/local/hive/bin:/local/python/bin:/local/hadoop/bin:/local/hadoop/bin:/local/hive/bin:/local/hadoop/bin:/usr/local/bin:/usr/bin:/bin

Mama Mia! Então, sob a coroa apenas o mínimo! Obviamente, você precisa carregar as variáveis de ambiente normais.
Vamos ver qual será o ambiente se adicionarmos bash -l:
$ echo "* * * * * bash -l env > ~/crontab.env" | crontab; sleep 60; echo "" | crontab; alexxz@bi1.mlan:~> grep ^PATH= crontab.env my.env Crontab.env: PATH=/local/hive/bin:/local/python/bin:/local/hadoop/bin:/local/hadoop/bin:/local/hadoop/bin:/local/hive/bin:/usr/local/bin:/usr/bin:/bin My.env: PATH=/local/hive/bin:/local/python/bin:/local/hadoop/bin:/local/hadoop/bin:/local/hive/bin:/local/hadoop/bin:/usr/local/bin:/usr/bin:/bin
A diferença não é tão perceptível. Todos os caminhos são apresentados. Alguns em uma ordem diferente, outros são repetidos, mas isso já é muito melhor do que era. O restante das variáveis também está bem ajustado. Obviamente, há uma pequena diferença no local, nas variáveis do SSH, mas isso não deve mais afetar drasticamente a operação do script.
Agora está claro por que bash -l é necessário nas entradas do crontab. E, claro, não se esqueça do rebanho.
Inicialização de depuração de scripts de logon
O problema parece estar resolvido, tudo da coroa funciona. Mas como é que alguns caminhos são duplicados na variável PATH? Portanto, há algum tipo de confusão na configuração do servidor. Vamos tentar descobrir.
Abrimos um homem para inicializar o ambiente, lemos quais scripts e em que ordem são executados, com entusiasmo começamos a passar por seus olhos - e depois de alguns minutos surge uma sensação de desespero. Algum fluxo interminável de condições sobre alguns casos especiais de arquiteturas, terminais e configurações de cores incrivelmente importantes para o comando ls. Dor, desespero, ódio! Estamos interessados em uma maldita variável PATH!
De fato, tudo é um pouco mais simples. Conheça:
env -i bash -x -l -c 'echo 123' > login.log 2>&1
O que essa equipe faz? Cria um novo processo bash com um ambiente puro, indica que é necessário executar scripts de inicialização e proteger tudo em detalhes no arquivo login.log. Agora temos a oportunidade de não executar todos os scripts em nossa mente, mas simplesmente ler o que, onde e quando foi executado e de onde veio esse ou aquele ambiente.
Não analisarei em detalhes como ler o log resultante. Tudo é quase trivial lá. Mencionei apenas que um hit veio de / etc / profile e dois de /etc/bash.bashrc. Sim, em algum lugar eles eram muito inteligentes ao montar pacotes em um pappet. Bem, nada, não me incomoda trabalhar.
Mas agora eu sei e posso!
PS Em casos muito difíceis e para entender tudo, você pode agrupar o comando em strace:
strace -f env -i bash -x -l -c 'echo 123' > login.log 2>&1