A única grande diferença é entre a fonte e a execução de um script. source foo.sh
irá fornecê-lo e todos os outros exemplos que você mostrar estão executando. Em mais detalhes:
./file.sh
Isso executará um script chamado file.sh
que está no diretório atual ( ./
). Normalmente, quando você executa command
, o shell procura nos diretórios do seu $PATH
arquivo executável chamado command
. 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
É basicamente o mesmo que, ./file.sh
exceto que, em vez de procurar no diretório atual file.sh
, ele está procurando no diretório pai ( ../
).
sh file.sh
Isso é equivalente a sh ./file.sh
, como acima, executará o script chamado file.sh
no diretório atual. A diferença é que você o está executando explicitamente com o sh
shell. Nos sistemas Ubuntu, isso é dash
e não bash
. Geralmente, os scripts têm uma linha shebang que fornece o programa em que 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 imprime 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, a chamada de um script shell script
substituirá a linha shebang (se houver) e executará o script com qualquer shell que você indicar.
source file.sh
ou . file.sh
Isso é chamado, surpreendentemente, como fonte do script. A palavra source
- chave é um alias para o .
comando embutido no shell . Essa é uma maneira de executar o script no shell atual. Normalmente, quando um script é executado, ele é executado em seu próprio shell, que é diferente do atual. Ilustrar:
$ cat foo.sh
#!/bin/bash
foo="Script"
echo "Foo (script) is $foo"
Agora, se eu definir a variável foo
para outra coisa no shell pai e executar o script, o script imprimirá um valor diferente de foo
(porque também é definido no script), mas o valor de foo
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 originar o script em vez de executá-lo, ele será executado no mesmo shell, portanto o valor de foo
no pai será 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
Portanto, o sourcing é usado nos poucos casos em que você deseja que um script afete o shell do qual está sendo executado. Geralmente é usado para definir variáveis de shell e disponibilizá-las após a conclusão do script.
Com tudo isso em mente, a razão pela qual você obtém respostas diferentes é, antes de tudo, que seu script não faz o que você pensa. 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 essa é outra discussão). Para esclarecer, simplifiquei um pouco o seu script 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 maneiras, 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 por tudo o que estiver definido lá. Isso afeta a maneira como o script é mostrado na saída de ps
. Em vez de ser listado como bash foo.sh
, ele será mostrado apenas como o foo.sh
que significa que você grep
sentirá falta dele. Na verdade, existem três instâncias do bash em execução: o processo pai, o bash executando o script e outro que executa o ps
comando . Este último é importante, o lançamento de um comando com substituição de comando ( `command`
ou $(command)
) resulta em uma cópia do shell pai sendo iniciada e que executa o comando. Aqui, no entanto, nada disso é mostrado devido à maneira que ps
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, como você está executando bash foo.sh
, a saída de ps
será mostrada bash foo.sh
e contada. Portanto, aqui temos o processo pai, a bash
execução do script e o shell clonado (executando o ps
), todos mostrados porque agora ps
mostrarão cada um deles porque seu comando incluirá a palavra bash
.
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ão bash
. Portanto, a única bash
instância é o shell pai no qual você iniciou seu script. Todas as outras conchas mencionadas acima estão sendo executadas sh
.
Sourcing (por .
ou a source
mesma coisa)
$ . ./foo.sh
The number of shells opened by terdon is 2
Como expliquei acima, o fornecimento de um script faz com que ele seja executado no mesmo shell que o processo pai. No entanto, um subshell separado é iniciado para iniciar o ps
comando e eleva o total para dois.
Como nota final, a maneira correta de contar os processos em execução não é analisar, ps
mas usá-la 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 é (observe 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 iniciado, sh
pois o processo filho não é bash
.