Dapatkan nama direktori saat ini (tanpa path lengkap) dalam skrip Bash

820

Bagaimana saya mendapatkan hanya nama direktori kerja saat ini dalam skrip bash, atau bahkan lebih baik, hanya perintah terminal.

pwdmemberikan path lengkap dari direktori kerja saat ini, misalnya /opt/local/bintetapi saya hanya inginbin

Derek Dahmer
sumber
1
Dengan path lengkap, lihat: Mendapatkan direktori sumber skrip Bash dari dalam .
kenorb

Jawaban:

1116

Tidak perlu basename, dan terutama tidak perlu untuk menjalankan pwd subkulit (yang menambahkan operasi garpu tambahan, dan mahal ); shell dapat melakukan ini secara internal menggunakan ekspansi parameter :

result=${PWD##*/}          # to assign to a variable

printf '%s\n' "${PWD##*/}" # to print to stdout
                           # ...more robust than echo for unusual names
                           #    (consider a directory named -e or -n)

printf '%q\n' "${PWD##*/}" # to print to stdout, quoted for use as shell input
                           # ...useful to make hidden characters readable.

Perhatikan bahwa jika Anda menerapkan teknik ini dalam keadaan lain (tidak PWD, tetapi beberapa variabel lain yang memegang nama direktori), Anda mungkin perlu memotong garis miring apa pun. Di bawah ini menggunakan dukungan extglob bash untuk bekerja bahkan dengan beberapa garis miring:

dirname=/path/to/somewhere//
shopt -s extglob           # enable +(...) glob syntax
result=${dirname%%+(/)}    # trim however many trailing slashes exist
result=${result##*/}       # remove everything before the last / that still remains
printf '%s\n' "$result"

Atau, tanpa extglob:

dirname="/path/to/somewhere//"
result="${dirname%"${dirname##*[!/]}"}" # extglob-free multi-trailing-/ trim
result="${result##*/}"                  # remove everything before the last /
Charles Duffy
sumber
31
Apa perbedaan antara $ {PWD ## * /} dan $ PWD?
Mr_Chimp
24
@ Mr_Chimp yang pertama adalah operasi ekspansi parameter yang memotong sisa direktori untuk hanya menyediakan nama dasar. Saya memiliki tautan dalam jawaban saya untuk dokumentasi ekspansi parameter; jangan ragu untuk mengikuti itu jika Anda memiliki pertanyaan.
Charles Duffy
24
@stefgosselin $PWDadalah nama variabel bash bawaan ; semua nama variabel bawaan ada dalam huruf kapital semua (untuk membedakannya dari variabel lokal, yang harus selalu memiliki setidaknya satu karakter huruf kecil). result=${PWD#*/}tidak tidak mengevaluasi /full/path/to/directory; alih-alih, itu hanya menghapus elemen pertama, membuatnya path/to/directory; menggunakan dua #karakter membuat pola cocok serakah, serasi karakter sebanyak mungkin. Baca halaman ekspansi parameter, ditautkan dalam jawaban, untuk lebih lanjut.
Charles Duffy
5
Perhatikan bahwa output dari pwdtidak selalu sama dengan nilai $PWD, karena komplikasi yang timbul dari cdtautan simbolik, apakah bash memiliki -o physicalopsi yang disetel, dan sebagainya. Ini digunakan untuk menjadi sangat tidak enak di sekitar penanganan direktori automounted, di mana merekam jalur fisik bukannya yang logis akan menghasilkan jalur yang, jika digunakan, akan memungkinkan automounter untuk secara spontan menurunkan direktori yang digunakan.
Alex North-Keys
3
Bagus! Seseorang harus menulis buku untuk orang-orang tua dari Kalimantan seperti saya. Mungkin ada satu di luar sana? Itu bisa memiliki admin sys crotchity tua mengatakan hal-hal seperti, "kembali pada hari saya, kami hanya shdan cshdan jika Anda ingin kunci backspace bekerja Anda harus membaca sttyhalaman manual keseluruhan , dan kami menyukainya!"
Red Cricket
335

Gunakan basenameprogramnya. Untuk kasus Anda:

% basename "$PWD"
bin
Arkady
sumber
20
Bukankah ini tujuan dari basenameprogram ini? Apa yang salah dengan jawaban ini selain melewatkan tanda kutip?
Nacht - Reinstate Monica
11
@Nacht basenamememang program eksternal yang melakukan hal yang benar, tetapi menjalankan setiap program eksternal untuk hal pesta dapat melakukan out-of-the-box dengan hanya menggunakan built-in fungsi konyol, menimbulkan dampak kinerja ( fork(), execve(), wait(), dll) untuk tak ada alasan.
Charles Duffy
3
... sebagai tambahan, keberatan saya untuk di basenamesini tidak berlaku dirname, karena dirnamememiliki fungsi yang ${PWD##*/}tidak - mengubah string tanpa garis miring ., misalnya. Jadi, sementara menggunakan dirnamesebagai alat eksternal memiliki overhead kinerja, itu juga memiliki fungsi yang membantu untuk mengkompensasi yang sama.
Charles Duffy
4
@LouisMaddox, sedikit kibitzing: functionKata kuncinya adalah POSIX-tidak cocok tanpa menambahkan nilai apa pun atas sintaks standar, dan kurangnya tanda kutip akan membuat bug dalam nama direktori dengan spasi. wdexec() { "./$(basename "$PWD")"; }mungkin menjadi alternatif yang lebih disukai.
Charles Duffy
28
Tentu menggunakan basenamekurang "efisien". Tapi itu mungkin lebih efisien dalam hal produktivitas pengembang karena mudah diingat dibandingkan dengan sintaksis jelek. Jadi, jika Anda mengingatnya, lakukanlah. Kalau tidak, basenameberfungsi juga. Adakah yang pernah harus meningkatkan "kinerja" skrip bash dan membuatnya lebih efisien?
usr
139
$ echo "${PWD##*/}"

​​​​​

DigitalRoss
sumber
2
@ jocap ... kecuali bahwa penggunaan gema di sana, tanpa mengutip argumen, salah . Jika nama direktori memiliki banyak spasi, atau karakter tab, itu akan diciutkan menjadi satu spasi; jika memiliki karakter wildcard, itu akan diperluas. Penggunaan yang benar akan echo "${PWD##*/}".
Charles Duffy
9
@ jocap ... juga, saya tidak yakin bahwa "gema" sebenarnya adalah bagian sah dari jawabannya. Pertanyaannya adalah bagaimana mendapatkan jawabannya, bukan bagaimana mencetak jawabannya; jika tujuannya adalah untuk menetapkannya ke variabel, misalnya, name=${PWD##*/}akan benar, dan name=$(echo "${PWD##*/}")akan salah (dalam arti tidak perlu-inefisiensi).
Charles Duffy
24

Anda dapat menggunakan kombinasi pwd dan basename. Misalnya

#!/bin/bash

CURRENT=`pwd`
BASENAME=`basename "$CURRENT"`

echo "$BASENAME"

exit;
mbelos
sumber
18
Tolong jangan. Backtick membuat subkulit (dengan demikian, operasi fork) - dan dalam hal ini, Anda menggunakannya dua kali ! [Sebagai tambahan, meskipun berdalih gaya - nama variabel harus huruf kecil kecuali Anda mengekspornya ke lingkungan atau itu adalah kasus khusus, khusus].
Charles Duffy
11
dan sebagai backtics quibble gaya kedua harus diganti dengan $ (). masih bercabang tetapi lebih mudah dibaca dan dengan lebih sedikit bantuan dibutuhkan.
Jeremy Wall
1
Berdasarkan konvensi, variabel lingkungan (PATH, EDITOR, SHELL, ...) dan variabel shell internal (BASH_VERSION, RANDOM, ...) sepenuhnya dikapitalisasi. Semua nama variabel lainnya harus huruf kecil. Karena nama variabel peka terhadap huruf besar-kecil, konvensi ini menghindari variabel lingkungan dan internal yang sengaja ditimpa.
Rany Albeg Wein
2
Tergantung pada konvensi. Orang bisa berpendapat bahwa semua variabel shell adalah variabel lingkungan (tergantung pada shell), dan dengan demikian semua variabel shell harus dalam huruf kapital semua. Orang lain dapat berdebat berdasarkan ruang lingkup - variabel global semuanya terbatas, sedangkan variabel lokal lebih kecil, dan ini semua adalah global. Namun yang lain mungkin berpendapat bahwa membuat variabel huruf besar semua menambah lapisan kontras dengan perintah skrip shell, yang juga semua huruf kecil tapi bermakna kata-kata bermakna. Saya mungkin atau mungkin tidak / setuju / dengan salah satu argumen itu, tetapi saya telah melihat ketiganya digunakan. :)
dannysauer
17

Bagaimana dengan grep:

pwd | grep -o '[^/]*$'
jeruk
sumber
tidak akan merekomendasikan karena tampaknya mendapatkan beberapa informasi warna sedangkan pwd | xargs basenametidak .. mungkin tidak begitu penting tetapi jawaban lainnya lebih sederhana dan lebih konsisten di seluruh lingkungan
davidhq
8

Saya suka jawaban yang dipilih (Charles Duffy), tetapi hati-hati jika Anda berada di direktori yang disinkronkan dan Anda ingin nama direktori target. Sayangnya saya tidak berpikir itu bisa dilakukan dalam ekspresi ekspansi parameter tunggal, mungkin saya salah. Ini seharusnya bekerja:

target_PWD=$(readlink -f .)
echo ${target_PWD##*/}

Untuk melihat ini, percobaan:

cd foo
ln -s . bar
echo ${PWD##*/}

melaporkan "bilah"

DIRNAME

Untuk menampilkan direktori terkemuka dari sebuah path (tanpa mengeluarkan fork-exec dari / usr / bin / dirname):

echo ${target_PWD%/*}

Ini misalnya akan mengubah foo / bar / baz -> foo / bar

FDS
sumber
5
Sayangnya, readlink -fini adalah ekstensi GNU, dan karenanya tidak tersedia pada BSD (termasuk OS X).
Xiong Chiamiov
8
echo "$PWD" | sed 's!.*/!!'

Jika Anda menggunakan shell Bourne atau ${PWD##*/}tidak tersedia.

anomal
sumber
4
FYI, ${PWD##*/}adalah POSIX sh - setiap modern / bin / sh (termasuk tanda hubung, abu, dll) mendukungnya; untuk menekan Bourne yang sebenarnya pada kotak baru-baru ini, Anda harus menggunakan sistem Solaris yang agak tua. Di luar itu - echo "$PWD"; meninggalkan tanda kutip menyebabkan bug (jika nama direktori memiliki spasi, karakter wildcard, dll).
Charles Duffy
1
Ya, saya menggunakan sistem Solaris yang oldish. Saya telah memperbarui perintah untuk menggunakan kutipan.
anomal
7

Utas ini hebat! Ini satu lagi rasa:

pwd | awk -F / '{print $NF}'
rodvlopes
sumber
5

Anehnya, tidak ada yang menyebutkan alternatif ini yang hanya menggunakan perintah bash bawaan:

i="$IFS";IFS='/';set -f;p=($PWD);set +f;IFS="$i";echo "${p[-1]}"

Sebagai bonus tambahan, Anda dapat dengan mudah mendapatkan nama direktori induk dengan:

[ "${#p[@]}" -gt 1 ] && echo "${p[-2]}"

Ini akan bekerja pada Bash 4.3-alpha atau lebih baru.

Arton Dorneles
sumber
secara mengejutkan? hanya bercanda, tetapi yang lain jauh lebih pendek dan lebih mudah diingat
codewandler
Ini cukup lucu = P Belum pernah mendengar tentang $ IFS (pemisah bidang internal) sebelum ini. pesta saya terlalu tua, tetapi dapat mengujinya di sini: jdoodle.com/test-bash-shell-script-online
Stan Kurdziel
5

Menggunakan:

basename "$PWD"

ATAU

IFS=/ 
var=($PWD)
echo ${var[-1]} 

Ubah Internal Filename Separator (IFS) kembali ke ruang kosong.

IFS= 

Ada satu ruang setelah IFS.

FireInTheSky
sumber
4
basename $(pwd)

atau

echo "$(basename $(pwd))"
pengguna3278975
sumber
2
Membutuhkan lebih banyak tanda kutip untuk menjadi benar - seperti itu, output dari pwdstring-split dan glob-diperluas sebelum diteruskan ke basename.
Charles Duffy
Jadi, basename "$(pwd)"- meskipun itu sangat tidak efisien dibandingkan dengan adil basename "$PWD", yang dengan sendirinya tidak efisien dibandingkan dengan menggunakan ekspansi parameter alih-alih memanggil basenamesama sekali.
Charles Duffy
4

Untuk menemukan joki di luar sana seperti saya:

find $PWD -maxdepth 0 -printf "%f\n"
pengguna2208522
sumber
Ubah $PWDuntuk "$PWD"untuk secara benar menangani nama direktori yang tidak biasa.
Charles Duffy
3

Saya biasanya menggunakan ini dalam skrip sh

SCRIPTSRC=`readlink -f "$0" || echo "$0"`
RUN_PATH=`dirname "${SCRIPTSRC}" || echo .`
echo "Running from ${RUN_PATH}"
...
cd ${RUN_PATH}/subfolder

Anda dapat menggunakan ini untuk mengotomatiskan hal-hal ...

Kemuliaan Belanda
sumber
readlink: illegal option -- f usage: readlink [-n] [file ...]
2

Grep bawah dengan regex juga berfungsi,

>pwd | grep -o "\w*-*$"
Abhishek Gurjar
sumber
3
Tidak berfungsi jika nama direktori berisi karakter "-".
daniula
1

Cukup gunakan:

pwd | xargs basename

atau

basename "`pwd`"
marcos
sumber
2
Ini dalam semua hal versi yang lebih buruk dari jawaban yang sebelumnya diberikan oleh Arkady ( stackoverflow.com/a/1371267/14122 ): xargsFormultion tidak efisien dan bermasalah ketika nama direktori berisi baris baru atau karakter kutipan, dan formulasi kedua memanggil pwdperintah dalam subkulit, alih-alih mengambil hasil yang sama melalui ekspansi variabel bawaan.
Charles Duffy
@CharlesDuffy Namun, apakah itu jawaban yang valid dan praktis untuk pertanyaan itu.
bachph
1

Jika Anda hanya ingin melihat direktori saat ini di wilayah bash prompt, Anda dapat mengedit .bashrcfile di ~. Ubah \wke \Wdalam baris:

PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '

Lari source ~/.bashrc dan itu hanya akan menampilkan nama direktori di wilayah prompt.

Ref: /superuser/60555/show-only-current-directory-name-not-full-path-on-bash-prompt

Gautam Sreekumar
sumber
0

Anda dapat menggunakan utilitas basename yang menghapus awalan yang diakhiri dengan / dan akhiran (jika ada dalam string) dari string, dan mencetak hasilnya pada output standar.

$basename <path-of-directory>
niks_4143
sumber
0

Saya sangat suka menggunakan gbasename, yang merupakan bagian dari GNU coreutils.

Steve Benner
sumber
FYI, itu tidak datang dengan distribusi Ubuntu saya.
Katastic Voyage
@KatasticVoyage, ada di Ubuntu, hanya dipanggil di basenamesana. Ini biasanya dipanggil gbasenamepada MacOS dan platform lain yang jika tidak dikirimkan dengan nama pengguna non-GNU.
Charles Duffy
-1

Perintah berikut ini akan menghasilkan pencetakan direktori kerja Anda saat ini dalam skrip bash.

pushd .
CURRENT_DIR="`cd $1; pwd`"
popd
echo $CURRENT_DIR
Paul Sabou
sumber
2
(1) Saya tidak yakin di mana kami memastikan bahwa daftar argumen sedemikian rupa sehingga $1akan berisi direktori kerja saat ini. (2) The pushddanpopd tidak ada gunanya di sini karena apa pun di dalam backticks dilakukan dalam subkulit - sehingga tidak dapat memengaruhi direktori induk shell untuk memulainya. (3) Menggunakan "$(cd "$1"; pwd)"akan lebih mudah dibaca dan tahan terhadap nama direktori dengan spasi.
Charles Duffy