Câu hỏi Trích xuất tên tệp và phần mở rộng trong Bash


Tôi muốn lấy tên tệp (không có phần mở rộng) và phần mở rộng riêng biệt.

Giải pháp tốt nhất mà tôi tìm thấy cho đến nay là:

NAME=`echo "$FILE" | cut -d'.' -f1`
EXTENSION=`echo "$FILE" | cut -d'.' -f2`

Điều này là sai vì nó không hoạt động nếu tên tệp chứa nhiều . nhân vật. Nếu, giả sử, tôi có a.b.js, nó sẽ xem xét a và b.js, thay vì a.b và js.

Nó có thể được thực hiện dễ dàng bằng Python với

file, ext = os.path.splitext(path)

nhưng tôi không muốn kích hoạt trình thông dịch Python chỉ vì điều này, nếu có thể.

Bất kỳ ý tưởng tốt hơn?


1620
2018-06-08 14:00


gốc


Câu hỏi này giải thích kỹ thuật bash này và một số kỹ thuật khác có liên quan. - jjclarkson
Khi áp dụng các câu trả lời tuyệt vời bên dưới, không chỉ cần dán biến của bạn như tôi hiển thị ở đây Sai rồi:  extension="{$filename##*.}" như tôi đã làm một lúc! Di chuyển $ bên ngoài các quăn: Đúng:  extension="${filename##*.}" - Chris K
Điều này rõ ràng là một vấn đề không tầm thường và đối với tôi rất khó để nói nếu các câu trả lời dưới đây là hoàn toàn chính xác. Thật tuyệt vời đây không phải là một hoạt động được xây dựng trong (ba) sh (câu trả lời dường như thực hiện các chức năng bằng cách sử dụng phù hợp với mô hình). Tôi quyết định sử dụng Python os.path.splitext như trên thay vào đó ... - Peter Gibson
Đồng ý - cùng một bình luận. Không có tiện ích biên dịch / có thể được biên dịch có sẵn mà có chuyển mạch dòng lệnh cho vô số trường hợp sử dụng này - ví dụ: nhận tiện ích mở rộng thay thế hoặc n lần cuối. delimiters hoặc kiểm tra loại tệp trong tiêu đề nhị phân, v.v. .? - Alex Hall
Như sự mở rộng phải đại diện Thiên nhiên của một tệp, có một ma thuật lệnh kiểm tra tập tin để thần thánh bản chất của mình và cung cấp tiện ích chuẩn. xem câu trả lời của tôi - F. Hauri


Các câu trả lời:


Đầu tiên, lấy tên tệp mà không có đường dẫn:

filename=$(basename -- "$fullfile")
extension="${filename##*.}"
filename="${filename%.*}"

Ngoài ra, bạn có thể tập trung vào '/' cuối cùng của đường dẫn thay vì '.' sẽ hoạt động ngay cả khi bạn có các phần mở rộng tệp không thể đoán trước:

filename="${fullfile##*/}"

2815
2018-06-08 14:05



Kiểm tra gnu.org/software/bash/manual/html_node/… cho toàn bộ tính năng. - D.Shawley
Thêm một số dấu ngoặc kép vào "$ fullfile" hoặc bạn sẽ có nguy cơ phá vỡ tên tệp. - lhunath
Heck, bạn thậm chí có thể viết tên tệp = "$ {fullfile ## * /}" và tránh gọi thêm basename - ephemient
"Giải pháp" này không hoạt động nếu tệp không có phần mở rộng - thay vào đó, toàn bộ tên tệp là đầu ra, điều này khá tệ khi xem xét các tệp không có tiện ích mở rộng có mặt khắp nơi. - nccc
Khắc phục để xử lý các tên tệp mà không có phần mở rộng: extension=$([[ "$filename" = *.* ]] && echo ".${filename##*.}" || echo ''). Lưu ý rằng nếu tiện ích mở rộng Là hiện tại, nó sẽ được trả lại bao gồm cả ban đầu ., ví dụ., .txt. - mklement0


~% FILE="example.tar.gz"
~% echo "${FILE%%.*}"
example
~% echo "${FILE%.*}"
example.tar
~% echo "${FILE#*.}"
tar.gz
~% echo "${FILE##*.}"
gz

Để biết thêm chi tiết, hãy xem mở rộng tham số vỏ trong sổ tay Bash.


503
2018-06-08 14:05



Bạn (có lẽ vô tình) đưa ra câu hỏi tuyệt vời về những gì cần làm nếu phần mở rộng của tên tệp có 2 dấu chấm trong đó, như trong .tar.gz ... Tôi chưa bao giờ xem vấn đề đó và tôi nghi ngờ nó không thể giải quyết mà không biết tất cả các phần mở rộng tệp hợp lệ có thể có ở phía trước. - rmeador
Tại sao không giải được? Trong ví dụ của tôi, cần xem xét rằng tệp chứa hai phần mở rộng, không phải là phần mở rộng có hai dấu chấm. Bạn xử lý riêng lẻ cả hai tiện ích mở rộng. - Juliano
Nó là unsolvable trên cơ sở từ vựng, bạn sẽ cần phải kiểm tra loại tập tin. Xem xét nếu bạn có trò chơi có tên dinosaurs.in.tar và bạn gzipped nó để dinosaurs.in.tar.gz :) - porges
Điều này trở nên phức tạp hơn nếu bạn đang đi qua đường dẫn đầy đủ. Một trong số tôi có một '.' trong một thư mục ở giữa đường dẫn, nhưng không có thư mục nào trong tên tệp. Ví dụ "a / b.c / d / e / filename" sẽ bị ".c / d / e / filename" - Walt Sellers
rõ ràng là không x.tar.gzphần mở rộng của là gz và tên tệp là x.tar đó là nó. Không có những thứ như mở rộng kép. Tôi khá chắc chắn tăng :: hệ thống tập tin xử lý nó theo cách đó. (split path, change_extension ...) và hành vi của nó dựa trên python nếu tôi không nhầm. - v.oddou


Thông thường bạn đã biết phần mở rộng, vì vậy bạn có thể muốn sử dụng:

basename filename .extension

ví dụ:

basename /path/to/dir/filename.txt .txt

và chúng tôi nhận được

filename

281
2017-10-19 10:56



Lập luận thứ hai đó basename là khá mở mắt, ty loại sir / madam :) - akaIDIOT
Và làm thế nào để trích xuất phần mở rộng, sử dụng kỹ thuật này? ;) Oh, chờ đã! Chúng tôi thực sự không biết trước. - Tomasz Gandor
Giả sử bạn có thư mục được nén mà kết thúc bằng .zip hoặc là .ZIP. Có cách nào bạn có thể làm một cái gì đó như basename $file {.zip,.ZIP}? - Dennis
Trong khi điều này chỉ trả lời một phần câu hỏi của OP, nó trả lời câu hỏi tôi đã gõ vào google. :-) Rất trơn! - sudo make install


Bạn có thể sử dụng phép thuật của biến POSIX:

bash-3.2$ FILENAME=somefile.tar.gz
bash-3.2$ echo ${FILENAME%%.*}
somefile
bash-3.2$ echo ${FILENAME%.*}
somefile.tar

Có một thông báo trước rằng nếu tên tệp của bạn có dạng ./somefile.tar.gz sau đó echo ${FILENAME%%.*} sẽ tham lam loại bỏ trận đấu dài nhất với . và bạn sẽ có chuỗi rỗng.

(Bạn có thể làm việc xung quanh đó với một biến tạm thời:

FULL_FILENAME=$FILENAME
FILENAME=${FULL_FILENAME##*/}
echo ${FILENAME%%.*}

)


Điều này trang web giải thích thêm.

${variable%pattern}
  Trim the shortest match from the end
${variable##pattern}
  Trim the longest match from the beginning
${variable%%pattern}
  Trim the longest match from the end
${variable#pattern}
  Trim the shortest match from the beginning

125
2018-02-05 09:09



Đơn giản hơn nhiều so với câu trả lời của Joachim nhưng tôi luôn phải tra cứu thay thế biến POSIX. Ngoài ra, điều này chạy trên Max OSX nơi cut không có --complement và sed không có -r. - jwadsack


Điều đó dường như không hoạt động nếu tệp không có phần mở rộng hoặc không có tên tệp. Đây là những gì tôi đang sử dụng; nó chỉ sử dụng nội trang và xử lý nhiều hơn (nhưng không phải tất cả) tên tập tin bệnh lý.

#!/bin/bash
for fullpath in "$@"
do
    filename="${fullpath##*/}"                      # Strip longest match of */ from start
    dir="${fullpath:0:${#fullpath} - ${#filename}}" # Substring from 0 thru pos of filename
    base="${filename%.[^.]*}"                       # Strip shortest match of . plus at least one non-dot char from end
    ext="${filename:${#base} + 1}"                  # Substring from len of base thru end
    if [[ -z "$base" && -n "$ext" ]]; then          # If we have an extension and no base, it's really the base
        base=".$ext"
        ext=""
    fi

    echo -e "$fullpath:\n\tdir  = \"$dir\"\n\tbase = \"$base\"\n\text  = \"$ext\""
done

Và đây là một số testcases:

$ basename-and-extension.sh / / home / me / / home / me / file /home/me/file.tar/home/me/file.tar.gz/home/me/.hidden / home / me / .hidden.tar / home / me / ...
/:
    dir = "/"
    base = ""
    ext = ""
/ home / me /:
    dir = "/ home / me /"
    base = ""
    ext = ""
/ home / me / file:
    dir = "/ home / me /"
    base = "tệp"
    ext = ""
/home/me/file.tar:
    dir = "/ home / me /"
    base = "tệp"
    ext = "tar"
/home/me/file.tar.gz:
    dir = "/ home / me /"
    base = "file.tar"
    ext = "gz"
/home/me/.hidden:
    dir = "/ home / me /"
    base = ".hidden"
    ext = ""
/home/me/.hidden.tar:
    dir = "/ home / me /"
    base = ".hidden"
    ext = "tar"
/ home / me / ..:
    dir = "/ home / me /"
    base = ".."
    ext = ""
.:
    dir = ""
    base = "."
    ext = ""

65
2017-09-10 05:17



Thay vì dir="${fullpath:0:${#fullpath} - ${#filename}}" Tôi thường thấy dir="${fullpath%$filename}". Nó đơn giản hơn để viết. Không chắc chắn nếu có bất kỳ sự khác biệt tốc độ thực sự hoặc gotchas. - dubiousjim
Điều này sử dụng #! / Bin / bash gần như luôn luôn sai. Ưu tiên #! / Bin / sh nếu có thể hoặc #! / Usr / bin / env bash nếu không. - Good Person
@ Người tốt: Tôi không biết làm thế nào nó hầu như luôn luôn sai: which bash -> /bin/bash ; có lẽ đó là bản phân phối của bạn? - vol7ron
@ vol7ron - trên nhiều bash distro là / usr / local / bin / bash. Trên OSX, nhiều người cài đặt một bash cập nhật trong / opt / local / bin / bash. Như vậy / bin / bash là sai và nên sử dụng env để tìm nó. Thậm chí tốt hơn là sử dụng các cấu trúc / bin / sh và POSIX. Ngoại trừ trên solaris đây là một vỏ POSIX. - Good Person
@ GoodPerson nhưng nếu bạn cảm thấy thoải mái hơn với bash, tại sao lại sử dụng sh? Không phải là như vậy, tại sao sử dụng Perl khi bạn có thể sử dụng sh? - vol7ron


Bạn có thể dùng basename.

Thí dụ:

$ basename foo-bar.tar.gz .tar.gz
foo-bar

Bạn cần cung cấp tên cơ sở với phần mở rộng sẽ được loại bỏ, tuy nhiên nếu bạn luôn thực hiện tar với -z thì bạn biết phần mở rộng sẽ là .tar.gz.

Điều này sẽ làm những gì bạn muốn:

tar -zxvf $1
cd $(basename $1 .tar.gz)

40
2018-02-05 08:50



tôi giả sử cd $(basename $1 .tar.gz) hoạt động cho các tệp .gz. Nhưng trong câu hỏi ông đã đề cập Archive files have several extensions: tar.gz, tat.xz, tar.bz2 - SS Hegde
Tomi Po đăng những thứ tương tự 2 năm trước. - Blauhirn
Xin chào Blauhirn, đây là một câu hỏi cũ. Tôi nghĩ có điều gì đó đã xảy ra với những ngày tháng. Tôi đặc biệt nhớ trả lời câu hỏi ngay sau khi được hỏi, và ở đó chỉ có một vài câu trả lời khác. Có thể là câu hỏi đã được sáp nhập với một câu hỏi khác, SO có làm điều đó không? - Bjarke Freund-Hansen
Đúng, tôi nhớ chính xác. Ban đầu tôi trả lời câu hỏi này stackoverflow.com/questions/14703318/… trong cùng một ngày nó được hỏi, 2 năm sau nó được sáp nhập vào cái này. Tôi khó có thể đổ lỗi cho một câu trả lời trùng lặp khi câu trả lời của tôi đã được di chuyển theo cách này. - Bjarke Freund-Hansen


Mellen viết trong một bình luận trên một bài đăng trên blog:

Sử dụng Bash, cũng có ${file%.*} để lấy tên tệp mà không có phần mở rộng và ${file##*.} để có được phần mở rộng một mình. Đó là,

file="thisfile.txt"
echo "filename: ${file%.*}"
echo "extension: ${file##*.}"

Đầu ra:

filename: thisfile
extension: txt

24
2017-07-21 10:24



Cú pháp này hoạt động như thế nào? - syntagma
@REACHUS: Xem gnu.org/software/bash/manual/html_node/… - mklement0