2010-07-29 40 views

Trả lời

461

Hầu hết các tựa UNIX có một thực thi basename cho một mục đích rất giống nhau (và dirname cho đường dẫn):

pax> a=/tmp/file.txt 
pax> b=$(basename $a) 
pax> echo $b 
file.txt 

đó không may chỉ mang đến cho bạn tên tập tin, bao gồm phần mở rộng, vì vậy bạn sẽ cần phải tìm một cách để tước mà off là tốt.

Vì vậy, nếu bạn vẫn phải làm điều đó, bạn cũng có thể tìm phương thức có thể tách ra khỏi đường dẫn tiện ích.

Một cách để làm điều đó (và điều này là một giải pháp -only bash, cần không có thực thi khác):

pax> a=/tmp/xx/file.tar.gz 
pax> xpath=${a%/*} 
pax> xbase=${a##*/} 
pax> xfext=${xbase##*.} 
pax> xpref=${xbase%.*} 
pax> echo;echo path=${xpath};echo pref=${xpref};echo ext=${xfext} 

path=/tmp/xx 
pref=file.tar 
ext=gz 

Đó chút đoạn đặt xpath (đường dẫn file), xpref (file prefix, những gì bạn đã yêu cầu cụ thể) và xfext (đuôi tệp).

+0

Tôi biết có điều gì đó cần làm với bash như trên. Tôi chỉ không biết từ khóa là gì. Tôi muốn lấy tên đường dẫn, tên tệp và phần mở rộng được tách thành các biến khác nhau. – Keith

+0

Nếu bạn muốn sử dụng đường dẫn: đường dẫn = $ (echo $ filename | sed -e 's/\/[^ \ /] * $/\ //') Nếu bạn muốn gia hạn: ext = $ (echo $ filename | sed -e 's /[^\.]*\.//') – jcubic

+3

@Keith: cho tên đường dẫn, sử dụng 'path = $ (dirname $ filename)'; không có một lệnh để cung cấp cho bạn phần mở rộng cho mỗi se, nhưng @paxdiablo chỉ cho bạn cách trình bao có thể thực hiện nó. –

10
$ source_file_filename_no_ext=${source_file%.*} 
$ echo ${source_file_filename_no_ext##*/} 
+0

Tôi chỉ nhận được ký tự đại diện với phần mở rộng bằng cách sử dụng này. – Kebman

18

Dưới đây là một cách dễ dàng để có được tên tập tin từ một đường dẫn:

echo "$PATH" | rev | cut -d"/" -f1 | rev 

Để loại bỏ phần mở rộng bạn có thể sử dụng, giả sử tên tập tin chỉ có ONE dấu chấm (dot mở rộng):

cut -d"." -f1 
+7

Đó không phải là một giả định tốt, và có các công cụ và lệnh được thiết kế cụ thể để làm điều này đúng cách. – Tony

+0

Ngoài ra, tôi sẽ không khuyên bạn nên sử dụng tên biến 'PATH', vì điều này có thể xung đột với biến' PATH' của hệ thống – Tabea

19

basenamedirname giải pháp thuận tiện hơn. Đó là các lệnh thay thế:

FILE_PATH="/opt/datastores/sda2/test.old.img" 
echo "$FILE_PATH" | sed "s/.*\///" 

Điều này trả về test.old.img như basename.

Đây là filename muối mà không mở rộng:

echo "$FILE_PATH" | sed -r "s/.+\/(.+)\..+/\1/" 

Nó trả test.old.

Và câu sau đây cung cấp đường dẫn đầy đủ như dirname lệnh.

echo "$FILE_PATH" | sed -r "s/(.+)\/.+/\1/" 

Nó trả /opt/datastores/sda2

+0

, nếu có thông số thì sao? –

6

Một số tùy chọn thay thế hơn vì regexes (Regi?) Là tuyệt vời!

Đây là một regex đơn giản để thực hiện công việc:

regex="[^/]*$" 

Ví dụ (grep):

FP="/hello/world/my/file/path/hello_my_filename.log" 
echo $FP | grep -oP "$regex" 
#Or using standard input 
grep -oP "$regex" <<< $FP 

Ví dụ (awk):

echo $FP | awk '{match($1, "$regex",a)}END{print a[0]} 
#Or using stardard input 
awk '{match($1, "$regex",a)}END{print a[0]} <<< $FP 

Nếu bạn cần một regex phức tạp hơn: Ví dụ: đường dẫn của bạn được bao bọc trong một chuỗi.

StrFP="my string is awesome file: /hello/world/my/file/path/hello_my_filename.log sweet path bro." 

#this regex matches a string not containing/and ends with a period 
#then at least one word character 
#so its useful if you have an extension 

regex="[^/]*\.\w{1,}" 

#usage 
grep -oP "$regex" <<< $StrFP 

#alternatively you can get a little more complicated and use lookarounds 
#this regex matches a part of a string that starts with/that does not contain a/
##then uses the lazy operator ? to match any character at any amount (as little as possible hence the lazy) 
##that is followed by a space 
##this allows use to match just a file name in a string with a file path if it has an exntension or not 
##also if the path doesnt have file it will match the last directory in the file path 
##however this will break if the file path has a space in it. 

regex="(?<=/)[^/]*?(?=\s)" 

#to fix the above problem you can use sed to remove spaces from the file path only 
## as a side note unfortunately sed has limited regex capibility and it must be written out in long hand. 
NewStrFP=$(echo $StrFP | sed 's:\(/[a-z]*\)\(\)\([a-z]*/\):\1\3:g') 
grep -oP "$regex" <<< $NewStrFP 

Giải pháp tổng thể với Regexes:

Chức năng này có thể cung cấp cho bạn tên tập tin có hoặc không có phần mở rộng của một filepath linux thậm chí nếu tên tập tin có nhiều s trong nó "". Nó cũng có thể xử lý không gian trong filepath và nếu đường dẫn tập tin được nhúng hoặc bọc trong một chuỗi.

#you may notice that the sed replace has gotten really crazy looking 
#I just added all of the allowed characters in a linux file path 
function Get-FileName(){ 
    local FileString="$1" 
    local NoExtension="$2" 
    local FileString=$(echo $FileString | sed 's:\(/[a-zA-Z0-9\<\>\|\\\:\)\(\&\;\,\?\*]*\)\(\)\([a-zA-Z0-9\<\>\|\\\:\)\(\&\;\,\?\*]*/\):\1\3:g') 

    local regex="(?<=/)[^/]*?(?=\s)" 

    local FileName=$(echo $FileString | grep -oP "$regex") 

    if [[ "$NoExtension" != "" ]]; then 
     sed 's:\.[^\.]*$::g' <<< $FileName 
    else 
     echo "$FileName" 
    fi 
} 

## call the function with extension 
Get-FileName "my string is awesome file: /hel lo/world/my/file test/path/hello_my_filename.log sweet path bro." 

##call function without extension 
Get-FileName "my string is awesome file: /hel lo/world/my/file test/path/hello_my_filename.log sweet path bro." "1" 

Nếu bạn phải gây rối với một con đường cửa sổ, bạn có thể bắt đầu với một này:

[^\\]*$  
5
$ file=${$(basename $file_path)%.*} 
+0

Điều này trả về "thay thế xấu" trong bash v4.4.7. Tôi nghĩ rằng giải pháp sed của Fırat KÜÇÜK tốt hơn, tức là $ (basename $ the_file_path) | sed "s /\..*//" – Marshal

+0

Tôi có nghĩa là 'echo $ (basename $ the_file_path) | sed "s /\..*//" ' – Marshal

Các vấn đề liên quan