Hoe kom ik aan het pad van de map waarin een Bash-script bevindt zich, indat script?
Ik wil een Bash-script gebruiken als opstartprogramma voor een andere toepassing. Ik wil de werkdirectory wijzigen in die waar het Bash-script zich bevindt, zodat ik de bestanden in die directory kan bewerken, zoals:
$ ./application
Antwoord 1, autoriteit 100%
#!/usr/bin/env bash
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
is een handige one-liner die je de volledige mapnaam van het script geeft, ongeacht waar het vandaan wordt aangeroepen.
Het werkt zolang het laatste onderdeel van het pad dat wordt gebruikt om het script te vinden geen symbolische link is (directorylinks zijn in orde). Als je ook links naar het script zelf wilt oplossen, heb je een oplossing met meerdere regels nodig:
#!/usr/bin/env bash
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 2>&1 && 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 2>&1 && pwd )"
Deze laatste werkt met elke combinatie van aliassen, source
, bash -c
, symlinks, enz.
Pas op:als u cd
naar een andere map gaat voordat u dit fragment uitvoert, kan het resultaat onjuist zijn!
Pas ook op voor $CDPATH
gotchas, en stderr output-bijwerkingen als de gebruiker cd slim heeft overschreven om de output naar stderr om te leiden (inclusief escape-reeksen, zoals bij het aanroepen van update_terminal_cwd >&2
op Mac). Door >/dev/null 2>&1
toe te voegen aan het einde van uw cd
-opdracht, worden beide mogelijkheden opgelost.
Probeer deze uitgebreidere vorm uit te voeren om te begrijpen hoe het werkt:
#!/usr/bin/env 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" )" >/dev/null 2>&1 && pwd )"
if [ "$DIR" != "$RDIR" ]; then
echo "DIR '$RDIR' resolves to '$DIR'"
fi
echo "DIR is '$DIR'"
En het zal iets afdrukken als:
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'
Antwoord 2, autoriteit 14%
Gebruik dirname "$0"
:
#!/bin/bash
echo "The script you are running has basename `basename "$0"`, dirname `dirname "$0"`"
echo "The present working directory is `pwd`"
Het gebruik van alleen pwd
werkt niet als u het script niet uitvoert vanuit de map waarin het zich bevindt.
[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
Antwoord 3, autoriteit 8%
De opdracht dirname
is de eenvoudigste, eenvoudig het pad naar de bestandsnaam ontleden van de variabele $0
(scriptnaam):
dirname "$0"
Maar, zoals matt bal aangaf, het geretourneerde pad is afhankelijk van hoe het script wordt aangeroepen. pwd
doet het werk niet omdat dat u alleen vertelt wat de huidige map is, niet in welke map het script zich bevindt. Bovendien, als een symbolische link naar een script wordt uitgevoerd, gaat u krijg een (waarschijnlijk relatief) pad naar waar de link zich bevindt, niet het eigenlijke script.
Enkele anderen hebben de opdracht readlink
genoemd, maar op zijn eenvoudigst kunt u het volgende gebruiken:
dirname "$(readlink -f "$0")"
readlink
zal het scriptpad omzetten in een absoluut pad vanaf de root van het bestandssysteem. Dus alle paden die enkele of dubbele punten, tildes en/of symbolische links bevatten, worden omgezet in een volledig pad.
Hier is een script dat elk van deze demonstreert, 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))"
Dit script uitvoeren in mijn thuismap, met een relatief pad:
>>>$ ./whatdir.sh
pwd: /Users/phatblat
$0: ./whatdir.sh
basename: whatdir.sh
dirname: .
dirname/readlink: /Users/phatblat
Nogmaals, maar met het volledige pad naar het script:
>>>$ /Users/phatblat/whatdir.sh
pwd: /Users/phatblat
$0: /Users/phatblat/whatdir.sh
basename: whatdir.sh
dirname: /Users/phatblat
dirname/readlink: /Users/phatblat
Nu mappen wijzigen:
>>>$ cd /tmp
>>>$ ~/whatdir.sh
pwd: /tmp
$0: /Users/phatblat/whatdir.sh
basename: whatdir.sh
dirname: /Users/phatblat
dirname/readlink: /Users/phatblat
En tot slot een symbolische link gebruiken om het script uit te voeren:
>>>$ ln -s ~/whatdir.sh whatdirlink.sh
>>>$ ./whatdirlink.sh
pwd: /tmp
$0: ./whatdirlink.sh
basename: whatdirlink.sh
dirname: .
dirname/readlink: /Users/phatblat
Antwoord 4, autoriteit 3%
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
Het werkt voor alle versies, inclusief
- wanneer gebeld via softlink met meerdere diepten,
- wanneer het bestand het
- wanneer script wordt aangeroepen door commando “
source
” oftewel.
(punt) operator. - wanneer arg
$0
wordt gewijzigd van beller. "./script"
"/full/path/to/script"
"/some/path/../../another/path/script"
"./some/folder/script"
Als alternatief, als het Bash-script zelf een relatieve symlinkis, wil jehet volgen en het volledige pad van het gekoppelde script retourneren:
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
wordt in het volledige pad gegeven, hoe het ook wordt genoemd.
Zorg ervoor dat u dit aan het begin van het script vindt.
Antwoord 5, autoriteit 2%
U kunt $BASH_SOURCE
gebruiken:
#!/bin/bash
scriptdir=`dirname "$BASH_SOURCE"`
Houd er rekening mee dat je #!/bin/bash
moet gebruiken en niet #!/bin/sh
omdat het een Bash-extensie is.
Antwoord 6, autoriteit 2%
Kort antwoord:
`dirname $0`
of (bij voorkeur):
$(dirname "$0")
Antwoord 7
Dit zou het moeten doen:
DIR="$(dirname "$(readlink -f "$0")")"
Dit werkt met symbolische links en spaties in het pad.
Bekijk de man-pagina’s voor dirname
en readlink
.
Vanaf het commentaarspoor lijkt het niet te werken met Mac OS.
Ik heb geen idee waarom dat is. Suggesties?
Antwoord 8
Hier is een gemakkelijk te onthouden script:
DIR="$(dirname "${BASH_SOURCE[0]}")" # Get the directory name
DIR="$(realpath "${DIR}")" # Resolve its full path if need be
9
SCRIPT_DIR=$( cd ${0%/*} && pwd -P )
10
$(dirname "$(readlink -f "$BASH_SOURCE")")
11
Hiermee krijgt u de huidige werkdirectory op mac & nbsp; os & nbsp; x & nbsp; v10.6.6 ( Snow & Nbsp; Leopard):
DIR=$(cd "$(dirname "$0")"; pwd)
12
Dit is Linux-specifiek, maar u kunt gebruiken:
SELF=$(readlink /proc/$$/fd/255)
13
Hier is een POSIX-compatibele one-liner:
SCRIPT_PATH=`dirname "$0"`; SCRIPT_PATH=`eval "cd \"$SCRIPT_PATH\" && pwd"`
# test
echo $SCRIPT_PATH
14
De kortste en meest elegante manier om dit te doen is:
#!/bin/bash
DIRECTORY=$(cd `dirname $0` && pwd)
echo $DIRECTORY
Dit zou op alle platforms werken en super schoon is.
Meer details zijn te vinden in “Welke map is dat bash-script in? “.
15
Ik heb ze allemaal geprobeerd en geen enkele werkte. Eentje was er heel dichtbij, maar het had een klein insect dat het ernstig brak; ze zijn vergeten het pad tussen aanhalingstekens te zetten.
Veel mensen gaan er ook van uit dat je het script vanuit een shell uitvoert, dus vergeten ze dat wanneer je een nieuw script opent, het standaard bij jou thuis staat.
Probeer deze map voor de grootte:
/var/No one/Thought/About Spaces Being/In a Directory/Name/And Here's your file.text
Dit doet het goed, ongeacht hoe of waar je het uitvoert:
#!/bin/bash
echo "pwd: `pwd`"
echo "\$0: $0"
echo "basename: `basename "$0"`"
echo "dirname: `dirname "$0"`"
Dus om het echt nuttig te maken, kun je als volgt naar de directory van het draaiende script gaan:
cd "`dirname "$0"`"
Antwoord 16
Dit is de eenvoudige, correcte manier:
actual_path=$(readlink -f "${BASH_SOURCE[0]}")
script_dir=$(dirname "$actual_path")
Uitleg:
-
${BASH_SOURCE[0]}
– het volledige pad naar het script. De waarde hiervan zal correct zijn, zelfs wanneer het script wordt gesourced, b.v.source <(echo 'echo $0')
drukt bashaf, terwijl het vervangen door${BASH_SOURCE[0]}
het volledige pad zal afdrukken van het schrift. (Dit gaat er natuurlijk van uit dat je in orde bent als je afhankelijk bent van Bash.) -
readlink -f
– Lost recursief alle symbolische links in het opgegeven pad op. Dit is een GNU-extensie en niet beschikbaar op (bijvoorbeeld) BSD-systemen. Als je een Mac gebruikt, kun je Homebrew gebruiken om GNUcoreutils
te installeren en dit te vervangen doorgreadlink -f
. -
En natuurlijk krijgt
dirname
de bovenliggende directory van het pad.
Antwoord 17
Dit is een kleine herziening van de oplossing die e-satis en 3bcdnlklvc04a hebben aangegeven in hun antwoord:
SCRIPT_DIR=''
pushd "$(dirname "$(readlink -f "$BASH_SOURCE")")" > /dev/null && {
SCRIPT_DIR="$PWD"
popd > /dev/null
}
Dit zou nog steeds moeten werken in alle gevallen die ze hebben vermeld.
Dit voorkomt popd
na een mislukte pushd
. Met dank aan konsolebox.
Antwoord 18
#!/bin/sh
PRG="$0"
# need this for relative symlinks
while [ -h "$PRG" ] ; do
PRG=`readlink "$PRG"`
done
scriptdir=`dirname "$PRG"`
Antwoord 19
Ik zou zoiets als dit gebruiken:
# 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
Antwoord 20
Voor systemen met GNU coreutils readlink
(bijvoorbeeld Linux):
$(readlink -f "$(dirname "$0")")
Het is niet nodig om BASH_SOURCE
te gebruiken wanneer $0
de scriptbestandsnaam bevat.
Antwoord 21
$_
is het vermelden waard als alternatief voor $0
. Als je een script uitvoert vanuit Bash, kan het geaccepteerde antwoord worden ingekort tot:
DIR="$( dirname "$_" )"
Merk op dat dit het eerste statement in je script moet zijn.
Antwoord 22
Dit zijn korte manieren om scriptinformatie te krijgen:
Mappen en bestanden:
Script: "/tmp/src dir/test.sh"
Calling folder: "/tmp/src dir/other"
Met deze commando’s:
echo Script-Dir : `dirname "$(realpath $0)"`
echo Script-Dir : $( cd ${0%/*} && pwd -P )
echo Script-Dir : $(dirname "$(readlink -f "$0")")
echo
echo Script-Name : `basename "$(realpath $0)"`
echo Script-Name : `basename $0`
echo
echo Script-Dir-Relative : `dirname "$BASH_SOURCE"`
echo Script-Dir-Relative : `dirname $0`
echo
echo Calling-Dir : `pwd`
En ik kreeg deze uitvoer:
Script-Dir : /tmp/src dir
Script-Dir : /tmp/src dir
Script-Dir : /tmp/src dir
Script-Name : test.sh
Script-Name : test.sh
Script-Dir-Relative : ..
Script-Dir-Relative : ..
Calling-Dir : /tmp/src dir/other
Zie ook: https://pastebin.com/j8kjxrpf
23
Dit werkt in Bash 3.2:
path="$( dirname "$( which "$0" )" )"
Als u een ~/bin
map in uw $PATH
hebt, hebt u A
in deze map. Het bronnen het script ~/bin/lib/B
. U weet waar het meegeleverde script ten opzichte van de originele, in de lib
Subdirectory, maar niet waar het is ten opzichte van de huidige directory van de gebruiker.
Dit is opgelost door het volgende (binnenin A
):
source "$( dirname "$( which "$0" )" )/lib/B"
Het maakt niet uit waar de gebruiker is of hoe hij / zij het script noemt. Dit zal altijd werken.
24
Probeer het gebruik van:
real=$(realpath $(dirname $0))
25
Ik heb veel van de gegeven antwoorden vergeleken en kwamen met wat meer compacte oplossingen. Deze lijken alle gekke randgevallen aan die ontstaan uit je favoriete combinatie van:
- absolute paden of relatieve paden
- Bestand en map Soft Links
- aanroepen als
script
,bash script
,bash -c script
,source script
of. script
- Spaties, tabbladen, newlines, Unicode, etc. in mappen en / of bestandsnaam
- Bestandsnamen die beginnen met een koppelteken
Als je onder Linux draait, lijkt het erop dat het gebruik van de proc
-handle de beste oplossing is om de volledig opgeloste bron van het momenteel lopende script te lokaliseren (in een interactieve sessie verwijst de link naar de respectievelijke /dev/pts/X
):
resolved="$(readlink /proc/$$/fd/255 && echo X)" && resolved="${resolved%$'\nX'}"
Dit heeft een klein beetje lelijkheid, maar de oplossing is compact en gemakkelijk te begrijpen. We gebruiken niet alleen bash-primitieven, maar dat vind ik goed omdat readlink
vereenvoudigt de taak aanzienlijk. De echo X
voegt een X
toe aan het einde van de variabele tekenreeks, zodat eventuele spaties in de bestandsnaam niet worden opgegeten, en de parametervervanging ${VAR%X}
aan het einde van de regel verwijdert de X
. Omdat readlink
een eigen nieuwe regel toevoegt (die normaal gesproken zou worden opgegeten in de opdrachtvervanging, zo niet voor onze vorige truc), moeten we daar ook vanaf. Dit wordt het gemakkelijkst bereikt met behulp van het $''
aanhalingsschema, waarmee we escape-reeksen zoals \n
kunnen gebruiken om nieuwe regels weer te geven (dit is ook hoe u gemakkelijk slinkse regels kunt maken benoemde mappen en bestanden).
Het bovenstaande moet uw behoeften dekken voor het lokaliseren van het momenteel lopende script op Linux, maar als u niet de proc
bestandssysteem tot uw beschikking hebt, of als u probeert de volledig opgelost te lokaliseren Pad van een ander bestand, dan vind je misschien de onderstaande code nuttig. Het is slechts een kleine modificatie van de bovenstaande one-liner. Als u rond speelt met vreemde map / bestandsnamen, controleert u de uitvoer met beide ls
en readlink
informatief, zoals ls
, “Vereenvoudigd” “Paden, substitueren ?
voor dingen zoals 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"
26
Ik geloof dat ik deze heb. Ik ben laat aan het feest, maar ik denk dat sommigen zullen waarderen dat het hier is als ze deze draad tegenkomen. De opmerkingen moeten uitleggen:
#!/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: "$@"\n" ;} # Borrow a horrible signal name.
# Probably best not to install as 'pathfull', if you can avoid it.
pathfull(){ cd "$(dirname "$@")"; link="$(readlink "$(basename "$@")")"
## 'test and 'ls' report different status for bad symlinks, so we use this.
if [ ! -e "$@" ]; then if $(ls -d "$@" 2>/dev/null) 2>/dev/null; then
errnoent 1>&2; exit 1; elif [ ! -e "$@" -a "$link" = "$@" ]; then
recurses 1>&2; exit 1; elif [ ! -e "$@" ] && [ ! -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 "$@" | cut -c1)" = '/' ]; then
printf "$@\n"; exit 0; else printf "$(pwd)/$(basename "$@")\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 "$@"
fi
Antwoord 27
Probeer de volgende cross-compatibele oplossing:
CWD="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
Omdat commando’s zoals realpath
of readlink
mogelijk niet beschikbaar zijn (afhankelijk van het besturingssysteem).
Opmerking: in Bash wordt aanbevolen om ${BASH_SOURCE[0]}
te gebruiken in plaats van $0
, anders kan het pad breken bij het sourcen van het bestand (source
/.
).
Als alternatief kunt u de volgende functie in Bash proberen:
realpath () {
[[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
}
Deze functie heeft één argument nodig. Als het argument al een absoluut pad heeft, druk het dan af zoals het is, druk anders de variabele $PWD
+ bestandsnaamargument af (zonder het voorvoegsel ./
).
Gerelateerd: