A única grande diferença é entre o fornecimento e a execução de um script. source foo.sh irá obtê-lo e todos os outros exemplos mostrados estão sendo executados. Mais detalhadamente:
-
./file.shIsso executará um script chamado
file.shque está no diretório atual (./). Normalmente, quando você executacommand, o shell irá procurar pelos diretórios no seu$PATHpara um arquivo executável chamadocommand. Se você fornecer um caminho completo, como/usr/bin/commandou./command, o$PATHserá ignorado e esse arquivo específico será executado. -
../file.shIsso é basicamente o mesmo que
./file.sh, exceto que, em vez de procurar no diretório atual porfile.sh, ele está procurando no diretório pai (../). -
sh file.shEste equivalente a
sh ./file.sh, como acima, executará o script chamadofile.shno diretório atual. A diferença é que você está executando explicitamente com o shellsh. Nos sistemas Ubuntu, isso édashe nãobash. Geralmente, os scripts têm uma linha shebang que fornece o programa para o qual devem ser executados. Chamá-los com um diferente substitui isso. Por exemplo:$ cat foo.sh #!/bin/bash ## The above is the shebang line, it points to bash ps h -p $$ -o args='' | cut -f1 -d' ' ## This will print the name of the shellEsse script simplesmente imprimirá o nome do shell usado para executá-lo. Vamos ver o que ele retorna quando chamado de maneiras diferentes:
$ bash foo.sh bash $ sh foo.sh sh $ zsh foo.sh zshPortanto, chamar chamando um script com
shell scriptsubstituirá a linha shebang (se presente) e executará o script com qualquer shell que você disser. -
source file.shou. file.shIsso é chamado, surpreendentemente, sourcing o script. A palavra-chave
sourceé um alias para o comando shell builtin.. Esta é uma maneira de executar o script dentro do shell atual. Normalmente, quando um script é executado, ele é executado em seu próprio shell, que é diferente do atual. Para ilustrar:$ cat foo.sh #!/bin/bash foo="Script" echo "Foo (script) is $foo"Agora, se eu definir a variável
foocomo outra coisa no shell pai e depois executar o script, o script imprimirá um valor diferente defoo(porque também é definido no script), mas o valor defoono shell pai não será alterado:$ foo="Parent" $ bash foo.sh Foo (script) is Script ## This is the value from the script's shell $ echo "$foo" Parent ## The value in the parent shell is unchangedNo entanto, se eu usar o script em vez de executá-lo, ele será executado no mesmo shell para que o valor de
foono pai seja alterado:$ source ./foo.sh Foo (script) is Script ## The script's foo $ echo "$foo" Script ## Because the script was sourced, ## the value in the parent shell has changedAssim, o sourcing é usado nos poucos casos em que você deseja que um script afete o shell do qual está sendo executado. É normalmente usado para definir variáveis de shell e disponibilizá-las depois que o script é concluído.
Com tudo isso em mente, a razão pela qual você obtém respostas diferentes é, antes de mais nada, que seu script não faz o que você acha que faz. Ele conta o número de vezes que bash aparece na saída de ps . Este não é o número de terminais abertos , é o número de shells em execução (na verdade, nem é isso, mas isso é outra discussão). Para esclarecer, simplifiquei um pouco seu roteiro para isso:
#!/bin/bash
logname=terdon
not='ps -au$logname | grep -c bash'
echo "The number of shells opened by $logname is $not"
E execute-o de várias formas com apenas um único terminal aberto:
-
Lançamento direto,
./foo.sh.$ ./foo.sh The number of shells opened by terdon is 1Aqui, você está usando a linha shebang. Isso significa que o script é executado diretamente pelo que está definido lá. Isso afeta a maneira como o script é mostrado na saída de
ps. Em vez de ser listado comobash foo.sh, ele será mostrado apenas comofoo.sh, o que significa que seugrepnão o verá. Na verdade, existem 3 instâncias bash em execução: o processo pai, o bash executando o script e outro que executa o comandops. Este último é importante, a ativação de um comando com substituição de comando ('command'ou$(command)) resulta em uma cópia do shell pai que está sendo ativado e que executa o comando. Aqui, no entanto, nenhum deles é mostrado devido à maneira comopsmostra sua saída. -
Lançamento direto com shell explícito (bash)
$ bash foo.sh The number of shells opened by terdon is 3Aqui, porque você está executando com
bash foo.sh, a saída depsmostrarábash foo.she será contada. Então, aqui temos o processo pai, obashexecutando o script e o shell clonado (executando ops) todos mostrados porque agorapsmostrará cada um deles porque seu comando será inclua a palavrabash. -
Lançamento direto com um shell diferente (
sh)$ sh foo.sh The number of shells opened by terdon is 1Isso é diferente porque você está executando o script com
she nãobash. Portanto, a únicabashinstance é o shell pai no qual você iniciou o script. Todos os outros shells mencionados acima estão sendo executados porsh. -
Terceirização (por
.ousource, mesma coisa)$ . ./foo.sh The number of shells opened by terdon is 2Como expliquei acima, a obtenção de um script faz com que ele seja executado no mesmo shell do processo pai. No entanto, um subshell separado é iniciado para iniciar o comando
pse isso eleva o total para dois.
Como nota final, a maneira correta de contar os processos em execução não é analisar ps , mas usar pgrep . Todos esses problemas teriam sido evitados se você tivesse acabado de executar
pgrep -cu terdon bash
Portanto, uma versão funcional do seu script que sempre imprime o número correto é (note a ausência de substituição de comando):
#!/usr/bin/env bash
user="terdon"
printf "Open shells:"
pgrep -cu "$user" bash
Isso retornará 1 quando originado e 2 (porque um novo bash será lançado para executar o script) para todas as outras formas de lançamento. Ele ainda retornará 1 quando lançado com sh , pois o processo filho não é bash .