O problema está em como você está chamando o . special builtin:
exec /bin/sh -c '. vars.sh; /usr/bin/fish'
Em sh , se o argumento não contiver / , . procurará o arquivo em $PATH . Então, acima, seria procurar por vars.sh em $PATH em vez do diretório atual como você pretendia.
Além disso, . sendo um especial embutido, sua falha faz com que o shell saia (quando não interativo), então o próximo comando (aqui fish ) não é executado, e é por isso que a janela do emulador de terminal desaparece sem um prompt fish .
Isso pode ser evitado chamando . as command . , que remove o atributo especial dos especiais .
Note que o comportamento de bash (a implementação sh do projeto GNU) é diferente a esse respeito quando não está no modo POSIX (quando não é chamado como sh , nem com --posix , e quando o ambiente não contém POSIXLY_CORRECT= nem SHELLOPTS=posix ):
bash ' . não faz com que o shell saia após a falha e procura por um argumento sem barra no diretório atual se não puder encontrá-lo em $PATH .
Em qualquer caso, no modo POSIX ou não, se você quiser o vars.sh no diretório atual, precisará da sintaxe ./vars.sh . Então é
exec sh -c 'command . ./vars.sh; exec fish'