Pada dasarnya saya perlu menjalankan skrip dengan jalur yang terkait dengan lokasi file skrip shell, bagaimana saya bisa mengubah direktori saat ini ke direktori yang sama dengan tempat file skrip berada?
Pada dasarnya saya perlu menjalankan skrip dengan jalur yang terkait dengan lokasi file skrip shell, bagaimana saya bisa mengubah direktori saat ini ke direktori yang sama dengan tempat file skrip berada?
Jawaban:
Di Bash, Anda harus mendapatkan yang Anda butuhkan seperti ini:
#!/usr/bin/env bash
BASEDIR=$(dirname "$0")
echo "$BASEDIR"
readlinkjuga (lihat jawaban al di bawah ini)
$BASH_SOURCEsebagai pengganti $0, karena $0tidak selalu berisi jalur skrip yang dipanggil, seperti ketika 'sumber' skrip.
$BASH_SOURCEspesifik Bash, pertanyaannya adalah tentang skrip shell secara umum.
CUR_PATH=$(pwd)atau pwdkembalikan direktori saat ini (yang tidak harus berupa dir induk skrip)!
$BASH_SOURCE, dan mengembalikan apa yang saya butuhkan. Skrip saya dipanggil dari skrip lain, dan $0kembali .sambil $BASH_SOURCEmengembalikan subdirektori yang tepat (dalam kasus saya scripts).
Posting asli berisi solusi (abaikan tanggapan, mereka tidak menambahkan sesuatu yang berguna). Pekerjaan yang menarik dilakukan oleh perintah unix yang disebutkan readlinkdengan opsi -f. Bekerja ketika skrip dipanggil oleh jalur absolut dan relatif.
Untuk 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
Untuk 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
Lihat juga: https://stackoverflow.com/a/246128/59087
readlink. Itu sebabnya saya merekomendasikan menggunakan pushd / popd (built-in untuk bash).
-fpilihan untuk readlinkmelakukan sesuatu yang berbeda pada OS X (Lion) dan mungkin BSD. stackoverflow.com/questions/1055671/…
-fsama sekali tidak didukung pada OS X (pada Lion); di sana Anda dapat membatalkan -funtuk menyelesaikan paling banyak satu tingkat tipuan, misalnya pushd "$(dirname "$(readlink "$BASH_SOURCE" || echo "$BASH_SOURCE")")", atau Anda dapat menggulung skrip berikut symlink-rekursif Anda sendiri seperti yang ditunjukkan dalam posting yang ditautkan.
sh /some/other/directory/script.sh), Dalam hal ini .akan menjadi pwd Anda, bukan/some/other/directory
Komentar sebelumnya tentang jawaban mengatakannya, tetapi mudah untuk melewatkan di antara semua jawaban lainnya.
Saat menggunakan bash:
echo this file: "$BASH_SOURCE"
echo this dir: "$(dirname "$BASH_SOURCE")"
dirname "$BASH_SOURCE"untuk menangani spasi dalam $ BASH_SOURCE.
"$(dirname "${BASH_SOURCE[0]}")"
Dengan asumsi Anda menggunakan bash
#!/bin/bash
current_dir=$(pwd)
script_dir=$(dirname $0)
echo $current_dir
echo $script_dir
Skrip ini harus mencetak direktori tempat Anda berada, dan kemudian direktori tempat skrip berada. Misalnya, saat memanggilnya /dengan skrip /home/mez/, ia menampilkan
/
/home/mez
Ingat, ketika menetapkan variabel dari output perintah, bungkus perintah di $(dan )- atau Anda tidak akan mendapatkan output yang diinginkan.
Jika Anda menggunakan 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/ popddengan cd $(dirname "${0}")dan cd -untuk membuatnya bekerja pada shell lain, jika mereka memiliki pwd -L.
Seperti yang dikatakan theMarko:
BASEDIR=$(dirname $0)
echo $BASEDIR
Ini berfungsi kecuali jika Anda menjalankan skrip dari direktori yang sama di mana skrip berada, dalam hal ini Anda mendapatkan nilai '.'
Untuk menyiasatinya gunakan:
current_dir=$(pwd)
script_dir=$(dirname $0)
if [ $script_dir = '.' ]
then
script_dir="$current_dir"
fi
Anda sekarang dapat menggunakan variabel current_dir di seluruh skrip Anda untuk merujuk ke direktori skrip. Namun ini mungkin masih memiliki masalah symlink.
Jawaban terbaik untuk pertanyaan ini dijawab di sini:
Mendapatkan direktori sumber skrip Bash dari dalam
Dan itu adalah:
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
One-liner yang akan memberi Anda nama direktori lengkap dari skrip di mana pun ia dipanggil.
Untuk memahami cara kerjanya, Anda dapat menjalankan skrip berikut:
#!/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'"
cd $(dirname $(readlink -f $0))
Mari menjadikannya POSIX oneliner:
a="/$0"; a=${a%/*}; a=${a#/}; a=${a:-.}; BASEDIR=$(cd "$a"; pwd)
Diuji pada banyak cangkang yang kompatibel dengan Bourne termasuk yang BSD.
Sejauh yang saya tahu saya adalah penulis dan saya memasukkannya ke dalam domain publik. Untuk info lebih lanjut, lihat: https://www.jasan.tk/posts/2017-05-11-posix_shell_dirname_replacement/
cd: too many argumentsjika spasi di jalan, dan kembali $PWD. (perbaikan yang jelas, tetapi hanya menunjukkan berapa banyak tepi kasus sebenarnya ada)
Jika Anda ingin mendapatkan direktori skrip aktual (terlepas dari apakah Anda menggunakan skrip menggunakan symlink atau langsung), cobalah:
BASEDIR=$(dirname $(realpath "$0"))
echo "$BASEDIR"
Ini berfungsi baik di linux dan macOS. Saya tidak bisa melihat orang di sini menyebutkan tentang realpath. Tidak yakin apakah ada kelemahan dalam pendekatan ini.
pada macOS, Anda perlu menginstal coreutilsuntuk menggunakan realpath. Misalnya: brew install coreutils.
PENGANTAR
Jawaban ini mengoreksi jawaban yang dipilih sangat rusak namun mengejutkan atas utas ini (ditulis oleh TheMarko):
#!/usr/bin/env bash
BASEDIR=$(dirname "$0")
echo "$BASEDIR"
MENGAPA MENGGUNAKAN dirname "$ 0" PADA SAJA BUKAN BEKERJA?
dirname $ 0 hanya akan berfungsi jika pengguna meluncurkan skrip dengan cara yang sangat spesifik. Saya dapat menemukan beberapa situasi di mana jawaban ini gagal dan membuat skrip mogok.
Pertama-tama, mari kita pahami bagaimana jawaban ini bekerja. Dia mendapatkan direktori script dengan melakukan
dirname "$0"
$ 0 mewakili bagian pertama dari perintah yang memanggil skrip (pada dasarnya itu adalah perintah yang dimasukkan tanpa argumen:
/some/path/./script argument1 argument2
$ 0 = "/ some / path /./ script"
dirname pada dasarnya menemukan yang terakhir / dalam sebuah string dan memotongnya di sana. Jadi, jika Anda melakukannya:
dirname /usr/bin/sha256sum
Anda akan mendapatkan: / usr / bin
Contoh ini berfungsi dengan baik karena / usr / bin / sha256sum adalah path yang diformat dengan benar tetapi
dirname "/some/path/./script"
tidak akan bekerja dengan baik dan akan memberi Anda:
BASENAME="/some/path/." #which would crash your script if you try to use it as a path
Katakanlah Anda berada di dir yang sama dengan skrip Anda dan Anda meluncurkannya dengan perintah ini
./script
$ 0 dalam situasi ini adalah ./script dan dirname $ 0 akan memberikan:
. #or BASEDIR=".", again this will crash your script
Menggunakan:
sh script
Tanpa memasukkan lintasan lengkap juga akan memberikan BASEDIR = "."
Menggunakan direktori relatif:
../some/path/./script
Memberikan dirname $ 0 dari:
../some/path/.
Jika Anda berada di direktori / some dan Anda memanggil skrip dengan cara ini (perhatikan tidak adanya / di awal, lagi-lagi jalur relatif):
path/./script.sh
Anda akan mendapatkan nilai ini untuk dirname $ 0:
path/.
dan ./path/./script (bentuk lain dari jalur relatif) memberikan:
./path/.
Hanya dua situasi di mana baseir $ 0 akan berfungsi adalah jika pengguna menggunakan sh atau sentuh untuk meluncurkan skrip karena keduanya akan menghasilkan $ 0:
$0=/some/path/script
yang akan memberi Anda jalan yang dapat Anda gunakan dengan dirname.
SOLUSINYA
Anda akan memiliki akun untuk dan mendeteksi setiap situasi yang disebutkan di atas dan menerapkan perbaikan untuk itu jika muncul:
#!/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
One-liner ini memberitahukan di mana skrip shell berada, tidak masalah apakah Anda menjalankannya atau jika Anda membuatnya . Juga, ini menyelesaikan tautan simbolis yang terlibat, jika itu masalahnya:
dir=$(dirname $(test -L "$BASH_SOURCE" && readlink -f "$BASH_SOURCE" || echo "$BASH_SOURCE"))
Omong-omong, saya kira Anda menggunakan / bin / bash .
Begitu banyak jawaban, semuanya masuk akal, masing-masing dengan tujuan pro dan kontra & sedikit berbeda (yang mungkin harus dinyatakan untuk masing-masing). Berikut adalah solusi lain yang memenuhi tujuan utama yaitu menjadi jelas dan bekerja di semua sistem, pada semua bash (tidak ada asumsi tentang versi bash, readlinkatau pwdopsi), dan melakukan apa yang Anda harapkan terjadi secara wajar (misalnya, menyelesaikan symlink adalah suatu masalah yang menarik, tetapi biasanya tidak seperti yang Anda inginkan), menangani kasus tepi seperti spasi di jalur, dll., mengabaikan kesalahan apa pun dan menggunakan standar waras jika ada masalah.
Setiap komponen disimpan dalam variabel terpisah yang dapat Anda gunakan secara individual:
# 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)"
Terinspirasi oleh jawaban blueyed
read < <(readlink -f $0 | xargs dirname)
cd $REPLY
Itu harus melakukan trik:
echo `pwd`/`dirname $0`
Mungkin terlihat jelek tergantung pada bagaimana ia dipanggil dan cwd tetapi harus membuat Anda pergi ke mana Anda harus pergi (atau Anda dapat mengubah string jika Anda peduli bagaimana tampilannya).
`pwd`/`dirname $0`tetapi mungkin masih gagal pada symlinks