Làm cách nào để xóa phần mở rộng của tên tệp trong tập lệnh shell?


143

Có gì sai với đoạn mã sau?

name='$filename | cut -f1 -d'.''

Như là, tôi nhận được chuỗi ký tự $filename | cut -f1 -d'.', nhưng nếu tôi loại bỏ các trích dẫn, tôi không nhận được bất cứ điều gì. Trong khi đó, gõ

"test.exe" | cut -f1 -d'.'

trong một vỏ cho tôi đầu ra tôi muốn , test. Tôi đã biết $filenameđã được chỉ định đúng giá trị. Những gì tôi muốn làm là gán cho một biến tên tệp mà không có phần mở rộng.


6
basename $filename .exesẽ làm điều tương tự. Đó là giả sử bạn luôn biết phần mở rộng nào bạn muốn xóa.
mpe

6
@mpe, ý bạn là thế basename "$filename" .exe. Nếu không tên tập tin với không gian sẽ là tin xấu.
Charles Duffy

Câu trả lời:


113

Bạn nên sử dụng cú pháp thay thế lệnh$(command) khi bạn muốn thực thi một lệnh trong script / lệnh.

Vì vậy, dòng của bạn sẽ là

name=$(echo "$filename" | cut -f 1 -d '.')

Mã giải thích:

  1. echolấy giá trị của biến $filenamevà gửi nó đến đầu ra tiêu chuẩn
  2. Sau đó chúng tôi lấy đầu ra và đưa nó vào cutlệnh
  3. Các cutsẽ sử dụng. như dấu phân cách (còn được gọi là dấu phân cách) để cắt chuỗi thành các phân đoạn và bằng cách -fchúng tôi chọn phân đoạn nào chúng tôi muốn có đầu ra
  4. Sau đó, $()thay thế lệnh sẽ nhận được đầu ra và trả về giá trị của nó
  5. Giá trị trả về sẽ được gán cho biến có tên name

Lưu ý rằng điều này cung cấp cho phần của biến cho đến giai đoạn đầu tiên .:

$ filename=hello.world
$ echo "$filename" | cut -f 1 -d '.'
hello
$ filename=hello.hello.hello
$ echo "$filename" | cut -f 1 -d '.'
hello
$ filename=hello
$ echo "$filename" | cut -f 1 -d '.'
hello

Cảm ơn. Tôi cũng nhận thấy rằng tôi cần sử dụng lệnh echo. tên =echo $filename | cut -f1 -d'.'
mimicocotopus

17
Backticks không được chấp nhận bởi POSIX, $()được ưu tiên.
jordanm

3
Ngã ba và đường ống để có được một vài ký tự là về giải pháp tồi tệ nhất có thể tưởng tượng.
Jens

39
Vấn đề với câu trả lời này là nó giả sử chuỗi đầu vào CHỈ có một dấu chấm ... @chepner bên dưới có giải pháp tốt hơn nhiều ... name = $ {tên tệp%. *}
Scott Stensland

4
Câu trả lời này là đặc trưng của người mới bắt đầu và không nên được lan truyền. Sử dụng cơ chế dựng sẵn như được mô tả bởi câu trả lời của
chepner

256

Bạn cũng có thể sử dụng mở rộng tham số:

$ filename=foo.txt
$ echo "${filename%.*}"
foo

6
đây là lời giải thích của lệnh: gnu.org/software/bash/manual/html_node/ Kẻ
Carlos Robles

1
Và ở đây tôi đã sử dụng echo -n "This.File.Has.Periods.In.It.txt" | awk -F. '{$NF=""; print $0}' | tr ' ' '.' | rev | cut -c 2- | rev. Cảm ơn.
dùng208145

1
Điều này có hoạt động với các tập tin với nhiều phần mở rộng như thế image.png.gznào không?
Hawker65

11
%.*sẽ chỉ xóa phần mở rộng cuối cùng; nếu bạn muốn loại bỏ tất cả các phần mở rộng, sử dụng %%.*.
chepner

1
Điều này dường như làm việc tốt hơn so với câu trả lời được chấp nhận. Nó chỉ tắt phần mở rộng cuối cùng nếu tệp có nhiều hơn một dấu chấm, trong khi cắt sẽ loại bỏ mọi thứ sau dấu chấm đầu tiên.
Dale Anderson


20

Nếu tên tệp của bạn chứa một dấu chấm (không phải là một trong các phần mở rộng) thì hãy sử dụng:

echo $filename | rev | cut -f 2- -d '.' | rev

1
Tôi đã quên vòng quay giữa, nhưng một khi tôi đã nhìn thấy nó, điều này thật tuyệt!
Pooba tối cao

Nó thậm chí còn tốt hơn với một -stùy chọn được đưa ra cut, để nó trả về một chuỗi trống bất cứ khi nào tên tệp không có dấu chấm.
Hibou57

1
Đây phải là câu trả lời được chấp nhận theo ý kiến ​​của tôi, vì nó hoạt động trên đường dẫn có dấu chấm trong đó, với các tệp ẩn bắt đầu bằng dấu chấm hoặc thậm chí với tệp có nhiều tiện ích mở rộng.
Tim Krief

20
file1=/tmp/main.one.two.sh
t=$(basename "$file1")                        # output is main.one.two.sh
name=$(echo "$file1" | sed -e 's/\.[^.]*$//') # output is /tmp/main.one.two
name=$(echo "$t" | sed -e 's/\.[^.]*$//')     # output is main.one.two

sử dụng bất cứ thứ gì bạn muốn Ở đây tôi giả sử rằng cuối cùng .(dấu chấm) theo sau là văn bản là phần mở rộng.


6
#!/bin/bash
file=/tmp/foo.bar.gz
echo $file ${file%.*}

đầu ra:

/tmp/foo.bar.gz /tmp/foo.bar

Lưu ý rằng chỉ có phần mở rộng cuối cùng được gỡ bỏ.


3

Đề nghị của tôi là sử dụng basename.
Theo mặc định, nó là Ubuntu, mã đơn giản trực quan và giải quyết phần lớn các trường hợp.

Dưới đây là một số trường hợp phụ để xử lý khoảng trắng và nhiều dấu chấm / phần mở rộng phụ:

pathfile="../space fld/space -file.tar.gz"
echo ${pathfile//+(*\/|.*)}

Nó thường được loại bỏ phần mở rộng từ đầu tiên ., nhưng thất bại trong ..con đường của chúng tôi

echo **"$(basename "${pathfile%.*}")"**  
space -file.tar     # I believe we needed exatly that

Đây là một lưu ý quan trọng:

Tôi đã sử dụng dấu ngoặc kép bên trong dấu ngoặc kép để xử lý khoảng trắng. Một trích dẫn sẽ không vượt qua do nhắn tin cho $. Bash là không bình thường và đọc "trích dẫn" thứ hai "đầu tiên" do mở rộng.

Tuy nhiên, bạn vẫn cần phải nghĩ đến .hidden_files

hidden="~/.bashrc"
echo "$(basename "${hidden%.*}")"  # will produce "~" !!!  

không phải là kết quả "" mong đợi. Để làm cho nó xảy ra sử dụng $HOMEhoặc /home/user_path/
vì một lần nữa bash là "không bình thường" và không mở rộng "~" (tìm kiếm bash BashPit thác)

hidden2="$HOME/.bashrc" ;  echo '$(basename "${pathfile%.*}")'

1
#!/bin/bash
filename=program.c
name=$(basename "$filename" .c)
echo "$name"

đầu ra:

program

Điều này khác với câu trả lời của Steven Penny 3 năm trước như thế nào?
gniourf_gniourf

1

Như Hawker65 đã chỉ ra trong phần bình luận của câu trả lời của người cho vay, giải pháp được bình chọn nhiều nhất không quan tâm đến nhiều phần mở rộng (như filename.tar.gz), cũng như các dấu chấm trong phần còn lại của đường dẫn (chẳng hạn như this.path / with .dots / in.path.name). Một giải pháp khả thi là:

a=this.path/with.dots/in.path.name/filename.tar.gz
echo $(dirname $a)/$(basename $a | cut -d. -f1)

Điều này loại bỏ "tar.gz" bằng cách chọn các ký tự trước phiên bản đầu tiên của một dấu chấm trong tên tệp không tính đường dẫn. Một người có lẽ không muốn tước phần mở rộng theo cách đó.
Frotz

0

Hai vấn đề với mã của bạn:

  1. Bạn đã sử dụng một '(đánh dấu) thay vì một `(đánh dấu lại) để bao quanh các lệnh tạo ra chuỗi bạn muốn lưu trữ trong biến.
  2. Bạn đã không "lặp lại" biến "$ filename" cho đường ống thành lệnh "cắt".

Tôi sẽ thay đổi mã của bạn thành "name =` echo $ filename | cut -f 1 -d '.' `", như được hiển thị bên dưới (một lần nữa, chú ý các dấu tick phía sau xung quanh định nghĩa biến tên):

$> filename=foo.txt
$> echo $filename
foo.txt
$> name=`echo $filename | cut -f1 -d'.'`
$> echo $name
foo
$> 
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.