Come impostare la directory di lavoro corrente sulla directory dello script?


569

Sto scrivendo una sceneggiatura bash. Ho bisogno che l'attuale directory di lavoro sia sempre la directory in cui si trova lo script.

Il comportamento predefinito è che l'attuale directory di lavoro nello script è quella della shell da cui la eseguo, ma non voglio questo comportamento.


Hai mai pensato di mettere uno script wrapper da qualche parte come / usr / bin da cd nella directory corretta (hardcoded) e quindi eseguire il tuo script?
Dagg Nabbit,

2
Perché hai bisogno della directory dello script? C'è probabilmente un modo migliore per risolvere il problema di fondo.
bstpierre,

12
Vorrei solo sottolineare che il comportamento che chiami "ovviamente indesiderabile" è in realtà del tutto necessario - se eseguo myscript path/to/filemi aspetto che lo script valuti il ​​percorso / file / relativo alla mia directory corrente, non qualunque sia la directory dello script in cui trovarsi. Inoltre, cosa ti sarebbe successo per uno script eseguito ssh remotehost bash < ./myscriptcome menzionato nelle FAQ di BASH?
Gordon Davisson,


3
cd "${BASH_SOURCE%/*}" || exit
Caw

Risposte:


656
#!/bin/bash
cd "$(dirname "$0")"

7
dirname restituisce '.' quando si utilizza bash in Windows. Quindi, la risposta di Paul è migliore.
Tvaroh,

7
Restituisce anche '.' in Mac OSX
Ben Clayton,

4
Vale la pena notare che le cose possono rompersi se un collegamento simbolico fa parte di $0. Nel tuo script potresti aspettarti, ad esempio, ../../di fare riferimento alla directory due livelli sopra la posizione dello script, ma questo non è necessariamente il caso se sono in esecuzione collegamenti simbolici.
Brian Gordon,

20
Se hai chiamato lo script come ./script, .è la directory corretta e la modifica in .essa finirà anche nella stessa directory in cui scriptsi trova, ovvero nella directory di lavoro corrente.
ndim,

6
Se si esegue lo script dalla directory corrente in questo modo bash script.sh, il valore di $0è script.sh. L'unico modo in cui il cdcomando "funzionerà" per te è perché non ti importa dei comandi falliti. Se dovessi usare set -o errexit(aka:) set -eper assicurarti che il tuo script non superi i comandi non riusciti, NON funzionerebbe perché cd script.shè un errore. Il modo affidabile [bash specifico] ècd "$(dirname ${BASH_SOURCE[0]})"
Bruno Bronosky il

322

Inoltre funziona:

cd "${0%/*}"

La sintassi è completamente descritta in questa risposta StackOverflow.


17
Spiegazione come funziona: stackoverflow.com/questions/6393551/...
kenorb

25
Non dimenticare di racchiudere tra virgolette se il percorso contiene spazi bianchi. cioè cd "$ {0% / *}"
H.Rabiee

17
Questo fallisce se lo script viene chiamato con bash script.sh- $0sarà solo il nome del file
Chris Watts

17
Direi che questa risposta è troppo concisa. Difficilmente apprendi nulla sulla sintassi. Qualche descrizione su come leggere questo sarebbe utile.
Frattura del

2
Questo trucco non genera anche una subshell, quindi è buono per i fanatici della velocità.
Teknopaul,

184

Prova le seguenti linee semplici:


Per tutti UNIX / OSX / Linux

dir=$(cd -P -- "$(dirname -- "$0")" && pwd -P)

bash

dir=$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)

Nota: un doppio trattino (-) viene utilizzato nei comandi per indicare la fine delle opzioni di comando, quindi i file contenenti trattini o altri caratteri speciali non interromperanno il comando.

Nota: in Bash, utilizzare ${BASH_SOURCE[0]}a favore di $0, altrimenti il ​​percorso può interrompersi quando lo si approva ( source/ .).


Per Linux, Mac e altri * BSD:

cd "$(dirname "$(realpath "$0")")";

Nota: realpathdovrebbe essere installato nella distribuzione Linux più popolare per impostazione predefinita (come Ubuntu), ma in alcuni può mancare, quindi è necessario installarlo.

Nota: se si utilizza Bash, utilizzare ${BASH_SOURCE[0]}a favore di $0, altrimenti il ​​percorso potrebbe interrompersi quando viene utilizzato ( source/ .).

Altrimenti potresti provare qualcosa del genere (utilizzerà il primo strumento esistente):

cd "$(dirname "$(readlink -f "$0" || realpath "$0")")"

Per Linux specifico:

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

Utilizzando GNU readlink su * BSD / Mac:

cd "$(dirname "$(greadlink -f "$0")")"

Nota: è necessario aver coreutilsinstallato (ad es. 1. Installa Homebrew , 2. brew install coreutils).


In bash

In bash puoi usare Parameter Expansions per raggiungere questo obiettivo, come:

cd "${0%/*}"

ma non funziona se lo script viene eseguito dalla stessa directory.

In alternativa puoi definire la seguente funzione in bash:

realpath () {
  [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
}

Questa funzione accetta 1 argomento. Se l'argomento ha già un percorso assoluto, stampalo così com'è, altrimenti stampa l' $PWDargomento variabile + nomefile (senza ./prefisso).

o qui è la versione presa dal .bashrcfile Debian :

function realpath()
{
    f=$@
    if [ -d "$f" ]; then
        base=""
        dir="$f"
    else
        base="/$(basename "$f")"
        dir=$(dirname "$f")
    fi
    dir=$(cd "$dir" && /bin/pwd)
    echo "$dir$base"
}

Relazionato:

Guarda anche:

Come posso ottenere il comportamento del readlink di GNU -f su un Mac?


4
una risposta molto migliore di quelle popolari perché risolve i syslink e tiene conto di diversi sistemi operativi. Grazie!
Dennis,

Grazie per questa risposta Quello in alto ha funzionato sul mio Mac ... ma cosa fa l' cpopzione - nel comando, @kenorb?
TobyG,

3
Un doppio trattino ( --) viene utilizzato nei comandi per indicare la fine delle opzioni di comando, quindi i file contenenti trattini o altri caratteri speciali non interromperanno il comando. Prova ad esempio a creare il file tramite touch "-test"e touch -- -test, quindi rimuovi il file tramite rm "-test"e rm -- -teste vedi la differenza.
Kenorb,

1
Rimuoverei il mio voto se potessi: realpath è deprecato unix.stackexchange.com/questions/136494/…
lsh

1
@lsh Dice che solo il realpathpacchetto Debian è deprecato, non GNU realpath. Se ritieni che non sia chiaro, puoi suggerire una modifica.
Kenorb,

73
cd "$(dirname "${BASH_SOURCE[0]}")"

È facile. Funziona.


1
Questa dovrebbe essere la risposta accettata. Funziona su OSX e Linux Ubuntu.
David Rissato Cruz,

5
Funziona alla grande quando lo script B proviene da altri script A ed è necessario utilizzare percorsi relativi allo script B. Grazie.
Enrico,

5
Questo funziona anche in Cygwin per quelli di noi abbastanza sfortunati da dover toccare Windows.
Bruno Bronosky,

3
Oppurecd "${BASH_SOURCE%/*}" || exit
caw

Funziona su macOS Mojave
Juan Boero il

6

La risposta accettata funziona bene per gli script che non sono stati collegati in modo simbolico altrove, come in $PATH.

#!/bin/bash
cd "$(dirname "$0")"

Tuttavia, se lo script viene eseguito tramite un collegamento simbolico,

ln -sv ~/project/script.sh ~/bin/; 
~/bin/script.sh

Questo eseguirà il cd nella ~/bin/directory e non nella ~/project/directory, il che probabilmente interromperà lo script se lo scopo di cdè includere dipendenze relative a~/project/

La risposta sicura al collegamento simbolico è di seguito:

#!/bin/bash
cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")"

readlink -f è necessario per risolvere il percorso assoluto del file potenzialmente collegato simbolicamente.

Le virgolette sono necessarie per supportare percorsi di file che potrebbero potenzialmente contenere spazi bianchi (cattiva pratica, ma non è sicuro supporre che non sarà così)


4

Questo script sembra funzionare per me:

#!/bin/bash
mypath=`realpath $0`
cd `dirname $mypath`
pwd

La riga di comando pwd fa eco alla posizione dello script come directory di lavoro corrente, indipendentemente da dove lo eseguo.


3
realpathè improbabile che sia installato ovunque. Il che potrebbe non importare, a seconda della situazione del PO.
bstpierre,

Il mio sistema non ha realpathma readlinksembra che sia simile.
In pausa fino a nuovo avviso.

2
Quale dist non ha realpath installato per impostazione predefinita? Ubuntu ce l'ha.
Kenorb,


1
cd "`dirname $(readlink -f ${0})`"

Puoi spiegare la tua risposta per favore?
Zulu,

1
Sebbene questo codice possa aiutare a risolvere il problema, non spiega perché e / o come risponde alla domanda. Fornire questo contesto aggiuntivo migliorerebbe significativamente il suo valore educativo a lungo termine. Si prega di modificare la risposta di aggiungere spiegazioni, tra cui quello che si applicano le limitazioni e le assunzioni.
Toby Speight,

-5
echo $PWD

PWD è una variabile d'ambiente.


5
Questo dà solo la directory chiamata da, non la directory in cui si trova lo script.
dagelf il

-6

Se hai solo bisogno di stampare l'attuale directory di lavoro, puoi seguire questo.

$ vim test

#!/bin/bash
pwd
:wq to save the test file.

Autorizza l'esecuzione:

chmod u+x test

Quindi esegui lo script da ./testallora puoi vedere la directory di lavoro attuale.


2
La domanda era come assicurarsi che lo script venisse eseguito nella propria directory, incluso se quella non era la directory di lavoro. Quindi questa non è una risposta. Comunque, perché farlo invece di ... correre pwd? Mi sembra un sacco di sprechi di pressione dei tasti per me.
underscore_d
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.