Câu trả lời:
basename
từ lõi GNU có thể giúp bạn thực hiện công việc này:
$ basename /root/video.mp4
video.mp4
Nếu bạn đã biết phần mở rộng của tệp, bạn có thể gọi basename
bằng cú pháp basename NAME [SUFFIX]
để xóa tệp:
$ basename /root/video.mp4 .mp4
video
Hoặc một tùy chọn khác sẽ cắt mọi thứ sau dấu chấm cuối bằng cách sử dụng sed
:
$ basename /root/video.old.mp4 | sed 's/\.[^.]*$//'
video.old
Sử dụng bất kỳ cách nào sau đây:
out_file="${in_file##*/}"
out_file="$(basename $in_file)"
out_file="$(echo $in_file | sed 's=.*/==')"
out_file="$(echo $in_file | awk -F"/" '{ print $NF }')"
ps. Bạn nhận được cùng một chuỗi bởi vì trong câu lệnh của bạn \(.*\.\)
khớp với chuỗi từ đầu cho đến khi dấu chấm ( /root/video.
) và sau đó bạn thêm thủ công .mp4
giống như trong chuỗi gốc của bạn. Bạn nên sử dụng s=.*\([^/]*\)=\1=
thay thế.
Cập nhật: (Cái đầu tiên đã được sửa bây giờ)
Để có được tên tệp duy nhất mà không cần gia hạn, bạn có thể:
out_file="$(echo $in_file | sed 's=.*/==;s/\.[^.]*$/.new_ext/')"
out_file="$(echo $in_file | sed 's=\([^/]*\)\.[^./]*$=\1.new_ext=')"
out_file="$(echo $in_file | awk -F"/" '{ gsub (/\.[^/.]*$/,".new_ext",$NF);print $NF }'
my.file.tar.gz
.
sed
và awk
. Đã sửa. Cảm ơn bạn.
Một trong những nguyên tắc cơ bản của việc sử dụng regex là các mẫu tự nhiên là tham lam khi chỉ định thẻ hoang dã. Mặc dù câu trả lời được đề xuất bởi @uloBasEI chắc chắn là một câu trả lời hoạt động, nó cũng yêu cầu sử dụng lệnh basename. Câu hỏi ban đầu từ @Shixons yêu cầu một giải pháp chỉ sử dụng sed.
Trước khi tiếp tục, thật hữu ích khi biết phiên bản sed nào là mục tiêu. Tôi đang giả sử BSD (như được vận chuyển với OSX).
Trước hết, mẫu được đề xuất trong câu hỏi ban đầu không hoạt động vì nó nắm bắt mọi thứ từ đầu chuỗi đầu vào cho đến và bao gồm cả dấu chấm cuối cùng. Không có neo, tìm kiếm này sẽ nuốt chửng mọi thứ từ trái sang phải. Do đó, mẫu phù hợp "/ 1" là mọi thứ lên đến và bao gồm cả dấu chấm cuối cùng. Ngay cả một tên tệp có nhiều dấu chấm sẽ bị nuốt chửng toàn bộ. Không phải là kết quả mong muốn ở tất cả.
Bước đầu tiên là thiết lập một chiến lược để xác định các mẫu. Tại đây, bạn muốn loại bỏ mọi thứ ở bên trái tên tệp (chúng ta sẽ giải quyết phần mở rộng sau):
out_file="$(echo $in_file | sed 's/^\(\/.*\/\)*.*/\1/')"
Tìm kiếm phù hợp từ đầu chuỗi. Nó khớp với mẫu "/.*" 0 hoặc nhiều lần và xóa mọi thứ sau đó. Chúng tôi in các mẫu phù hợp với "\ 1". Chúng tôi không tìm kiếm trên toàn cầu; chúng tôi đang tìm kiếm từ đầu chuỗi bằng cách chỉ định ^ neo.
Chúng tôi hiểu rõ hơn bằng cách bật tùy chọn "-E" để chúng tôi không phải thoát dấu ngoặc đơn:
out_file="$(echo $in_file | sed -E 's/^(\/.*\/)*.*/\1/')"
Vì vậy, bây giờ chúng ta có phần bên trái. Hãy thêm phần bên phải. Lưu ý rằng chúng ta cần giữ phần bên trái dưới dạng mẫu vì đó là cách chúng ta có thể chỉ định rằng phần đó xuất hiện 0 hoặc nhiều lần. Tất cả những gì chúng ta làm bây giờ là thêm một mẫu cho phần bên phải:
out_file="$(echo $in_file | sed -E 's/^(\/.*\/)*(.*)/\2/')"
Chúng tôi chỉ in ra trận đấu thứ hai, do đó loại bỏ mọi thứ trừ tên tệp. Nhưng chúng ta vẫn cần xóa phần mở rộng tên tệp.
out_file="$(echo $in_file | sed -E 's/^(\/.*\/)*(.*)\..*$/\2/')"
"$" Ở cuối là tùy chọn.
Cuối cùng, để thêm tiện ích mở rộng mới, bạn chỉ cần sửa lại như vậy:
out_file="$(echo $in_file | sed -E 's/^(\/.*\/)*(.*)\..*$/\2.mp4/')"
Một tối ưu hóa bổ sung là làm cho dấu gạch chéo chuyển tiếp đầu tiên tùy chọn để xử lý các đường dẫn tương đối:
out_file="$(echo $in_file | sed -E 's/^([\/]?.*\/)*(.*)\..*$/\2.mp4/')"
Tôi bắt gặp câu hỏi này bằng cách lười biếng trong khi tìm kiếm một mẫu sed để thay thế tên cơ sở . Tôi đang làm việc trên một hệ thống bị tước mà không cài đặt lệnh đó.
sed 's/\.[^.]*$//'
như bạn có, sẽ thất bại cho (ẩn).filename
và.
và..
thư mục