Nhận tên thư mục hiện tại (không có đường dẫn đầy đủ) trong tập lệnh Bash


820

Làm thế nào tôi có thể chỉ nhận được tên thư mục làm việc hiện tại trong một tập lệnh bash, hoặc thậm chí tốt hơn, chỉ là một lệnh đầu cuối.

pwdđưa ra đường dẫn đầy đủ của thư mục làm việc hiện tại, ví dụ /opt/local/binnhưng tôi chỉ muốnbin


1
Với đường dẫn đầy đủ, xem: Lấy thư mục nguồn của tập lệnh Bash từ bên trong .
kenorb

Câu trả lời:


1116

Không cần tên cơ sở, và đặc biệt là không cần một mạng con chạy pwd (có thêm một hoạt động ngã ba, và đắt tiền ); Shell có thể thực hiện điều này trong nội bộ bằng cách sử dụng mở rộng tham số :

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.

Lưu ý rằng nếu bạn đang áp dụng kỹ thuật này trong các trường hợp khác (không phải PWD, nhưng một số biến khác có tên thư mục), bạn có thể cần phải cắt bất kỳ dấu gạch chéo nào. Dưới đây sử dụng hỗ trợ extglob của bash để hoạt động ngay cả với nhiều dấu gạch chéo:

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"

Ngoài ra, không có extglob:

dirname="/path/to/somewhere//"
result="${dirname%"${dirname##*[!/]}"}" # extglob-free multi-trailing-/ trim
result="${result##*/}"                  # remove everything before the last /

31
Sự khác biệt giữa $ {PWD ## * /} và $ PWD là gì?
Mr_Chimp

24
@Mr_Chimp trước đây là một hoạt động mở rộng tham số mà cắt phần còn lại của thư mục để chỉ cung cấp tên cơ sở. Tôi có một liên kết trong câu trả lời của tôi với tài liệu về mở rộng tham số; hãy làm theo nếu bạn có bất kỳ câu hỏi nào
Charles Duffy

24
@stefgosselin $PWDlà tên của biến bash tích hợp; tất cả các tên biến tích hợp đều có trong tất cả các chữ hoa (để phân biệt chúng với các biến cục bộ, chúng phải luôn có ít nhất một ký tự chữ thường). result=${PWD#*/}không không đánh giá để /full/path/to/directory; thay vào đó, nó chỉ tước phần tử đầu tiên, làm cho nó path/to/directory; sử dụng hai #ký tự làm cho mô hình khớp với nhau, tham gia càng nhiều ký tự càng tốt. Đọc trang mở rộng tham số, được liên kết trong câu trả lời, để biết thêm.
Charles Duffy

5
Lưu ý rằng đầu ra từ pwdkhông phải lúc nào cũng giống với giá trị của $PWD, do các biến chứng phát sinh từ cding thông qua các liên kết tượng trưng, ​​cho dù bash có -o physicaltùy chọn được đặt hay không, v.v. Điều này được sử dụng để đặc biệt khó chịu khi xử lý các thư mục được tự động hóa, trong đó việc ghi lại đường dẫn vật lý thay vì đường dẫn logic sẽ tạo ra một đường dẫn, nếu được sử dụng, sẽ cho phép thiết bị tự động gỡ bỏ thư mục đang sử dụng.
Alex North-Keys

3
Đẹp! Ai đó nên viết một cuốn sách cho những người vỏ Borne cũ như tôi. Có lẽ có một cái ngoài đó? Nó có thể có một crotchity sys cũ ADMIN nói những thứ như thế, "trở lại trong ngày của tôi, chúng tôi chỉ shcshvà nếu bạn muốn phím backspace để làm việc bạn phải đọc toàn bộ sttytrang người đàn ông, và chúng tôi thích nó!"
Dế đỏ

335

Sử dụng basenamechương trình. Đối với trường hợp của bạn:

% basename "$PWD"
bin

20
Đây không phải là mục đích của basenamechương trình sao? Điều gì là sai với câu trả lời này ngoài việc thiếu dấu ngoặc kép?
Nacht - Tái lập Monica

11
@Nacht basenamethực sự là một chương trình bên ngoài mà làm điều đúng, nhưng chạy bất kỳ chương trình bên ngoài cho một bash điều có thể làm out-of-the-box chỉ sử dụng được xây dựng trong chức năng là ngớ ngẩn, phải gánh chịu tác động hiệu quả ( fork(), execve(), wait(), vv) cho không có lý do.
Charles Duffy

3
... như một bên, sự phản đối của tôi basenameở đây không áp dụng dirname, bởi vì dirnamecó chức năng ${PWD##*/}không - chẳng hạn như chuyển đổi một chuỗi không có dấu gạch chéo .. Do đó, trong khi sử dụng dirnamenhư một công cụ bên ngoài có chi phí hoạt động, nó cũng có chức năng giúp bù đắp tương tự.
Charles Duffy

4
@LouisMaddox, một chút kibitzing: functionTừ khóa không tương thích POSIX mà không thêm bất kỳ giá trị nào qua cú pháp được tiêu chuẩn hóa và việc thiếu dấu ngoặc kép sẽ tạo ra lỗi trong tên thư mục có khoảng trắng. wdexec() { "./$(basename "$PWD")"; }có thể là một lựa chọn tốt hơn
Charles Duffy

28
Chắc chắn sử dụng basenamelà ít "hiệu quả". Nhưng nó có thể hiệu quả hơn về năng suất của nhà phát triển vì nó dễ nhớ so với cú pháp bash xấu xí. Vì vậy, nếu bạn nhớ nó, đi cho nó. Nếu không, basenamehoạt động là tốt. Đã có ai từng phải cải thiện "hiệu suất" của tập lệnh bash và làm cho nó hiệu quả hơn chưa?
usr

139
$ echo "${PWD##*/}"

Hay nói, là một tài tài của, qua, qua, qua một tài khác, qua giữ, qua một tài khác


2
@jocap ... ngoại trừ việc sử dụng echo ở đó, mà không trích dẫn đối số, là sai . Nếu tên thư mục có nhiều khoảng trắng hoặc ký tự tab, nó sẽ được thu gọn thành một khoảng trắng; nếu nó có ký tự đại diện, nó sẽ được mở rộng. Sử dụng đúng sẽ được echo "${PWD##*/}".
Charles Duffy

9
@jocap ... đồng thời, tôi không chắc chắn rằng "tiếng vang" trên thực tế là một phần hợp pháp của câu trả lời. Câu hỏi là làm thế nào để có được câu trả lời, không phải cách in câu trả lời; ví dụ, nếu mục tiêu là gán nó cho một biến, name=${PWD##*/}sẽ đúng và name=$(echo "${PWD##*/}")sẽ sai (theo nghĩa không cần thiết là không hiệu quả).
Charles Duffy

24

Bạn có thể sử dụng kết hợp pwd và basename. Ví dụ

#!/bin/bash

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

echo "$BASENAME"

exit;

18
Xin đừng. Backticks tạo ra một subshell (do đó, một hoạt động ngã ba) - và trong trường hợp này, bạn đang sử dụng chúng hai lần ! [Là một bổ sung, mặc dù phân biệt kiểu cách - tên biến nên là chữ thường trừ khi bạn xuất chúng ra môi trường hoặc chúng là trường hợp đặc biệt, dành riêng].
Charles Duffy

11
và như là một phân tích ngược phân biệt kiểu thứ hai nên được thay thế bằng $ (). vẫn dĩa nhưng dễ đọc hơn và ít cần cắt bỏ hơn.
Jeremy Wall

1
Theo quy ước, các biến môi trường (PATH, EDITOR, SHELL, ...) và các biến shell bên trong (BASH_VERSION, RANDOM, ...) được viết hoa hoàn toàn. Tất cả các tên biến khác nên viết thường. Vì tên biến phân biệt chữ hoa chữ thường, nên quy ước này tránh vô tình ghi đè các biến môi trường và biến nội bộ.
Rany Albeg Wein

2
Phụ thuộc vào quy ước. Người ta có thể lập luận rằng tất cả các biến shell là biến môi trường (tùy thuộc vào shell), và do đó tất cả các biến shell phải ở trong tất cả các mũ. Một số người khác có thể tranh luận dựa trên phạm vi - các biến toàn cục là mũ toàn phần, trong khi các biến cục bộ là chữ thường và tất cả đều là toàn cục. Tuy nhiên, một người khác có thể lập luận rằng việc tạo các biến toàn chữ hoa sẽ thêm một lớp tương phản bổ sung với các tập lệnh shell rải rác, cũng là những từ viết thường nhưng ngắn có nghĩa. Tôi có thể hoặc không thể / đồng ý / với bất kỳ đối số nào trong số đó, nhưng tôi đã thấy cả ba cách sử dụng. :)
dannysauer

17

Làm thế nào về grep:

pwd | grep -o '[^/]*$'

sẽ không khuyến nghị bởi vì nó dường như nhận được một số thông tin màu sắc trong khi pwd | xargs basenamekhông .. có lẽ không quan trọng nhưng câu trả lời khác đơn giản và nhất quán hơn giữa các môi trường
davidhq

8

Tôi thích câu trả lời được chọn (Charles Duffy), nhưng hãy cẩn thận nếu bạn đang ở trong một thư mục liên kết tượng trưng và bạn muốn tên của thư mục tiêu. Thật không may, tôi không nghĩ rằng nó có thể được thực hiện trong một biểu thức mở rộng tham số duy nhất, có lẽ tôi đã nhầm. Điều này sẽ làm việc:

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

Để thấy điều này, một thử nghiệm:

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

báo cáo "thanh"

TRỰC TIẾP

Để hiển thị các thư mục hàng đầu của một đường dẫn (không phát sinh rẽ nhánh của / usr / bin / dirname):

echo ${target_PWD%/*}

Điều này sẽ ví dụ biến đổi foo / bar / baz -> foo / bar


5
Thật không may, readlink -flà một phần mở rộng GNU và do đó không có sẵn trên BSD (bao gồm cả OS X).
Xiong Chiamiov

8
echo "$PWD" | sed 's!.*/!!'

Nếu bạn đang sử dụng vỏ Bourne hoặc ${PWD##*/}không có sẵn.


4
FYI, ${PWD##*/}là POSIX sh - mọi hiện đại / bin / sh (bao gồm dash, tro, v.v.) đều hỗ trợ nó; để đạt được Bourne thực tế trên một hộp gần đây, bạn cần phải sử dụng hệ thống Solaris hơi cũ. Ngoài ra - echo "$PWD"; để lại dấu ngoặc kép dẫn đến lỗi (nếu tên thư mục có khoảng trắng, ký tự đại diện, v.v.).
Charles Duffy

1
Vâng, tôi đã sử dụng một hệ thống Solaris cũ. Tôi đã cập nhật lệnh để sử dụng dấu ngoặc kép.
bất thường

7

Chủ đề này là tuyệt vời! Đây là một hương vị nữa:

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

5

Đáng ngạc nhiên, không ai đề cập đến sự thay thế này chỉ sử dụng các lệnh bash tích hợp:

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

Là một phần thưởng bổ sung, bạn có thể dễ dàng có được tên của thư mục mẹ với:

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

Chúng sẽ hoạt động trên Bash 4.3-alpha hoặc mới hơn.


thật ngạc nhiên ? chỉ nói đùa, nhưng những người khác thì ngắn hơn và dễ nhớ hơn
viết mã

Điều này khá buồn cười = P Chưa từng nghe về $ IFS (dấu tách trường nội bộ) trước đó. bash của tôi đã quá cũ, nhưng có thể kiểm tra nó ở đây: jdoodle.com/test-bash-shell-script-online
Stan Kurdziel

5

Sử dụng:

basename "$PWD"

HOẶC LÀ

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

Biến Dấu tách tên tệp nội bộ (IFS) trở lại khoảng trắng.

IFS= 

Có một không gian sau IFS.


4
basename $(pwd)

hoặc là

echo "$(basename $(pwd))"

2
Cần thêm nhiều trích dẫn để chính xác - vì vậy, đầu ra của pwdchuỗi được phân tách và mở rộng toàn cầu trước khi được chuyển đến basename.
Charles Duffy

Do đó, basename "$(pwd)"- mặc dù điều đó rất kém hiệu quả so với chỉ basename "$PWD", bản thân nó không hiệu quả so với việc sử dụng mở rộng tham số thay vì gọi basenamecả.
Charles Duffy

4

Đối với những người tìm thấy ngoài kia như tôi:

find $PWD -maxdepth 0 -printf "%f\n"

Thay đổi $PWDđể "$PWD"xử lý chính xác tên thư mục bất thường.
Charles Duffy

3

tôi thường sử dụng điều này trong các kịch bản sh

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

bạn có thể sử dụng điều này để tự động hóa mọi thứ ...


readlink: illegal option -- f usage: readlink [-n] [file ...]

2

Dưới grep với regex cũng đang hoạt động,

>pwd | grep -o "\w*-*$"

3
Nó không hoạt động nếu tên thư mục chứa các ký tự "-".
daniula

1

Chỉ dùng:

pwd | xargs basename

hoặc là

basename "`pwd`"

2
Đây hoàn toàn là một phiên bản tồi tệ hơn của câu trả lời được đưa ra trước đây bởi Arkady ( stackoverflow.com/a/1371267/14122 ): Công xargsthức này không hiệu quả và có lỗi khi tên thư mục chứa dòng mới hoặc ký tự trích dẫn và công thức thứ hai gọi pwdlệnh trong một lớp con, thay vì truy xuất cùng một kết quả thông qua việc mở rộng biến tích hợp.
Charles Duffy

@CharlesDuffy Tuy nhiên, đây là một câu trả lời hợp lệ và thiết thực cho câu hỏi.
bachph


0

Bạn có thể sử dụng tiện ích tên cơ sở để xóa bất kỳ tiền tố kết thúc bằng / và hậu tố (nếu có trong chuỗi) khỏi chuỗi và in kết quả trên đầu ra tiêu chuẩn.

$basename <path-of-directory>

0

Tôi rất thích sử dụng gbasename, đó là một phần của GNU coreutils.


FYI, nó không đi kèm với bản phân phối Ubuntu của tôi.
Chuyến đi Katastic

@KatasticVoyage, nó có trên Ubuntu, nó chỉ được gọi basenameở đó. Nó thường chỉ được gọi gbasenametrên MacOS và các nền tảng khác có tên cơ sở không phải là GNU.
Charles Duffy

-1

Các lệnh sau sẽ dẫn đến việc in thư mục làm việc hiện tại của bạn trong tập lệnh bash.

pushd .
CURRENT_DIR="`cd $1; pwd`"
popd
echo $CURRENT_DIR

2
(1) Tôi không chắc chắn nơi chúng tôi đảm bảo rằng danh sách đối số là như vậy $1sẽ chứa thư mục làm việc hiện tại. (2) Việc phục vụ pushdpopdkhông có mục đích ở đây vì mọi thứ bên trong backticks đều được thực hiện trong một lớp con - vì vậy nó không thể ảnh hưởng đến thư mục của shell cha. (3) Sử dụng "$(cd "$1"; pwd)"sẽ dễ đọc và linh hoạt hơn đối với các tên thư mục có khoảng trắng.
Charles Duffy
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.