Dapatkan dirname / nama file terakhir dalam argumen jalur file di Bash

228

Saya mencoba menulis hook pasca-komitmen untuk SVN, yang di-host di server pengembangan kami. Tujuan saya adalah mencoba untuk secara otomatis checkout salinan dari proyek yang dikomit ke direktori di mana ia di-host di server. Namun saya harus dapat membaca hanya direktori terakhir dalam string direktori yang diteruskan ke skrip untuk checkout ke sub-direktori yang sama di mana proyek kami di-host.

Sebagai contoh jika saya membuat komitmen SVN untuk proyek "contoh", skrip saya mendapat "/ usr / local / svn / repos / example" sebagai argumen pertama. Saya perlu mendapatkan "contoh" dari ujung string dan kemudian menyatukannya dengan string lain sehingga saya dapat checkout ke "/ server / root / example" dan melihat perubahan langsung.

TJ L
sumber

Jawaban:

344

basename tidak menghapus direktori awalan jalur:

$ basename /usr/local/svn/repos/example
example
$ echo "/server/root/$(basename /usr/local/svn/repos/example)"
/server/root/example
sth
sumber
2
basename pasti yang saya cari. Bagaimana bisa mendapatkan nama dasar argumen disimpan ke dalam variabel? MisalnyaSUBDIR="/path/to/whatever/$(basename $1)"
TJ L
5
@ tj111: kedengarannya seperti tidak ada $1, atau $1kosong
sth
Sayangnya, jika Anda membungkus perintah, nama bas bukan ide yang baik. hanya sesuatu yang perlu diingat
dtc
98

Pendekatan berikut dapat digunakan untuk mendapatkan lintasan apa pun dari lintasan:

some_path=a/b/c
echo $(basename $some_path)
echo $(basename $(dirname $some_path))
echo $(basename $(dirname $(dirname $some_path)))

Keluaran:

c
b
a
Jingguo Yao
sumber
1
tidak bekerja dengan jalur yang memiliki spasi ... Anda dapat mengatasinya dengan tanda kutip ... yang entah bagaimana berfungsiecho "$(basename "$(dirname "$pathname")")"
Ray Foss
75

Bash bisa mendapatkan bagian terakhir dari jalur tanpa harus memanggil eksternal basename:

subdir="/path/to/whatever/${1##*/}"
Dijeda sampai pemberitahuan lebih lanjut.
sumber
2
Di Mac saya, menggunakan notasi substring lebih dari urutan besarnya lebih cepat daripada dirname / nama dasar untuk kasus di mana Anda melakukan sesuatu yang sepele untuk masing-masing dari beberapa ribu file.
George
1
d=/home/me/somefolder;subdir="/$d/${1##*/}"Saya akhirnya dengan sesuatu seperti //home/me/somefolder//$ d sebenarnya berasal dari loop for d in $(find $SOMEFOLDER -maxdepth 1 -type d);Menggunakan subdir=$(basename $d)karya seperti yang diharapkan.
Buttle Butkus
1
@ButtleButkus: Anda harus menggunakan whilealih-alih formengulangi output dari find( find -print0 | xargs -0lebih baik) atau menggunakan globbing: for d in $SOMEFOLDER/*/(garis miring terakhir berfungsi seperti -type d- Anda dapat menggunakan **di Bash 4 untuk rekursi jika Anda shopt -s globstar, tetapi pesan "Argument list too long" adalah bisa jadi). Perhatikan bahwa ${1}bagian dari perintah mewakili argumen pertama dari skrip atau fungsi. Anda mungkin perlu menggunakan ${d##*/}atau beberapa spesifikasi variabel atau argumen atau memastikan bahwa suatu argumen sedang diajukan$1
Dijeda sampai pemberitahuan lebih lanjut.
2
@DennisWilliamson Terima kasih banyak untuk berbagi . Untuk pembaca masa depan yang mulai bertanya - tanya mengapa itu tidak berfungsi atau saya satu-satunya yang bodoh di sini 😂. Di atas jawabannya anggap $1mengandung the path from which last component is to be taken out. Saya melewatkan bagian itu. target_path='/home/user/dir1/dir2/dir3/'; target_path="${target_path%/}"; last_component=${target_path##*/}; echo $last_componentKasing penggunaan saya: - Bekerja 😉
Vinay Vissh
2
Lihat di sini untuk penjelasan tentang bagaimana dan mengapa ${1##*/} berfungsi: unix.stackexchange.com/a/171786/15070
Matthew