Domanda Ottenere la directory sorgente di uno script Bash dall'interno


Come ottengo il percorso della directory in cui a bash  lo script si trova, dentro  quella sceneggiatura?

Ad esempio, supponiamo di voler utilizzare uno script Bash come programma di avvio per un'altra applicazione. Voglio cambiare la directory di lavoro in quella in cui si trova lo script Bash, così posso operare sui file in quella directory, in questo modo:

$ ./applicativo 

3934


origine


risposte:


DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"

è un one-liner utile che ti darà il nome completo della directory dello script indipendentemente da dove viene chiamato.

Funzionerà finché l'ultimo componente del percorso utilizzato per trovare lo script non è un collegamento simbolico (i collegamenti delle directory sono OK). Se vuoi anche risolvere qualsiasi link allo script stesso, hai bisogno di una soluzione multi-line:

SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null && pwd )"
  SOURCE="$(readlink "$SOURCE")"
  [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done
DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null && pwd )"

Quest'ultimo funzionerà con qualsiasi combinazione di alias, source, bash -c, collegamenti simbolici, ecc.

Attenzione: se tu cd in una directory diversa prima di eseguire questo snippet, il risultato potrebbe essere errato! Inoltre, fai attenzione $CDPATH trucchi .

Per capire come funziona, prova a eseguire questo modulo più dettagliato:

#!/bin/bash

SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  TARGET="$(readlink "$SOURCE")"
  if [[ $TARGET == /* ]]; then
    echo "SOURCE '$SOURCE' is an absolute symlink to '$TARGET'"
    SOURCE="$TARGET"
  else
    DIR="$( dirname "$SOURCE" )"
    echo "SOURCE '$SOURCE' is a relative symlink to '$TARGET' (relative to '$DIR')"
    SOURCE="$DIR/$TARGET" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
  fi
done
echo "SOURCE is '$SOURCE'"
RDIR="$( dirname "$SOURCE" )"
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
if [ "$DIR" != "$RDIR" ]; then
  echo "DIR '$RDIR' resolves to '$DIR'"
fi
echo "DIR is '$DIR'"

E stamperà qualcosa come:

SOURCE './scriptdir.sh' is a relative symlink to 'sym2/scriptdir.sh' (relative to '.')
SOURCE is './sym2/scriptdir.sh'
DIR './sym2' resolves to '/home/ubuntu/dotfiles/fo fo/real/real1/real2'
DIR is '/home/ubuntu/dotfiles/fo fo/real/real1/real2'

5262



Uso dirname "$0":

#!/bin/bash
echo "The script you are running has basename `basename "$0"`, dirname `dirname "$0"`"
echo "The present working directory is `pwd`"

utilizzando pwd da solo non funzionerà se non stai eseguendo lo script dalla directory in cui è contenuto.

[matt@server1 ~]$ pwd
/home/matt
[matt@server1 ~]$ ./test2.sh
The script you are running has basename test2.sh, dirname .
The present working directory is /home/matt
[matt@server1 ~]$ cd /tmp
[matt@server1 tmp]$ ~/test2.sh
The script you are running has basename test2.sh, dirname /home/matt
The present working directory is /tmp

657



Il comando dirname è il più semplice, semplicemente analizzando il percorso fino al nome del file fuori dalla variabile $ 0 (nome script):

dirname "$0"

Ma come opaco b  sottolineato, il percorso restituito è diverso a seconda di come viene chiamato lo script. pwd non fa il lavoro perché ti dice solo quale sia la directory corrente, non in quale directory risiede lo script. Inoltre, se viene eseguito un collegamento simbolico a uno script, otterrai un percorso (probabilmente relativo) dove risiede il link, non lo script vero e proprio.

Alcuni altri hanno menzionato il readlink  comando, ma nel modo più semplice, puoi usare:

dirname "$(readlink -f "$0")"

readlink risolverà il percorso dello script in un percorso assoluto dalla radice del filesystem. Pertanto, qualsiasi percorso contenente punti singoli o doppi, tilde e / o collegamenti simbolici verrà risolto in un percorso completo.

Ecco una sceneggiatura che dimostra ognuno di questi, whatdir.sh:

#!/bin/bash
echo "pwd: `pwd`"
echo "\$0: $0"
echo "basename: `basename $0`"
echo "dirname: `dirname $0`"
echo "dirname/readlink: $(dirname $(readlink -f $0))"

Esecuzione di questo script nella mia directory home, utilizzando un percorso relativo:

>>>$ ./whatdir.sh 
pwd: /Users/phatblat
$0: ./whatdir.sh
basename: whatdir.sh
dirname: .
dirname/readlink: /Users/phatblat

Di nuovo, ma usando il percorso completo per lo script:

>>>$ /Users/phatblat/whatdir.sh 
pwd: /Users/phatblat
$0: /Users/phatblat/whatdir.sh
basename: whatdir.sh
dirname: /Users/phatblat
dirname/readlink: /Users/phatblat

Ora cambiando directory:

>>>$ cd /tmp
>>>$ ~/whatdir.sh 
pwd: /tmp
$0: /Users/phatblat/whatdir.sh
basename: whatdir.sh
dirname: /Users/phatblat
dirname/readlink: /Users/phatblat

E finalmente usando un link simbolico per eseguire lo script:

>>>$ ln -s ~/whatdir.sh whatdirlink.sh
>>>$ ./whatdirlink.sh 
pwd: /tmp
$0: ./whatdirlink.sh
basename: whatdirlink.sh
dirname: .
dirname/readlink: /Users/phatblat

365



pushd . > /dev/null
SCRIPT_PATH="${BASH_SOURCE[0]}"
if ([ -h "${SCRIPT_PATH}" ]); then
  while([ -h "${SCRIPT_PATH}" ]); do cd `dirname "$SCRIPT_PATH"`; 
  SCRIPT_PATH=`readlink "${SCRIPT_PATH}"`; done
fi
cd `dirname ${SCRIPT_PATH}` > /dev/null
SCRIPT_PATH=`pwd`;
popd  > /dev/null

Funziona per tutte le versioni, incluso

  • quando viene chiamato tramite un collegamento multifunzione a profondità multipla,
  • quando lo file
  • quando lo script viene chiamato dal comando " source"aka . operatore (punto).
  • quando arg $0viene modificato dal chiamante.
  • "./script" 
  • "/full/path/to/script" 
  • "/some/path/../../another/path/script"
  • "./some/folder/script" 

In alternativa, se lo script di bash è a link simbolico relativo  tu volere  seguirlo e restituire il percorso completo dello script collegato:

pushd . > /dev/null
SCRIPT_PATH="${BASH_SOURCE[0]}";
if ([ -h "${SCRIPT_PATH}" ]) then
  while([ -h "${SCRIPT_PATH}" ]) do cd `dirname "$SCRIPT_PATH"`; SCRIPT_PATH=`readlink "${SCRIPT_PATH}"`; done
fi
cd `dirname ${SCRIPT_PATH}` > /dev/null
SCRIPT_PATH=`pwd`;
popd  > /dev/null

SCRIPT_PATH è dato in pieno percorso, indipendentemente da come viene chiamato.
Assicurati di averlo individuato all'inizio dello script.

Questo commento e codice Copyleft, licenza selezionabile sotto GPL2.0 o successiva o CC-SA 3.0 (CreativeCommons Condividi allo stesso modo) o successiva. (c) 2008. Tutti i diritti riservati. Nessuna garanzia di alcun tipo. Sei stato avvertito.
http://www.gnu.org/licenses/gpl-2.0.txt
http://creativecommons.org/licenses/by-sa/3.0/
18eedfe1c99df68dc94d4a94712a71aaa8e1e9e36cacf421b9463dd2bbaa02906d0d6656


162



Risposta breve:

`dirname $0`

o ( preferibilmente ):

$(dirname "$0")

92



Puoi utilizzare $ BASH_SOURCE

#!/bin/bash

scriptdir=`dirname "$BASH_SOURCE"`

Si noti che è necessario utilizzare #! / Bin / bash e non #! / Bin / sh poiché è un'estensione bash


88



Questo dovrebbe farlo:

DIR=$(dirname "$(readlink -f "$0")")

Funziona con symlink e spazi nel percorso. Vedere le pagine man per dirname  e readlink .

Modificare:

Dalla traccia commenti sembra non funzionare con Mac OS. Non ho idea del perché sia ​​così. Eventuali suggerimenti?


57



pwd può essere usato per trovare la directory di lavoro corrente, e dirname per trovare la directory di un particolare file (comando che è stato eseguito, è $0, così dirname $0 dovrebbe darti la directory dello script corrente).

Però, dirname dà precisamente la parte di directory del nome del file, che più probabilmente di quanto non sarà relativa alla directory di lavoro corrente. Se lo script ha bisogno di cambiare directory per qualche motivo, quindi l'output da dirname diventa privo di significato.

Suggerisco il seguente:

#!/bin/bash

reldir=`dirname $0`
cd $reldir
directory=`pwd`

echo "Directory is $directory"

In questo modo, ottieni una directory assoluta, piuttosto che relativa.

Poiché lo script verrà eseguito in un'istanza bash separata, non è necessario ripristinare la directory di lavoro in seguito, ma se si desidera modificare lo script per qualche motivo, è possibile assegnare facilmente il valore di pwd a una variabile prima di cambiare directory, per uso futuro.

Anche se solo

cd `dirname $0`

risolve lo scenario specifico nella domanda, trovo che avere il percorso assoluto per essere più utile in generale.


49