Fondamentalmente ho bisogno di eseguire lo script con percorsi relativi al percorso del file di script della shell, come posso cambiare la directory corrente nella stessa directory in cui risiede il file di script?
Fondamentalmente ho bisogno di eseguire lo script con percorsi relativi al percorso del file di script della shell, come posso cambiare la directory corrente nella stessa directory in cui risiede il file di script?
Risposte:
In Bash, dovresti ottenere ciò di cui hai bisogno in questo modo:
#!/usr/bin/env bash
BASEDIR=$(dirname "$0")
echo "$BASEDIR"
readlink
(vedi la risposta di al sotto)
$BASH_SOURCE
al posto di $0
, perché $0
non contiene sempre il percorso dello script che viene invocato, come quando si "procura" uno script.
$BASH_SOURCE
è specifico di Bash, la domanda riguarda lo script di shell in generale.
CUR_PATH=$(pwd)
oppure pwd
restituisci la directory corrente (che non deve essere la directory principale degli script)!
$BASH_SOURCE
e restituisce ciò di cui avevo bisogno. Il mio script viene chiamato da un altro script e $0
restituisce .
mentre $BASH_SOURCE
restituisce la sottodirectory corretta (nel mio caso scripts
).
Il post originale contiene la soluzione (ignora le risposte, non aggiungono nulla di utile). L'interessante lavoro viene svolto dal menzionato comando unix readlink
con opzione -f
. Funziona quando lo script viene chiamato da un percorso assoluto e relativo.
Per bash, sh, ksh:
#!/bin/bash
# Absolute path to this script, e.g. /home/user/bin/foo.sh
SCRIPT=$(readlink -f "$0")
# Absolute path this script is in, thus /home/user/bin
SCRIPTPATH=$(dirname "$SCRIPT")
echo $SCRIPTPATH
Per tcsh, csh:
#!/bin/tcsh
# Absolute path to this script, e.g. /home/user/bin/foo.csh
set SCRIPT=`readlink -f "$0"`
# Absolute path this script is in, thus /home/user/bin
set SCRIPTPATH=`dirname "$SCRIPT"`
echo $SCRIPTPATH
Vedi anche: https://stackoverflow.com/a/246128/59087
readlink
. Ecco perché ho raccomandato di usare pushd / popd (built-in per bash).
-f
opzione per readlink
fare qualcosa di diverso su OS X (Lion) e possibilmente BSD. stackoverflow.com/questions/1055671/...
-f
non è supportato affatto su OS X (come da Lion); lì puoi o lasciar perdere -f
per accontentarti di risolvere al massimo un livello di riferimento indiretto, ad esempio pushd "$(dirname "$(readlink "$BASH_SOURCE" || echo "$BASH_SOURCE")")"
, oppure puoi creare il tuo script ricorsivo che segue il link simbolico, come dimostrato nel post collegato.
sh /some/other/directory/script.sh)
, in questo caso .
sarebbe il tuo pwd, non/some/other/directory
Un precedente commento su una risposta lo diceva, ma è facile perdere tra tutte le altre risposte.
Quando si utilizza bash:
echo this file: "$BASH_SOURCE"
echo this dir: "$(dirname "$BASH_SOURCE")"
dirname "$BASH_SOURCE"
invece per gestire gli spazi in $ BASH_SOURCE.
"$(dirname "${BASH_SOURCE[0]}")"
Supponendo che stai usando bash
#!/bin/bash
current_dir=$(pwd)
script_dir=$(dirname $0)
echo $current_dir
echo $script_dir
Questo script dovrebbe stampare la directory in cui ci si trova e quindi la directory in cui si trova lo script. Ad esempio, quando lo si chiama /
con lo script in /home/mez/
, viene generato
/
/home/mez
Ricorda, quando assegni le variabili dall'output di un comando, includi il comando $(
e )
- o non otterrai l'output desiderato.
Se stai usando bash ....
#!/bin/bash
pushd $(dirname "${0}") > /dev/null
basedir=$(pwd -L)
# Use "pwd -P" for the path without links. man bash for more info.
popd > /dev/null
echo "${basedir}"
pushd
/ popd
con cd $(dirname "${0}")
e cd -
per farlo funzionare su altre shell, se hanno a pwd -L
.
Come suggerisce il Marko:
BASEDIR=$(dirname $0)
echo $BASEDIR
Funziona a meno che non si esegua lo script dalla stessa directory in cui risiede lo script, nel qual caso si ottiene un valore di "."
Per aggirare il problema, utilizzare:
current_dir=$(pwd)
script_dir=$(dirname $0)
if [ $script_dir = '.' ]
then
script_dir="$current_dir"
fi
Ora puoi utilizzare la variabile current_dir in tutto lo script per fare riferimento alla directory degli script. Tuttavia, questo potrebbe ancora avere il problema del collegamento simbolico.
La migliore risposta a questa domanda ha avuto risposta qui:
Ottenere la directory di origine di uno script Bash dall'interno
E questo è:
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
One-liner che ti darà il nome completo della directory dello script, indipendentemente da dove viene chiamato.
Per capire come funziona puoi eseguire il seguente script:
#!/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'"
Facciamolo un oneliner POSIX:
a="/$0"; a=${a%/*}; a=${a#/}; a=${a:-.}; BASEDIR=$(cd "$a"; pwd)
Testato su molte shell compatibili con Bourne, comprese quelle BSD.
Per quanto ne so sono l'autore e lo metto di dominio pubblico. Per maggiori informazioni, consultare: https://www.jasan.tk/posts/2017-05-11-posix_shell_dirname_replacement/
cd: too many arguments
se spazi nel percorso e ritorna $PWD
. (una soluzione ovvia, ma mostra solo quanti casi limite ci sono effettivamente)
BASE_DIR="$(cd "$(dirname "$0")"; pwd)";
echo "BASE_DIR => $BASE_DIR"
Se si desidera ottenere la directory di script effettiva (indipendentemente dal fatto che si stia invocando lo script utilizzando un collegamento simbolico o direttamente), provare:
BASEDIR=$(dirname $(realpath "$0"))
echo "$BASEDIR"
Funziona sia su Linux che su MacOS. Non ho visto nessuno menzionato qui realpath
. Non sono sicuro se ci siano degli svantaggi in questo approccio.
su macOS, devi installare coreutils
per usare realpath
. Ad esempio: brew install coreutils
.
INTRODUZIONE
Questa risposta corregge la risposta molto rotta ma sorprendentemente votata per primo di questa discussione (scritta da TheMarko):
#!/usr/bin/env bash
BASEDIR=$(dirname "$0")
echo "$BASEDIR"
PERCHÉ USARE dirname "$ 0" SU NON È PROPRIO?
dirname $ 0 funzionerà solo se l'utente avvia lo script in un modo molto specifico. Sono stato in grado di trovare diverse situazioni in cui questa risposta fallisce e blocca lo script.
Prima di tutto, capiamo come funziona questa risposta. Sta ottenendo la directory degli script facendo
dirname "$0"
$ 0 rappresenta la prima parte del comando che chiama lo script (in pratica è il comando inserito senza gli argomenti:
/some/path/./script argument1 argument2
$ 0 = "/ qualche / percorso /./ script"
dirname fondamentalmente trova l'ultimo / in una stringa e lo tronca lì. Quindi se lo fai:
dirname /usr/bin/sha256sum
otterrai: / usr / bin
Questo esempio funziona bene perché / usr / bin / sha256sum è un percorso formattato correttamente ma
dirname "/some/path/./script"
non funzionerebbe bene e ti darebbe:
BASENAME="/some/path/." #which would crash your script if you try to use it as a path
Supponi di essere nella stessa directory del tuo script e di avviarlo con questo comando
./script
$ 0 in questa situazione sarà ./script e dirname $ 0 darà:
. #or BASEDIR=".", again this will crash your script
usando:
sh script
Senza inserire il percorso completo, verrà anche fornito un BASEDIR = "."
Utilizzando le relative directory:
../some/path/./script
Fornisce un dirname $ 0 di:
../some/path/.
Se ti trovi nella directory / some e chiami lo script in questo modo (nota l'assenza di / all'inizio, di nuovo un percorso relativo):
path/./script.sh
Otterrai questo valore per dirname $ 0:
path/.
e ./path/./script (un'altra forma del percorso relativo) fornisce:
./path/.
Le uniche due situazioni in cui basedir $ 0 funzionerà è se l'utente usa sh o touch per avviare uno script perché entrambi comporteranno $ 0:
$0=/some/path/script
che ti darà un percorso che puoi usare con dirname.
LA SOLUZIONE
Dovresti tenere conto e rilevare ognuna delle situazioni sopra menzionate e applicare una correzione se si presenta:
#!/bin/bash
#this script will only work in bash, make sure it's installed on your system.
#set to false to not see all the echos
debug=true
if [ "$debug" = true ]; then echo "\$0=$0";fi
#The line below detect script's parent directory. $0 is the part of the launch command that doesn't contain the arguments
BASEDIR=$(dirname "$0") #3 situations will cause dirname $0 to fail: #situation1: user launches script while in script dir ( $0=./script)
#situation2: different dir but ./ is used to launch script (ex. $0=/path_to/./script)
#situation3: different dir but relative path used to launch script
if [ "$debug" = true ]; then echo 'BASEDIR=$(dirname "$0") gives: '"$BASEDIR";fi
if [ "$BASEDIR" = "." ]; then BASEDIR="$(pwd)";fi # fix for situation1
_B2=${BASEDIR:$((${#BASEDIR}-2))}; B_=${BASEDIR::1}; B_2=${BASEDIR::2}; B_3=${BASEDIR::3} # <- bash only
if [ "$_B2" = "/." ]; then BASEDIR=${BASEDIR::$((${#BASEDIR}-1))};fi #fix for situation2 # <- bash only
if [ "$B_" != "/" ]; then #fix for situation3 #<- bash only
if [ "$B_2" = "./" ]; then
#covers ./relative_path/(./)script
if [ "$(pwd)" != "/" ]; then BASEDIR="$(pwd)/${BASEDIR:2}"; else BASEDIR="/${BASEDIR:2}";fi
else
#covers relative_path/(./)script and ../relative_path/(./)script, using ../relative_path fails if current path is a symbolic link
if [ "$(pwd)" != "/" ]; then BASEDIR="$(pwd)/$BASEDIR"; else BASEDIR="/$BASEDIR";fi
fi
fi
if [ "$debug" = true ]; then echo "fixed BASEDIR=$BASEDIR";fi
Questo one-liner indica dove si trova lo script della shell, non importa se lo hai eseguito o se lo hai fatto . Inoltre, risolve tutti i collegamenti simbolici coinvolti, se questo è il caso:
dir=$(dirname $(test -L "$BASH_SOURCE" && readlink -f "$BASH_SOURCE" || echo "$BASH_SOURCE"))
A proposito, suppongo che tu stia usando / bin / bash .
Così tante risposte, tutte plausibili, ognuna con obiettivi di pro e contro e leggermente diversi (che probabilmente dovrebbero essere dichiarati per ciascuno). Ecco un'altra soluzione che soddisfa un obiettivo primario di essere chiari e di lavorare su tutti i sistemi, su tutti i bash (nessun presupposto sulle versioni bash, o readlink
opwd
opzioni) e ragionevolmente fa quello che ti aspetteresti che accada (ad esempio, risolvere i collegamenti simbolici è un problema interessante, ma di solito non è quello che si desidera), gestire casi limite come spazi nei percorsi, ecc., ignora eventuali errori e utilizza un valore predefinito sano in caso di problemi.
Ogni componente è memorizzato in una variabile separata che è possibile utilizzare singolarmente:
# script path, filename, directory
PROG_PATH=${BASH_SOURCE[0]} # this script's name
PROG_NAME=${PROG_PATH##*/} # basename of script (strip path)
PROG_DIR="$(cd "$(dirname "${PROG_PATH:-$PWD}")" 2>/dev/null 1>&2 && pwd)"
Ispirato dalla risposta di blueyed
read < <(readlink -f $0 | xargs dirname)
cd $REPLY
Questo dovrebbe fare il trucco:
echo `pwd`/`dirname $0`
Potrebbe sembrare brutto a seconda di come è stato invocato e del CWD, ma dovrebbe portarti dove devi andare (o puoi modificare la stringa se ti interessa come appare).
`pwd`/`dirname $0`
ma potrebbe ancora fallire sui collegamenti simbolici