Encontre a localização do script de shell de origem

8

É possível que um script de shell de origem saiba sua localização? Eu li determinar caminho para o script shell originado mas as respostas focar bashe tcshe falhar se um shell POSIX é usado. $0também não é a solução e produz resultados errados .

Uma solução não precisa ser 100% confiável. É improvável que o caminho contenha links físicos ou simbólicos.

# sourcing the script should yield the absolute path to the script
. somedir/thescript

# within “thescript”
-> /tmp/foo/bar/somedir

Alguns antecedentes: O script faz parte de um aplicativo existente que contém dezenas de binários em um bindiretório em um local conhecido (varia de acordo com a arquitetura) em relação ao script de origem. Para usar o aplicativo, o usuário fornece o script que anexa o bindiretório ao diretório PATH, para que os binários do aplicativo possam ser facilmente chamados no shell atual (qualquer que seja o shell).

Marco
fonte
Por favor, uma vez que verifique este link, pode ser o que você está procurando paste.ubuntu.com/6242655
Rahul Patil
@RahulPatil Isso só funciona no bash e é altamente portável.
Marco
1
Tenho certeza de que a resposta é não. Você encontrou apenas métodos específicos do shell porque não há um método portátil. Para que você precisa disso? Você pode dizer aos usuários do script para fazer algo diferente de . /path/to/scriptcomo MARCO_DIR=/path/to; . $MARCO_DIR/scriptou eval "$(/path/to/script)"? @RahulPatil BASH_SOURCEé obviamente específico para o bash.
Gilles 'SO- stop be evil'
@ Gilles Como eu disse, não precisa ser muito robusto. Mas não encontrei uma maneira que funcione remotamente nos casos mais simples de um shell POSIX. (Portanto, a solução, no momento, é que o uso desse aplicativo exija uma das conchas suportadas.) Sinta-se à vontade para postar "não ..." como resposta, se essa for a resposta.
Marco
@Gilles Alterar a maneira como o aplicativo é configurado não é uma opção. Quebraria as instalações. É por isso que eu gostaria de tornar o script mais robusto sem alterar a maneira como é chamado. No final, apenas define o PATH; portanto, se o script falhar, o fallback é encontrar os binários e adicionar seu caminho ao ambiente.
Marco

Respostas:

9

O local do script de origem não está disponível, a menos que você esteja usando um shell que ofereça extensões para a especificação POSIX. Você pode testar isso com o seguinte snippet:

env -i PATH=/usr/bin:/bin sh -c '. ./included.sh' | grep included

onde included.shcontém

echo "$0"
set

No bash, o nome do script de origem está $BASH_SOURCE. No zsh (no modo de compatibilidade zsh, não no modo de compatibilidade sh ou ksh), ele está $0(observe que em uma função, $0é o nome da função). No pdksh and dash, ele não está disponível. No ksh93, esse método não revela a solução, mas o caminho completo para o script incluído está disponível como ${.sh.file}.

Se exigir bash ou ksh93 ou zsh for bom o suficiente, você pode usar este trecho:

if [ -n "$BASH_SOURCE" ]; then
  this_script=$BASH_SOURCE
elif [ -n "$ZSH_VERSION" ]; then
  setopt function_argzero
  this_script=$0
elif eval '[[ -n ${.sh.file} ]]' 2>/dev/null; then
  eval 'this_script=${.sh.file}'
else
  echo 1>&2 "Unsupported shell. Please use bash, ksh93 or zsh."
  exit 2
fi

Você pode tentar adivinhar a localização do script, observando quais arquivos o shell abriu. Experimentalmente, isso parece funcionar com o dash e o pdksh, mas não com o bash ou o ksh93, que pelo menos para um script curto, fechou o arquivo de script no momento em que o executava.

  open_file=$(lsof -F n -p $$ | sed -n '$s/^n//p')
  if [ -n "$open_file" ]; then
    # best guess: $open_file is this script
  fi

O script pode não ser o arquivo com o descritor de número mais alto se o script for originado em um script complexo que esteja sendo reproduzido com redirecionamentos. Você pode querer percorrer os arquivos abertos. Não é garantido que funcione de qualquer maneira. A única maneira confiável de localizar um script de origem é usar o bash, ksh93 ou zsh.


Se você puder alterar a interface, em vez de procurar seu script, faça com que ele imprima um snippet de shell a ser passado evalno chamador. É o que os scripts para definir variáveis ​​de ambiente geralmente fazem. Ele permite que seu script seja escrito independentemente dos caprichos do shell e da configuração do chamador.

#!/bin/sh
FOO_DIR=$(dirname -- "$0")
cat <<EOF
FOO_DIR='$(printf %s "$FOO_DIR" | sed "s/'/'\\''/g")'
PATH="\$PATH:$FOO_DIR/bin";
export FOO_DIR PATH
EOF

No chamador: eval "`/path/to/setenv`"

Gilles 'SO- parar de ser mau'
fonte
0

Adicionando à resposta do Gilles, você PODE ser capaz de obter o shell script ( if $0 = ash) através do /proc/$$ interface. Eu não sei o quão preciso isso é, mas /proc/$$/fd/11parece sempre apontar para this_script com as cinzas do BusyBox.

Fornecer isso como uma resposta potencial para aqueles que se deparam com esta página (como eu).

A última resposta foi excluída - portanto, minha reivindicação para este trabalho é testada em 4 plataformas HW diferentes (x86, ARM, XLP e powerpc). Todos dão a mesma resposta de fd / 11. Um readlink de /proc/$$/fd/11produz meu script.

Andy

A. Kennedy
fonte