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.sh
Isso executará um script chamado
file.sh
que está no diretório atual (./
). Normalmente, quando você executacommand
, o shell irá procurar pelos diretórios no seu$PATH
para um arquivo executável chamadocommand
. Se você fornecer um caminho completo, como/usr/bin/command
ou./command
, o$PATH
será ignorado e esse arquivo específico será executado. -
../file.sh
Isso é 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.sh
Este equivalente a
sh ./file.sh
, como acima, executará o script chamadofile.sh
no diretório atual. A diferença é que você está executando explicitamente com o shellsh
. Nos sistemas Ubuntu, isso édash
e 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 shell
Esse 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 zsh
Portanto, chamar chamando um script com
shell script
substituirá a linha shebang (se presente) e executará o script com qualquer shell que você disser. -
source file.sh
ou. file.sh
Isso é 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
foo
como 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 defoo
no 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 unchanged
No entanto, se eu usar o script em vez de executá-lo, ele será executado no mesmo shell para que o valor de
foo
no 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 changed
Assim, 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 1
Aqui, 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 seugrep
nã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 comops
mostra sua saída. -
Lançamento direto com shell explícito (bash)
$ bash foo.sh The number of shells opened by terdon is 3
Aqui, porque você está executando com
bash foo.sh
, a saída deps
mostrarábash foo.sh
e será contada. Então, aqui temos o processo pai, obash
executando o script e o shell clonado (executando ops
) todos mostrados porque agoraps
mostrará 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 1
Isso é diferente porque você está executando o script com
sh
e nãobash
. Portanto, a únicabash
instance é 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 2
Como 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
ps
e 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
.