Bagaimana cara mengatur direktori kerja saat ini ke direktori skrip?

569

Saya sedang menulis skrip bash. Saya membutuhkan direktori kerja saat ini untuk selalu menjadi direktori tempat skrip berada.

Perilaku default adalah bahwa direktori kerja saat ini dalam skrip adalah dari shell tempat saya menjalankannya, tapi saya tidak ingin perilaku ini.

jameshfisher
sumber
Sudahkah Anda mempertimbangkan untuk meletakkan skrip wrapper di suatu tempat seperti / usr / bin untuk cd ke direktori yang benar (hardcoded) dan kemudian jalankan skrip Anda?
Dagg Nabbit
2
Mengapa Anda memerlukan direktori skrip? Mungkin ada cara yang lebih baik untuk menyelesaikan masalah yang mendasarinya.
bstpierre
12
Saya hanya ingin menunjukkan bahwa perilaku yang Anda panggil "jelas tidak diinginkan" sebenarnya sangat diperlukan - jika saya menjalankan myscript path/to/filesaya berharap skrip untuk mengevaluasi path / ke / file relatif ke direktori MY saat ini, bukan direktori apa pun yang terjadi skrip yang akan berlokasi di. Juga, apa yang akan Anda memiliki terjadi untuk script dijalankan dengan ssh remotehost bash < ./myscriptsebagai BASH FAQ menyebutkan?
Gordon Davisson
3
cd "${BASH_SOURCE%/*}" || exit
gak

Jawaban:

656
#!/bin/bash
cd "$(dirname "$0")"
ndim
sumber
7
dirname mengembalikan '.' saat menggunakan bash di Windows. Jadi, jawaban Paul lebih baik.
Tvaroh
7
Juga mengembalikan '.' di Mac OSX
Ben Clayton
4
Perlu dicatat bahwa hal-hal dapat rusak jika tautan simbolik menjadi bagian darinya $0. Dalam skrip Anda, Anda mungkin berharap, misalnya, ../../untuk merujuk ke direktori dua tingkat di atas lokasi skrip, tetapi ini tidak selalu terjadi jika tautan simbolik sedang dimainkan.
Brian Gordon
20
Jika Anda memanggil skrip sebagai ./script, .adalah direktori yang benar, dan mengubahnya .juga akan berakhir di direktori di mana scriptberada, yaitu di direktori kerja saat ini.
ndim
6
Jika Anda menjalankan skrip dari direktori saat ini seperti itu bash script.sh, maka nilainya $0adalah script.sh. Satu-satunya cara cdperintah akan "bekerja" untuk Anda adalah karena Anda tidak peduli dengan perintah yang gagal. Jika Anda menggunakan set -o errexit(alias:) set -euntuk memastikan bahwa skrip Anda tidak melewati perintah yang gagal, ini TIDAK akan berhasil karena cd script.shada kesalahan. Cara [khusus bash] yang dapat diandalkan adalahcd "$(dirname ${BASH_SOURCE[0]})"
Bruno Bronosky
322

Berikut ini juga berfungsi:

cd "${0%/*}"

Sintaksnya dijelaskan secara menyeluruh dalam jawaban StackOverflow ini .

Paul Schulz
sumber
17
Penjelasan cara kerjanya: stackoverflow.com/questions/6393551/…
kenorb
25
Jangan lupa untuk menyertakan tanda kutip jika path berisi spasi. yaitu cd "$ {0% / *}"
H.Rabiee
17
Ini gagal jika skrip dipanggil dengan bash script.sh- $0hanya akan menjadi nama file
Chris Watts
17
Saya akan mengatakan jawaban ini terlalu ringkas. Anda hampir tidak belajar apa pun tentang sintaksis. Beberapa deskripsi tentang cara membaca ini akan sangat membantu.
pertandingan
2
Trik ini tidak menelurkan subkulit juga jadi bagus untuk speed freaks.
teknopaul
184

Coba satu kalimat sederhana berikut:


Untuk semua UNIX / OSX / Linux

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

Pesta

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

Catatan: Tanda hubung ganda (-) digunakan dalam perintah untuk menandakan akhir dari opsi perintah, sehingga file yang berisi tanda hubung atau karakter khusus lainnya tidak akan melanggar perintah.

Catatan: Di Bash, gunakan yang ${BASH_SOURCE[0]}mendukung $0, jika tidak jalur tersebut dapat rusak saat mengambilnya ( source/ .).


Untuk Linux, Mac dan * BSD lainnya:

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

Catatan: realpathharus diinstal di distribusi Linux paling populer secara default (seperti Ubuntu), tetapi dalam beberapa itu bisa hilang, jadi Anda harus menginstalnya.

Catatan: Jika Anda menggunakan Bash, gunakan yang ${BASH_SOURCE[0]}mendukung $0, jika tidak, jalur tersebut dapat rusak saat mengambilnya ( source/ .).

Kalau tidak, Anda bisa mencoba sesuatu seperti itu (itu akan menggunakan alat pertama yang ada):

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

Khusus untuk Linux:

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

Menggunakan tautan baca GNU pada * BSD / Mac:

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

Catatan: Anda harus coreutilsmenginstal (mis. 1. Instal Homebrew , 2. brew install coreutils).


Di bash

Dalam bash, Anda dapat menggunakan Parameter Expansions untuk mencapai itu, seperti:

cd "${0%/*}"

tetapi tidak berfungsi jika skrip dijalankan dari direktori yang sama.

Atau Anda dapat mendefinisikan fungsi berikut di bash:

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

Fungsi ini membutuhkan 1 argumen. Jika argumen sudah memiliki path absolut, cetaklah sebagaimana adanya, jika tidak, cetak $PWDargumen variabel + nama file (tanpa ./awalan).

atau di sini adalah versi yang diambil dari .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"
}

Terkait:

Lihat juga:

Bagaimana saya bisa mendapatkan perilaku readlink -f GNU di Mac?

kenorb
sumber
4
jawaban yang jauh lebih baik daripada yang populer karena itu memecahkan syslinks, dan akun untuk OS yang berbeda. Terima kasih!
Dennis
Terima kasih atas jawaban ini. Yang paling atas bekerja di Mac saya ... tapi apa yang dilakukan - switch di cpperintah, @kenorb?
TobyG
3
Tanda hubung ganda ( --) digunakan dalam perintah untuk menandakan akhir dari opsi perintah, sehingga file yang mengandung tanda hubung atau karakter khusus lainnya tidak akan melanggar perintah. Coba misalnya membuat file via touch "-test"dan touch -- -test, lalu hapus file via rm "-test"dan rm -- -test, dan lihat perbedaannya.
kenorb
1
Saya akan menghapus upvote saya jika saya bisa: realpath sudah usang unix.stackexchange.com/questions/136494/…
lsh
1
@ lsh Dikatakan bahwa hanya realpathpaket Debian yang ditinggalkan, bukan GNU realpath. Jika menurut Anda tidak jelas, Anda dapat menyarankan hasil edit.
kenorb
73
cd "$(dirname "${BASH_SOURCE[0]}")"

Mudah. Berhasil.

Dan Moulding
sumber
1
Ini harus menjadi jawaban yang diterima. Bekerja pada OSX dan Linux Ubuntu.
David Rissato Cruz
5
Ini berfungsi baik ketika skrip B bersumber dari skrip A lainnya dan Anda perlu menggunakan jalur relatif terhadap skrip B. Terima kasih.
Enrico
5
Ini juga berfungsi di Cygwin bagi kita yang kurang beruntung harus menyentuh Windows.
Bruno Bronosky
3
Ataucd "${BASH_SOURCE%/*}" || exit
caw
Bekerja di macOS Mojave
Juan Boero
6

Jawaban yang diterima berfungsi dengan baik untuk skrip yang belum disinkronkan di tempat lain, seperti ke $PATH.

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

Namun jika skrip dijalankan melalui symlink,

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

Ini akan cd ke ~/bin/direktori dan bukan ~/project/direktori, yang mungkin akan memecah skrip Anda jika tujuan cdadalah untuk memasukkan dependensi relatif terhadap~/project/

Jawaban aman symlink adalah di bawah ini:

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

readlink -f diperlukan untuk menyelesaikan jalur absolut dari file yang berpotensi disinkronkan.

Kutipan diperlukan untuk mendukung filepath yang berpotensi mengandung spasi putih (praktik buruk, tetapi tidak aman untuk menganggap ini tidak akan terjadi)

James McGuigan
sumber
4

Skrip ini sepertinya berfungsi untuk saya:

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

Baris perintah pwd menggemakan lokasi skrip sebagai direktori kerja saat ini di mana pun saya menjalankannya.

Amardeep AC9MF
sumber
3
realpathtidak mungkin dipasang di mana-mana. Yang mungkin tidak masalah, tergantung situasi OP.
bstpierre
Sistem saya tidak memiliki realpathtetapi memiliki readlinkyang tampaknya serupa.
Dijeda sampai pemberitahuan lebih lanjut.
2
Dist mana yang tidak menginstal realpath secara default? Ubuntu memilikinya.
kenorb
1
cd "`dirname $(readlink -f ${0})`"

sumber
Bisakah Anda jelaskan jawaban Anda?
Zulu
1
Meskipun kode ini dapat membantu menyelesaikan masalah, kode ini tidak menjelaskan mengapa dan / atau bagaimana ia menjawab pertanyaan. Memberikan konteks tambahan ini akan secara signifikan meningkatkan nilai pendidikan jangka panjangnya. Harap edit jawaban Anda untuk menambahkan penjelasan, termasuk batasan dan asumsi apa yang berlaku.
Toby Speight
-5
echo $PWD

PWD adalah variabel lingkungan.

Maverick Holdem
sumber
5
Ini hanya memberikan direktori dipanggil dari, bukan direktori tempat skrip berada.
dagelf
-6

Jika Anda hanya perlu mencetak direktori kerja sekarang maka Anda dapat mengikuti ini.

$ vim test

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

Berikan izin eksekusi:

chmod u+x test

Kemudian jalankan skrip pada ./testsaat itu Anda dapat melihat direktori kerja saat ini.

Chandan
sumber
2
Pertanyaannya adalah bagaimana memastikan skrip berjalan di direktori sendiri , termasuk jika itu bukan direktori yang berfungsi. Jadi ini bukan jawaban. Lagi pula, mengapa melakukan ini alih-alih hanya ... berlari pwd? Sepertinya banyak tombol yang terbuang sia-sia bagi saya.
underscore_d