La única diferencia importante es entre el abastecimiento y la ejecución de un script. source foo.sh
lo buscará y todos los demás ejemplos que muestres se están ejecutando. Con más detalle:
./file.sh
Esto ejecutará un script llamado file.sh
que está en el directorio actual ( ./
). Normalmente, cuando ejecuta command
, el shell buscará en los directorios de su $PATH
archivo ejecutable llamado command
. Si proporciona una ruta completa, como /usr/bin/command
o ./command
, $PATH
se ignora y se ejecuta ese archivo específico.
../file.sh
Esto es básicamente lo mismo que, ./file.sh
excepto que en lugar de buscar en el directorio actual file.sh
, está buscando en el directorio padre ( ../
).
sh file.sh
Esto equivale a sh ./file.sh
, como arriba, ejecutará el script llamado file.sh
en el directorio actual. La diferencia es que lo está ejecutando explícitamente con el sh
shell. En los sistemas Ubuntu, eso es dash
y no bash
. Por lo general, los scripts tienen una línea shebang que le da al programa el que deben ejecutarse. Llamarlos con uno diferente anula eso. Por ejemplo:
$ 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
Ese script simplemente imprimirá el nombre del shell utilizado para ejecutarlo. Veamos qué devuelve cuando se llama de diferentes maneras:
$ bash foo.sh
bash
$ sh foo.sh
sh
$ zsh foo.sh
zsh
Entonces, llamar a llamar a un script con shell script
anulará la línea shebang (si está presente) y ejecutará el script con cualquier shell que le diga.
source file.sh
o . file.sh
Esto se llama, sorprendentemente, el abastecimiento del guión. La palabra clave source
es un alias para el .
comando incorporado de shell . Esta es una forma de ejecutar el script dentro del shell actual. Normalmente, cuando se ejecuta un script, se ejecuta en su propio shell, que es diferente del actual. Para ilustrar:
$ cat foo.sh
#!/bin/bash
foo="Script"
echo "Foo (script) is $foo"
Ahora, si configuro la variable foo
en otra cosa en el shell principal y luego ejecuto el script, el script imprimirá un valor diferente de foo
(porque también se establece dentro del script) pero el valor de foo
en el shell principal no cambiará:
$ 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
Sin embargo, si obtengo el script en lugar de ejecutarlo, se ejecutará en el mismo shell, por lo que se cambiará el valor de foo
en el padre:
$ 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
Por lo tanto, el abastecimiento se usa en los pocos casos en los que desea que un script afecte al shell desde el que lo está ejecutando. Normalmente se usa para definir variables de shell y tenerlas disponibles una vez que finaliza el script.
Con todo eso en mente, la razón por la que obtienes respuestas diferentes es, en primer lugar, que tu guión no hace lo que crees que hace. Cuenta el número de veces que bash
aparece en la salida de ps
. Este no es el número de terminales abiertos , es el número de shells en ejecución (de hecho, ni siquiera es eso, pero esa es otra discusión). Para aclarar, simplifiqué un poco su guión para esto:
#!/bin/bash
logname=terdon
not=`ps -au$logname | grep -c bash`
echo "The number of shells opened by $logname is $not"
Y ejecútelo de varias maneras con solo un terminal abierto:
Lanzamiento directo, ./foo.sh
.
$ ./foo.sh
The number of shells opened by terdon is 1
Aquí, estás usando la línea shebang. Esto significa que el script se ejecuta directamente por lo que esté configurado allí. Esto afecta la forma en que se muestra el script en la salida de ps
. En lugar de aparecer como bash foo.sh
, solo se mostrará como lo foo.sh
que significa que grep
lo perderá. En realidad, se están ejecutando 3 instancias de bash: el proceso principal, el bash que ejecuta el script y otro que ejecuta el ps
comando . Esto último es importante, el lanzamiento de un comando con sustitución de comando ( `command`
o $(command)
) da como resultado una copia del shell principal que se inicia y que ejecuta el comando. Aquí, sin embargo, ninguno de estos se muestra debido a la forma en que ps
muestra su salida.
Lanzamiento directo con shell explícito (bash)
$ bash foo.sh
The number of shells opened by terdon is 3
Aquí, porque está ejecutando con bash foo.sh
, la salida de ps
se mostrará bash foo.sh
y se contará. Entonces, aquí tenemos el proceso padre, la bash
ejecución del script y el shell clonado (ejecutando ps
), todos mostrados porque ahora ps
mostrará cada uno de ellos porque su comando incluirá la palabra bash
.
Lanzamiento directo con un shell diferente ( sh
)
$ sh foo.sh
The number of shells opened by terdon is 1
Esto es diferente porque está ejecutando el script con sh
y no bash
. Por lo tanto, la única bash
instancia es el shell principal donde inició su script. Todos los otros depósitos mencionados anteriormente están siendo ejecutados en su sh
lugar.
Abastecimiento (ya sea por .
o source
, lo mismo)
$ . ./foo.sh
The number of shells opened by terdon is 2
Como expliqué anteriormente, el abastecimiento de un script hace que se ejecute en el mismo shell que el proceso padre. Sin embargo, se inicia una subshell separada para iniciar el ps
comando y eso lleva el total a dos.
Como nota final, la forma correcta de contar los procesos en ejecución no es analizar ps
sino usar pgrep
. Todos estos problemas se habrían evitado si hubieras corrido
pgrep -cu terdon bash
Entonces, una versión funcional de su script que siempre imprime el número correcto es (tenga en cuenta la ausencia de sustitución de comandos):
#!/usr/bin/env bash
user="terdon"
printf "Open shells:"
pgrep -cu "$user" bash
Eso devolverá 1 cuando se obtenga y 2 (porque se lanzará un nuevo bash para ejecutar el script) para todas las otras formas de lanzamiento. Todavía devolverá 1 cuando se inicie, sh
ya que el proceso secundario no lo es bash
.