Đưa ra một đường dẫn tệp chuỗi như /foo/fizzbuzz.bar
, làm thế nào tôi có thể sử dụng bash để trích xuất chỉ một fizzbuzz
phần của chuỗi đã nói?
Đưa ra một đường dẫn tệp chuỗi như /foo/fizzbuzz.bar
, làm thế nào tôi có thể sử dụng bash để trích xuất chỉ một fizzbuzz
phần của chuỗi đã nói?
Câu trả lời:
Đây là cách thực hiện với các toán tử # và% trong Bash.
$ x="/foo/fizzbuzz.bar"
$ y=${x%.bar}
$ echo ${y##*/}
fizzbuzz
${x%.bar}
cũng có thể ${x%.*}
xóa mọi thứ sau một dấu chấm hoặc ${x%%.*}
xóa mọi thứ sau dấu chấm đầu tiên.
Thí dụ:
$ x="/foo/fizzbuzz.bar.quux"
$ y=${x%.*}
$ echo $y
/foo/fizzbuzz.bar
$ y=${x%%.*}
$ echo $y
/foo/fizzbuzz
Tài liệu có thể được tìm thấy trong hướng dẫn Bash . Tìm kiếm ${parameter%word}
và ${parameter%%word}
phần phù hợp với phần.
nhìn vào lệnh basename:
NAME="$(basename /foo/fizzbuzz.bar .bar)"
Sử dụng tên cơ sở tôi đã sử dụng như sau để đạt được điều này:
for file in *; do
ext=${file##*.}
fname=`basename $file $ext`
# Do things with $fname
done;
Điều này không đòi hỏi kiến thức chuyên môn về phần mở rộng tệp và hoạt động ngay cả khi bạn có tên tệp có dấu chấm trong tên tệp của nó (phía trước phần mở rộng của nó); basename
mặc dù nó không yêu cầu chương trình , nhưng đây là một phần của lõi GNU nên nó sẽ được gửi cùng với bất kỳ bản phân phối nào.
fname=`basename $file .$ext`
Cách bash tinh khiết:
~$ x="/foo/bar/fizzbuzz.bar.quux.zoom";
~$ y=${x/\/*\//};
~$ echo ${y/.*/};
fizzbuzz
Chức năng này được giải thích trên man bash trong phần "Mở rộng tham số". Non bash cách rất nhiều: awk, perl, sed và như vậy.
EDIT: Hoạt động với các dấu chấm trong hậu tố tập tin và không cần biết hậu tố (phần mở rộng), nhưng không hoạt động với các dấu chấm trong chính tên .
Các hàm basename và dirname là những gì bạn đang theo đuổi:
mystring=/foo/fizzbuzz.bar
echo basename: $(basename "${mystring}")
echo basename + remove .bar: $(basename "${mystring}" .bar)
echo dirname: $(dirname "${mystring}")
Có đầu ra:
basename: fizzbuzz.bar
basename + remove .bar: fizzbuzz
dirname: /foo
mystring=$1
chứ không phải là giá trị không đổi hiện tại (mà sẽ ngăn chặn một vài cảnh báo, là nhất định không chứa dấu cách / nhân vật glob / etc), và địa chỉ các vấn đề đó tìm thấy?
echo "basename: $(basename "$mystring")"
- theo cách đó nếu mystring='/foo/*'
bạn không được *
thay thế bằng danh sách các tệp trong thư mục hiện tại sau khi basename
kết thúc.
Sử dụng basename
giả định rằng bạn biết phần mở rộng tập tin là gì, phải không?
Và tôi tin rằng các đề xuất biểu thức chính quy khác nhau không đối phó với tên tệp chứa nhiều hơn một "."
Sau đây dường như để đối phó với dấu chấm đôi. Ồ, và tên tệp có chứa "/" (chỉ dành cho cú đá)
Để diễn giải Pascal, "Xin lỗi kịch bản này quá dài. Tôi không có thời gian để làm cho nó ngắn hơn"
#!/usr/bin/perl
$fullname = $ARGV[0];
($path,$name) = $fullname =~ /^(.*[^\\]\/)*(.*)$/;
($basename,$extension) = $name =~ /^(.*)(\.[^.]*)$/;
print $basename . "\n";
Ngoài cú pháp tuân thủ POSIX được sử dụng trong câu trả lời này ,
basename string [suffix]
như trong
basename /foo/fizzbuzz.bar .bar
GNUbasename
hỗ trợ cú pháp khác:
basename -s .bar /foo/fizzbuzz.bar
với kết quả tương tự. Sự khác biệt và lợi thế là -s
ngụ ý -a
, hỗ trợ nhiều đối số:
$ basename -s .bar /foo/fizzbuzz.bar /baz/foobar.bar
fizzbuzz
foobar
Điều này thậm chí có thể được tạo tên tệp an toàn bằng cách tách đầu ra với các byte NUL bằng cách sử dụng -z
tùy chọn, ví dụ: đối với các tệp này chứa khoảng trắng, dòng mới và ký tự toàn cầu (được trích dẫn bởi ls
):
$ ls has*
'has'$'\n''newline.bar' 'has space.bar' 'has*.bar'
Đọc thành một mảng:
$ readarray -d $'\0' arr < <(basename -zs .bar has*)
$ declare -p arr
declare -a arr=([0]=$'has\nnewline' [1]="has space" [2]="has*")
readarray -d
yêu cầu Bash 4.4 hoặc mới hơn. Đối với các phiên bản cũ hơn, chúng tôi phải lặp:
while IFS= read -r -d '' fname; do arr+=("$fname"); done < <(basename -zs .bar has*)
Nếu bạn không thể sử dụng tên cơ sở như được đề xuất trong các bài đăng khác, bạn luôn có thể sử dụng sed. Đây là một ví dụ (xấu xí). Nó không phải là tốt nhất, nhưng nó hoạt động bằng cách trích xuất chuỗi mong muốn và thay thế đầu vào bằng chuỗi mong muốn.
echo '/foo/fizzbuzz.bar' | sed 's|.*\/\([^\.]*\)\(\..*\)$|\1|g'
Điều này sẽ giúp bạn có đầu ra
fizzbuzz
Cẩn thận với giải pháp perl được đề xuất: nó loại bỏ bất cứ thứ gì sau dấu chấm đầu tiên.
$ echo some.file.with.dots | perl -pe 's/\..*$//;s{^.*/}{}'
some
Nếu bạn muốn làm điều đó với perl, điều này hoạt động:
$ echo some.file.with.dots | perl -pe 's/(.*)\..*$/$1/;s{^.*/}{}'
some.file.with
Nhưng nếu bạn đang sử dụng Bash, các giải pháp với y=${x%.*}
(hoặc basename "$x" .ext
nếu bạn biết phần mở rộng) đơn giản hơn nhiều.
Kết hợp câu trả lời được xếp hạng cao nhất với câu trả lời được xếp hạng thứ hai để có được tên tệp mà không có đường dẫn đầy đủ:
$ x="/foo/fizzbuzz.bar.quux"
$ y=(`basename ${x%%.*}`)
$ echo $y
fizzbuzz
${parameter%word}
và${parameter%%word}
trong phần khớp phần.