bash - working - linux cd to script directory




Ottenere la directory sorgente di uno script Bash dall'interno (20)

Ciò ottiene la directory di lavoro corrente su Mac OS X 10.6.6:

DIR=$(cd "$(dirname "$0")"; pwd)

Come posso ottenere il percorso della directory in cui si trova uno script Bash , all'interno di quello script?

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:

$ ./application

Ecco il modo semplice e corretto:

actual_path=$(readlink -f "${BASH_SOURCE[0]}")
script_dir=$(dirname "$actual_path")

Spiegazione:

  • ${BASH_SOURCE[0]} - il percorso completo dello script. Il valore di questo sarà corretto anche quando lo script viene originato, ad esempio source <(echo 'echo $0') stampa bash , mentre sostituendolo con ${BASH_SOURCE[0]} stamperà il percorso completo dello script. (Ovviamente, questo presuppone che tu stia facendo una dipendenza da Bash.)

  • readlink -f - Risolve in modo ricorsivo qualsiasi readlink -f simbolico nel percorso specificato. Questa è un'estensione GNU e non disponibile su (per esempio) sistemi BSD. Se stai usando un Mac, puoi usare Homebrew per installare i coreutils GNU e sostituirli con greadlink -f .

  • E naturalmente dirname ottiene la directory padre del percorso.


Ho confrontato molte delle risposte fornite e ho trovato alcune soluzioni più compatte. Questi sembrano gestire tutti i casi pazzi che derivano dalla tua combinazione preferita di:

  • Percorsi assoluti o percorsi relativi
  • Collegamenti software e directory
  • Invocazione come script , script bash script , bash script bash -c script , source script o . script . script
  • Spazi, tabulazioni, newline, unicode, ecc. In directory e / o nomefile
  • Nomi di file che iniziano con un trattino

Se stai eseguendo da Linux, sembra che l'utilizzo proc del proc sia la soluzione migliore per individuare l'origine completamente risolta dello script attualmente in esecuzione (in una sessione interattiva, il link punta al rispettivo /dev/pts/X ):

resolved="$(readlink /proc/$$/fd/255 && echo X)" && resolved="${resolved%$'\nX'}"

Questo ha un po 'di bruttezza, ma la correzione è compatta e facile da capire. Non stiamo usando solo i primitivi bash, ma sono d'accordo perché readlink semplifica considerevolmente il compito. L' echo X aggiunge una X alla fine della stringa variabile in modo che nessuno spazio vuoto finale nel nome del file non venga mangiato e la sostituzione dei parametri ${VAR%X} alla fine della riga si sbarazza della X Poiché readlink aggiunge una nuova riga (che normalmente verrebbe utilizzata nella sostituzione del comando, se non fosse per il nostro trucco precedente), dobbiamo sbarazzarci anche di questo. Questo è più facile con lo schema di quoting $'' , che ci permette di usare sequenze di escape come \n per rappresentare newline (questo è anche il modo in cui puoi facilmente creare directory e file con un nome errato).

Quanto sopra dovrebbe coprire le tue necessità di localizzare lo script attualmente in esecuzione su Linux, ma se non hai il filesystem proc a tua disposizione, o se stai cercando di localizzare il percorso completamente risolto di qualche altro file, allora forse tu? troveremo il seguente codice utile. È solo una leggera modifica rispetto alla copertina. Se stai giocando con strane directory / nomi di file, controllare l'output con ls e readlink è informativo, dato che ls genererà percorsi "semplificati", sostituendo ? per cose come newlines.

absolute_path=$(readlink -e -- "${BASH_SOURCE[0]}" && echo x) && absolute_path=${absolute_path%?x}
dir=$(dirname -- "$absolute_path" && echo x) && dir=${dir%?x}
file=$(basename -- "$absolute_path" && echo x) && file=${file%?x}

ls -l -- "$dir/$file"
printf '$absolute_path: "%s"\n' "$absolute_path"

Ho provato ognuno di questi e nessuno di loro ha funzionato. Uno era molto vicino ma aveva un piccolo bug che lo spezzò male; si sono dimenticati di avvolgere il percorso tra virgolette.

Inoltre, molte persone pensano che tu stia eseguendo lo script da una shell, dimenticando quindi che quando apri un nuovo script, questo viene automaticamente portato a casa.

Prova questa directory per le dimensioni:

/ var / Nessuno / Pensiero / Informazioni sugli spazi Essere / In una directory / Nome / Ed ecco il file.text

Questo va bene indipendentemente da come o dove lo si esegue.

#!/bin/bash
echo "pwd: `pwd`"
echo "\$0: $0"
echo "basename: `basename "$0"`"
echo "dirname: `dirname "$0"`"

Quindi, per renderlo davvero utile ecco come passare alla directory dello script in esecuzione:

cd "`dirname "$0"`"

spero che sia d'aiuto


Non penso che sia così facile come altri hanno fatto per essere. pwd non funziona, poiché la directory corrente non è necessariamente la directory con lo script. $ 0 non ha sempre le informazioni. Considera i seguenti tre modi per invocare uno script.

./script

/usr/bin/script

script

Nel primo e nel terzo modo, $ 0 non ha le informazioni complete sul percorso. Nel secondo e nel terzo, pwd non funziona. L'unico modo per ottenere la dir nel terzo modo sarebbe quella di scorrere il percorso e trovare il file con la corrispondenza corretta. Fondamentalmente il codice dovrebbe rifare il funzionamento del sistema operativo.

Un modo per fare ciò che stai chiedendo sarebbe semplicemente di inserire i dati nella directory / usr / share e farne riferimento per percorso completo. I dati non dovrebbero comunque essere nella directory / usr / bin, quindi questa è probabilmente la cosa da fare.


Per i sistemi che hanno GNU coreutils readlink (ad esempio linux):

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

Non è necessario utilizzare BASH_SOURCE quando $0 contiene il nome file dello script.


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


Questo è specifico per Linux, ma puoi usare:

SELF=$(readlink /proc/$$/fd/255)

Quindi ... credo di averlo. In ritardo per la festa, ma penso che alcuni apprezzeranno che essere qui è loro imbattersi in questo thread. I commenti dovrebbero spiegare.

#!/bin/sh # dash bash ksh # !zsh (issues). G. Nixon, 12/2013. Public domain.

## 'linkread' or 'fullpath' or (you choose) is a little tool to recursively
## dereference symbolic links (ala 'readlink') until the originating file
## is found. This is effectively the same function provided in stdlib.h as
## 'realpath' and on the command line in GNU 'readlink -f'.

## Neither of these tools, however, are particularly accessible on the many
## systems that do not have the GNU implementation of readlink, nor ship
## with a system compiler (not to mention the requisite knowledge of C).

## This script is written with portability and (to the extent possible, speed)
## in mind, hence the use of printf for echo and case statements where they
## can be substituded for test, though I've had to scale back a bit on that.

## It is (to the best of my knowledge) written in standard POSIX shell, and
## has been tested with bash-as-bin-sh, dash, and ksh93. zsh seems to have
## issues with it, though I'm not sure why; so probably best to avoid for now.

## Particularly useful (in fact, the reason I wrote this) is the fact that
## it can be used within a shell script to find the path of the script itself.
## (I am sure the shell knows this already; but most likely for the sake of
## security it is not made readily available. The implementation of "$0"
## specificies that the $0 must be the location of **last** symbolic link in
## a chain, or wherever it resides in the path.) This can be used for some
## ...interesting things, like self-duplicating and self-modifiying scripts.

## Currently supported are three errors: whether the file specified exists
## (ala ENOENT), whether its target exists/is accessible; and the special
## case of when a sybolic link references itself "foo -> foo": a common error
## for beginners, since 'ln' does not produce an error if the order of link
## and target are reversed on the command line. (See POSIX signal ELOOP.)

## It would probably be rather simple to write to use this as a basis for
## a pure shell implementation of the 'symlinks' util included with Linux.

## As an aside, the amount of code below **completely** belies the amount
## effort it took to get this right -- but I guess that's coding for you.

##===-------------------------------------------------------------------===##

for argv; do :; done # Last parameter on command line, for options parsing.

## Error messages. Use functions so that we can sub in when the error occurs.

recurses(){ printf "Self-referential:\n\t$argv ->\n\t$argv\n" ;}
dangling(){ printf "Broken symlink:\n\t$argv ->\n\t"$(readlink "$argv")"\n" ;}
errnoent(){ printf "No such file: "[email protected]"\n" ;} # Borrow a horrible signal name.

# Probably best not to install as 'pathfull', if you can avoid it.

pathfull(){ cd "$(dirname "[email protected]")"; link="$(readlink "$(basename "[email protected]")")"

## 'test and 'ls' report different status for bad symlinks, so we use this.

 if [ ! -e "[email protected]" ]; then if $(ls -d "[email protected]" 2>/dev/null) 2>/dev/null;  then
    errnoent 1>&2; exit 1; elif [ ! -e "[email protected]" -a "$link" = "[email protected]" ];   then
    recurses 1>&2; exit 1; elif [ ! -e "[email protected]" ] && [ ! -z "$link" ]; then
    dangling 1>&2; exit 1; fi
 fi

## Not a link, but there might be one in the path, so 'cd' and 'pwd'.

 if [ -z "$link" ]; then if [ "$(dirname "[email protected]" | cut -c1)" = '/' ]; then
   printf "[email protected]\n"; exit 0; else printf "$(pwd)/$(basename "[email protected]")\n"; fi; exit 0
 fi

## Walk the symlinks back to the origin. Calls itself recursivly as needed.

 while [ "$link" ]; do
   cd "$(dirname "$link")"; newlink="$(readlink "$(basename "$link")")"
   case "$newlink" in
    "$link") dangling 1>&2 && exit 1                                       ;;
         '') printf "$(pwd)/$(basename "$link")\n"; exit 0                 ;;
          *) link="$newlink" && pathfull "$link"                           ;;
   esac
 done
 printf "$(pwd)/$(basename "$newlink")\n"
}

## Demo. Install somewhere deep in the filesystem, then symlink somewhere 
## else, symlink again (maybe with a different name) elsewhere, and link
## back into the directory you started in (or something.) The absolute path
## of the script will always be reported in the usage, along with "$0".

if [ -z "$argv" ]; then scriptname="$(pathfull "$0")"

# Yay ANSI l33t codes! Fancy.
 printf "\n\033[3mfrom/as: \033[4m$0\033[0m\n\n\033[1mUSAGE:\033[0m   "
 printf "\033[4m$scriptname\033[24m [ link | file | dir ]\n\n         "
 printf "Recursive readlink for the authoritative file, symlink after "
 printf "symlink.\n\n\n         \033[4m$scriptname\033[24m\n\n        "
 printf " From within an invocation of a script, locate the script's "
 printf "own file\n         (no matter where it has been linked or "
 printf "from where it is being called).\n\n"

else pathfull "[email protected]"
fi

Risposta breve:

`dirname $0`

o ( preferably ):

$(dirname "$0")

Usa dirname "$0" :

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

usare pwd da solo non funzionerà se non si sta eseguendo lo script dalla directory in cui è contenuto.

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

Vorrei usare qualcosa del genere:

# retrieve the full pathname of the called script
scriptPath=$(which $0)

# check whether the path is a link or not
if [ -L $scriptPath ]; then

    # it is a link then retrieve the target path and get the directory name
    sourceDir=$(dirname $(readlink -f $scriptPath))

else

    # otherwise just get the directory name of the script path
    sourceDir=$(dirname $scriptPath)

fi

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

Tuttavia, dirname fornisce esattamente la porzione di directory del nome del file, che più probabilmente di quanto non sarà relativa alla directory di lavoro corrente. Se lo script necessita di cambiare directory per qualche motivo, l'output di 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 si cambia 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.


La migliore soluzione compatta a mio avviso sarebbe:

"$( cd "$( echo "${BASH_SOURCE[0]%/*}" )"; pwd )"

Non si fa affidamento su nient'altro che Bash. L'uso di dirname, readlinke basenamealla fine porterà a problemi di compatibilità, quindi è meglio evitare se possibile.


Prova la seguente soluzione cross-compatibile:

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

come realpatho i readlinkcomandi non sono sempre disponibili (a seconda del sistema operativo) ed ${BASH_SOURCE[0]}è disponibile solo in bash shell.

In alternativa puoi provare la seguente funzione in bash:

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

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

Relazionato:


Questo è l'unico modo che ho trovato per dire in modo affidabile:

SCRIPT_DIR=$(dirname $(cd "$(dirname "$BASH_SOURCE")"; pwd))

Hmm, se nel percorso basename & dirname non sono sufficienti per tagliarlo e camminare è difficile (cosa succede se il genitore non ha esportato PATH!). Tuttavia, la shell deve avere un handle aperto per il suo script, e in bash l'handle è # 255.

SELF=`readlink /proc/$$/fd/255`

per me va bene.


#!/bin/sh
PRG="$0"

# need this for relative symlinks
while [ -h "$PRG" ] ; do
   PRG=`readlink "$PRG"`
done

scriptdir=`dirname "$PRG"`

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 si esegue il cd in una directory diversa prima di eseguire questo snippet, il risultato potrebbe essere errato! Inoltre, $CDPATH trucchi $CDPATH .

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'

SCRIPT_DIR=$( cd ${0%/*} && pwd -P )




directory