'ls -1': cách liệt kê tên tệp mà không cần gia hạn


24

ls -1 liệt kê các yếu tố của tôi như vậy:

foo.png
bar.png
foobar.png
...

Tôi muốn nó được liệt kê mà không .pngnhư vậy:

foo
bar
foobar
...

(thư mục chỉ chứa .pngtệp)

Ai đó có thể cho tôi biết làm thế nào để sử dụng greptrong trường hợp này?

Mục đích: Tôi có một tệp văn bản trong đó tất cả các tên được liệt kê mà không có phần mở rộng. Tôi muốn tạo một tập lệnh so sánh tệp văn bản với thư mục để xem tệp nào bị thiếu.


36
Bạn muốn cẩn thận với một yêu cầu như thế này. Linux không có phần mở rộng tên tệp. Linux có tên tệp có thể có hoặc không có .trong đó. Mặc dù Công ước nói đến tên tập tin của bạn với .pnglúc kết thúc, không có lý do tại sao tôi không thể có một tập tin png tên foo.ziphoặc my.picture.20160518hoặc chỉ mypic.
thánh ca

2
@hymie Tôi biết, nhưng các yếu tố của tôi trong thư mục đó đều được đặt tên bằng .png ở cuối.
Colin

14
"Phần mở rộng" là gì? Đó không phải là một phần của việc đặt tên tệp Unix; nó được chuyển từ VMS / NT / Windows. Và các bạn trẻ cũng rời khỏi bãi cỏ của tôi. :)
mpez0

28
Chúng ta đừng nói quá. HĐH coi các phần mở rộng chỉ đơn giản là một phần của tên tệp, nhưng rất nhiều chương trình unix chú ý đến chúng, từ trình biên dịch đến GUI. Khái niệm này chắc chắn không xa lạ với unix.
alexis

1
Nó thường được đề xuất để tránh phân tích đầu rals và dẫn đầu ra của ls, và findchủ yếu là vì khả năng phát sinh newline, `tab char trong tên tệp. Nếu tên tệp là The new art of working on .png\NEWLINE files and other formatsnhiều giải pháp được đề xuất sẽ tạo ra vấn đề.
Hastur

Câu trả lời:


41

Bạn chỉ cần vỏ cho công việc này.

POSIXly:

for f in *.png; do
    printf '%s\n' "${f%.png}"
done

Với zsh:

print -rl -- *.png(:r)

4
Không cần printf; echo ${f%.png}sẽ đủ.
David Conrad

11
@Conrad: sử dụng echo sẽ không hoạt động chính xác trong một số trường hợp, nếu tên tệp bắt đầu bằng dấu gạch ngang hoặc chứa các chuỗi thoát.
cuonglm

3
@DavidConrad: Xem thêm unix.stackexchange.com/a/65819/38906
cuonglm

35
ls -1 | sed -e 's/\.png$//'

Các sedLoại bỏ lệnh (có nghĩa là, nó sẽ thay thế với chuỗi rỗng) bất kỳ chuỗi .pngtìm thấy ở cuối của một tên tập tin.

Cái .này được thoát ra \.để nó được hiểu sedlà một ký .tự theo nghĩa đen chứ không phải là biểu thức chính quy .(có nghĩa là khớp với bất kỳ ký tự nào). Đây $là neo cuối dòng, vì vậy nó không khớp .pngở giữa tên tệp.


4
Tôi nghĩ OP muốn bất kỳ tiện ích mở rộng nào bị tước, nhưng có lẽ chỉ là "cái cuối cùng". Vì vậy, có thể sửa đổi câu trả lời hay khác của bạn với:sed 's/\.[^.]*$//'
Otheus

1
vâng, regrec đó sẽ hoạt động trong trường hợp đó ... nhưng nếu OP muốn điều đó, họ nên nói như vậy thay vì nói cụ thể họ "muốn nó được liệt kê mà không có .png"
cas

4
Điều -1này là không cần thiết, là mặc định ở đây.
jlliagre

3
@jlliagre Tôi đồng ý với cas rằng -1nên được chỉ định. Nó chỉ là mặc định khi đường ống được bật, đó là một bất ngờ tiềm ẩn đối với một số người. Vì vậy, làm cho nó rõ ràng giúp hiểu. Tôi cũng làm điều này trong các kịch bản của mình để tôi biết loại đầu ra nào tôi mong đợi.
Otheus

1
Cảnh báo Trong trường hợp tên tệp có khóa ( .png) trước một char dòng mới, bạn sẽ xóa ngay cả cái đó .pngvà không chỉ cái cuối cùng. Tốt hơn là tránh đường ống và phân tích đầu ra của ls, nó thường bảo lưu những bất ngờ ẩn giấu tốt ... (một số từ và tài liệu tham khảo nhiều hơn trong câu trả lời).
Hastur

16

Nếu bạn chỉ muốn sử dụng bash:

for i in *; do echo "${i%.png}"; done

Bạn nên liên hệ grepkhi cố gắng tìm các kết quả khớp, không phải để xóa / thay thế cho sedphù hợp hơn:

find . -maxdepth 1 -name "*.png"  | sed 's/\.png$//'

Khi bạn quyết định, bạn cần tạo một số thư mục con để sắp xếp thứ tự trong các tệp PNG của mình, bạn có thể dễ dàng thay đổi điều đó thành:

find . -name "*.png"  | sed 's/\.png$//'

ls -1 | sed 's / .png //' hoạt động tuyệt vời. Cảm ơn bạn!
Colin

Các find đường ống để sedgiải pháp có thể trình bày một số vấn đề nếu bạn tìm thấy một tập tin với phím ( .png) như là một phần của tên và ngay trước khi một ký tự xuống dòng. Tốt hơn là tránh đường ống và phân tích đầu ra của findhoặc ls, nó thường bảo lưu những bất ngờ ẩn giấu tốt ... (một số từ và tài liệu tham khảo nhiều hơn trong câu trả lời).
Hastur

Có lẽ thay thế findbằng một cái gì đó như echotrong ví dụ trước. Không rõ mục đích findphục vụ ở đó và kết quả phụ thuộc vào cấu trúc thư mục (nghĩa là nếu bạn có thư mục files.png)

@BroSlow Cập nhật lên một cái gì đó hợp lý hơn.
Anthon

13

Tôi sẽ đi basename(giả sử triển khai GNU):

basename --suffix=.png -- *.png

Lưu ý rằng nếu bạn muốn sử dụng nó trong một đường ống, bạn có thể thấy hữu ích khi sử dụng tùy chọn -z(hoặc --zero) của tên cơ sở GNU để tạo đầu ra được phân tách bằng NUL (thay vì phân tách dòng mới).
Toby Speight

11

Một câu trả lời rất giống nhau (tôi ngạc nhiên khi biến thể đặc biệt này chưa xuất hiện) là:

ls | sed -n 's/\.png$//p'
  • Bạn không cần -1tùy chọn ls, vì lsgiả sử rằng nếu đầu ra tiêu chuẩn không phải là thiết bị đầu cuối (trong trường hợp này là đường ống).
  • các -ntùy chọn để sedcó nghĩa là 'không in dòng theo mặc định'
  • các /ptùy chọn ở phần cuối của các phương tiện thay thế '... và in dòng này nếu thay người đã được thực hiện.

Hiệu ứng ròng của điều đó là chỉ in ra những dòng kết thúc .png, với phần .pngbị loại bỏ. Đó là, điều này cũng phục vụ cho việc khái quát hóa một chút câu hỏi của OP, trong đó thư mục không chỉ chứa .pngcác tệp.

Các sed -nkỹ thuật thường hữu ích trong trường hợp bạn khác có thể sử dụng grep + sed.


Tôi thích cách chăm sóc bạn đã sử dụng để viết câu trả lời của bạn. Giải pháp này sẽ đưa ra các vấn đề với tên tệp bao gồm cả dòng mới , nó sẽ không in phần đầu tiên của tên. Thậm chí nhiều hơn nếu nó là một cái khó hơn với khóa ( .png) trước char dòng mới: trong trường hợp đó bạn sẽ in phần đó mà không png, không xóa phần cuối. Nó thường được kiểm tra để tránh phân tích (và đường ống) đầu ra lsvì các vấn đề có thể được ẩn đi ngay khi bạn không nghĩ về ...
Hastur

2
@Hastur Bạn đã đúng, về nguyên tắc và trang nổi tiếng về việc không phân tích ls liệt kê các vấn đề (và giải pháp) tiếp theo khi xử lý tên tệp bệnh lý. Nhưng cách xử lý tốt nhất là tránh có tên tệp bệnh lý (doh!); và nếu bạn không thể, hoặc nếu bạn phải mạnh mẽ chống lại họ, thì hãy sử dụng findhoặc - có thể tốt hơn - sử dụng ngôn ngữ mạnh hơn là shquản lý chúng (thực tế làsh có thể làm mọi thứ không có nghĩa là đó là lựa chọn tốt nhất trong từng trường hợp). Vỏ được thiết kế để sử dụng đầu tiên.
Norman Gray

Về nguyên tắc, tôi đồng ý về khả năng sử dụng, nhưng biến thể này không thành công khi bạn có một tên tệp với mỗi dòng mới bên trong. Điều này có thể dễ dàng xảy ra không được chú ý, ví dụ, khi bạn sao chép và dán một dòng từ pdf trong GUI, vì vậy bạn chỉ nghĩ rằng nên tránh các tên tệp bệnh lý .
Hastur

Hơn nữa, IMHO Thật dễ dàng để bắt đầu phân tích ls, nhưng nó đang gặp phải những vấn đề trong tương lai. Thông thường chúng ta tạo các tập lệnh mà chúng ta sẽ sử dụng sau này, khi chúng ta đã quên giới hạn của chúng ... (đó là con người, nó là bình thường). Tôi đã đề xuất một findví dụ (có -execvà không có đường ống) ngay cả khi tôi cho rằng một vỏ tốt hơn (vì vỏ nguyên chất) trả lời một , phù hợp và chắc chắn của cuonglm .
Hastur

@Hastur: những vấn đề trong tương lai sẽ phát sinh. Rất nhiều thứ trong hệ thống không mạnh mẽ đối với các tệp có dòng mới. Ví dụ: thử sử dụnglocate hoặc maketrên chúng.
Revierpost

8

Bạn chỉ có thể sử dụng các lệnh BASH để làm điều đó (không có bất kỳ công cụ bên ngoài nào).

for file in *; do echo "${file%.*}"; done 

Điều này hữu ích khi bạn không có / usr / bin và hoạt động tốt cho các tên tệp như this.is.image.png và cho tất cả các tiện ích mở rộng.


6

Nó không an toàn để phân tích lshoặc đường ống find[ 1 , 2 ]

Sẽ không an toàn khi phân tích (và chuyển đổi) đầu ra của lshoặc find, chủ yếu bởi vì có thể tìm thấy trong các tên tệp không phải là ký tự thông thường như dòng mới , tab ... Ở đây, chu trình shell thuần sẽ hoạt động [ cuonglm ] .
Ngay cả findlệnh không được đặt với tùy chọn-exec sẽ hoạt động:

find ./*.png  -exec  basename {} .png  \;

Cập nhật / Ghi chú : Bạn có thể sử dụng find .để tìm kiếm ngay cả các tệp bị ẩn hoặc find ./*.pngchỉ nhận những tệp không bị ẩn. Với find *.png -exec ...bạn có thể có vấn đề trong trường hợp nó xuất hiện một tệp có tên .pngvì find sẽ lấy nó làm tùy chọn. Bạn có thể thêm-maxdepth 0 để tránh đi xuống trong các thư mục có tên là Dir_01.pnghoặc find ./*.png -prune -exec ...khi maxdepth không được phép (cảm ơn Stéphane). Nếu bạn muốn tránh liệt kê các thư mục đó, bạn nên thêm tùy chọn -type f(cũng sẽ loại trừ các loại tệp không thường xuyên khác). Hãy nhìn vào manbức tranh toàn cảnh đầy đủ hơn về tất cả các tùy chọn có sẵn và nhớ kiểm tra khi chúng tuân thủ POSIX, để có tính di động tốt hơn.

Một số từ nữa

Ví dụ, có thể xảy ra việc sao chép tiêu đề từ tài liệu và dán vào tên tệp, một hoặc nhiều dòng mới sẽ kết thúc bằng chính tên tệp. Chúng ta thậm chí có thể không may mắn đến mức một tiêu đề có thể chứa ngay cả khóa chúng ta phải sử dụng ngay trước dòng mới:

The new art of working on .png
files and other formats.

Nếu bạn muốn kiểm tra, bạn có thể tạo các tên tệp như thế này bằng các lệnh

touch "A file with two lines"$'\n'"and This is the second.png"
touch "The new art of working on .png"$'\n'"files and other formats.png"

Đơn giản /bin/ls *pngsẽ xuất? thay vì các ký tự không in được

A file with two lines?and This is the second.png
The new art of working on .png?files and other formats.png

Trong tất cả các trường hợp bạn sẽ đặt đầu ra của lệnh lshoặc findlệnh sau sẽ không có gợi ý nào để hiểu nếu dòng hiện tại đến từ một tên tệp mới hoặc nếu nó đi theo một tự dòng mới trong tên tệp tiền lệ . Một cái tên khó chịu thực sự, nhưng vẫn là một tên hợp pháp.

Một chu trình shell với Parameter-Expansion, ${parameter%word}trong cả hai biến thể có printfhoặc echosẽ hoạt động [ cuonglm ], [ Anthon1 ] .

for f in *.png; do printf "%s\n" "${f%.png}" ; done

Từ trang man của Mở rộng tham số Shell [ 3 ]

$ {tham số% word}
$ {tham số %% word}

... kết quả của việc mở rộng là giá trị của tham số có mẫu khớp ngắn nhất (trường hợp '%') hoặc mẫu khớp dài nhất (trường hợp '%%') đã bị xóa.


Ngoài ra, kết quả của findlệnh của bạn là một biến bit (ví dụ: nếu có một thư mục được gọi files.png)

1
@BroSlow thân mến, khi tôi viết câu trả lời ở trên, tôi đã thử 13 (tất cả) các biến thể khác có mặt trong thời điểm đó, bằng dòng lệnh, trong một kịch bản, được đưa ra như là đối số của một lời gọi shell. Hãy làm như vậy và cho tôi biết nếu họ cư xử theo cách bạn mong đợi. Tôi đã thực hiện các thử nghiệm của mình với bash 4.3.11, dash 0,5.7-4, zsh (khi cần) 5.0.2. Cảm thấy bạn thoải mái để đọc bài viết này mà thêm một cái gì đó nhiều hơn. Tôi đồng ý về lưu ý của đường ống đầu ra find, vì điều này tôi đã đề nghị rõ ràng-exec , và tôi đã viết trong tiêu đề. :-).
Hastur

Đọc lại wiki một lần nữa. Tôi vẫn nghĩ rằng bạn cần phải đưa ra ví dụ của mình, vì đó là những gì đang được thảo luận ở đây. Và đối với phần lớn các phiên bản hiện đại ls, không có vấn đề gì khi đầu ra được dẫn hoặc chuyển hướng, nhưng như đã đề cập trong wiki, nó có thể không hoạt động cho tất cả. Hầu hết sẽ chỉ chèn ?vị trí của các ký tự đặc biệt khi đầu ra được gửi đến thiết bị đầu cuối. tức là làm echo *.png | od -cls *.png | od -c. Vấn đề về dòng mới không phải là vấn đề ls, đó là vấn đề với bất kỳ lệnh nào không kết thúc ở cả hai phía của đường ống.

1
printf "${f%.png}\n"sai. Đối số đầu tiên là định dạng, bạn không nên sử dụng dữ liệu biến trong đó. Thậm chí có thể được coi là một lỗ hổng DoS ( %1000000000s.pngví dụ: thử với một tệp).
Stéphane Chazelas

Bạn cần find ./*.png -prune -exec...hoặc bạn gặp vấn đề với tên tệp bắt đầu bằng -(và các tệp của thư mục loại, lưu ý rằng -maxdepthkhông thể mang theo được)
Stéphane Chazelas

4

chưa đủ sao?

ls -1 | sed 's/\.png//g'

hoặc nói chung, cái này

ls -1 | sed 's/\.[a-z]*//g'

sẽ xóa tất cả các tiện ích mở rộng


Đó là nhưng các giải pháp khác làm việc quá.
Colin

Ý tôi là, câu hỏi của bạn bắt đầu với ls -1, vì vậy ls -1 nên làm điều đó. :)
Rohail Abbas

Điều -1này là không cần thiết, là mặc định ở đây.
jlliagre

@Rohail Abbas Nhưng không phải hệ thống nào cũng cài đặt sed?
Colin

1
Thật vậy, nhưng lsdù sao nó cũng không có tùy chọn đó khi đầu ra của nó không phải là một thiết bị đầu cuối, đó là trường hợp ở đây.
jlliagre

3

Sử dụng rev:

ls -1 | rev | cut -f 2- -d "." | rev

revđảo ngược tất cả các chuỗi (dòng); bạn cắt mọi thứ sau lần đầu tiên '.' và rev đảo ngược tàn dư.

Nếu bạn muốn grep'alma':

ls -1 | rev | cut -f 2- -d "." | rev | grep 'alma'

Điều -1này là không cần thiết, là mặc định ở đây.
jlliagre

2
Điều này thất bại nặng nề trên một tập tin có tênMy.2016.Summer.Vacation.png
David Conrad

@DavidConrad my bad: / Tôi đã sửa thànhcut -f 2-
Tom Solid

Bây giờ nó hoạt động với tệp đó nhưng chưa có tệp .pngvà dòng mới ngay sau đó ... Nên tránh phân tích cú pháp lsvì nó thích che giấu những điều ngạc nhiên ... :-)
Hastur

2

Nếu tôi biết thư mục chỉ có các tệp có .png là một phần mở rộng, tôi sẽ chỉ chạy: ls | awk -F. '{print $1}'

Điều này sẽ trả về "trường" đầu tiên cho bất cứ nơi nào có tên tệp.extension.

Thí dụ:

[rsingh@rule51 TESTDIR]$ ls
10.png  1.png  2.png  3.png  4.png  5.png  6.png  7.png  8.png  9.png

[rsingh@rule51 TESTDIR]$ ls | awk -F. '{print $1}'
10
1
2
3
4
5
6
7
8
9

Thật không may, nó sẽ thất bại trên tất cả các tên tệp có nhiều hơn một ., Image.1.pngvà ngay cả trên những tên không có tên đẹp , bên trong có các ký tự đặc biệt. dưới dạng dòng mới hoặc dòng mà bạn sẽ sử dụng làm dấu tách bản ghi (đầu vào) trong awk, RS. Bạn nên tránh phân tích lsđầu ra bởi vì nó thích che giấu vấn đề sẽ phát sinh khi bạn không mong đợi. Bạn có thể đọc thêm trong các tham chiếu 1 hoặc 2 . BTW rất hay ý tưởng sử dụng awk ... Tôi đặt một số ví dụ trong một câu trả lời.
Hastur

Tuy nhiên, đúng, với mẫu được cung cấp bởi Colin, nó sẽ hoạt động tốt. Để làm cho nó hoạt động cho trường hợp bạn đề xuất, có lẽ tôi nên đổi nó thành: [rsingh @ rule51 TESTDIR] $ ls | sed -e 's / .png $ // '10 1 2 3 4 5 6 7 8 9 harry.the.bunny whats.a.png.filename Không cố gắng để làm khó, nhưng với nhu cầu của Colin, tôi không chắc vấn đề gì sẽ được phân tích cú pháp ls.
rsingh

xin lỗi ... tôi chỉ nhận ra rằng tôi đã không hiển thị thư mục với các tệp trước khi sed sửa đổi đầu ra của 'ls' [rsingh @ rule51 TESTDIR] $ ls 10.png 2.png 4.png 6.png 8.png harry.the.bunny.png 1.png 3.png 5.png 7.png 9.png whats.a.png.filename.png [rsingh @ rule51 TESTDIR] $ ls | sed -e 's / .png $ // '10 1 2 3 4 5 6 7 8 9 harry.the.bunny whats.a.png.filename
rsingh

Note1 bạn cần phải thoát khỏi .\.bên trong sed -e 's/\.png$//', nhưng để nó trở thành một câu trả lời chỉ bằng văn bản. :-( note2 bạn có thể thử sử dụng awkvới một cái gì đó như ls | awk -F. '{if ($NF=="png") {for (i=1;i<NF-1;i++) printf("%s.", $i) ; printf $(NF-1)"\n"}}'... nhưng bạn sẽ luôn gặp phải vấn đề mà awk không thể biết liệu dòng đang đến có theo dõi hay không một dòng mới bên trong tên tệp. Tôi đã cố gắng nói tốt hơn trong câu trả lời của mình .
Hastur

Cảm ơn Hastur, tôi đã bỏ lỡ điều đó :). Ngoài ra, tôi đã bỏ việc sử dụng awk để ủng hộ sed trong trường hợp này.
rsingh

2

theo nhận xét của bạn "Tôi có một tệp văn bản trong đó tất cả các tên được liệt kê mà không có phần mở rộng. Tôi muốn tạo một tập lệnh PHP so sánh tệp văn bản với thư mục để xem tệp nào bị thiếu":

for file in $(cat yourlist) ; do
  [ -f "${file}.png" ] || {
    echo "$file : listed in yourlist, but missing in the directory"
  }
done
#assumes that filenames have no space...
# otherwise use instead:
#  while IFS= read file ; do ...(same inner loop as above)... ; done < yourlist

và ngược lại:

for file in *.png ; do
  grep "^${file%.png}$" yourlist >/dev/null || {
    echo "$file: present in the directory but not listed in yourlist"
  }
done
#I assume there are no spaces/tabs/? before/after names in 'yourlist'. Change the script accordingly if there are some (or sanitize the list)

1

ls -l | sed 's/\.png$//'

Là phương pháp chính xác nhất như được đánh dấu bởi @roaima. Nếu không có các \.pngtệp thoát được đặt tên a_png.pngsẽ được liệt kê là : a_.


sử dụng ls -l, như bạn làm, cung cấp các chi tiết về tệp, đó không phải là những gì OP yêu cầu.
Anthon

1

Một dòng shell đơn giản (ksh, bash hoặc zsh; không dash):

set -- *.png; printf '%s\n' "${@%.png}"

Một chức năng đơn giản (từ Không có phần mở rộng):

ne(){ set -- *.png; printf '%s\n' "${@%.png}"; }

Hoặc một chức năng loại bỏ bất kỳ tiện ích mở rộng nào (png theo mặc định):

ne(){ ext=${1:-png}; set -- *."$ext"; printf '%s\n' "${@%.${ext}}"; }

Sử dụng như là:

ne jpg

Nếu đầu ra là dấu hoa thị *, không có tệp nào có phần mở rộng đó tồn tại.


1

Bạn có thể thử nguồn cấp dữ liệu sau đây, đầu ra của ls, trình quản lý của bạn là "." và vì tất cả các tệp của bạn sẽ có name.png, bạn in cột đầu tiên:
ls | awk -F"." '{print $1}'


-1

Nếu bạn đã truy cập vào sed, điều này tốt hơn vì nó sẽ loại bỏ phần mở rộng tập tin cuối cùng, bất kể đó là gì (png, jpg, tiff, v.v ...)

ls | sed -e 's/\..*$//'

7
Phá vỡ tên tập tin như thế nào this.is.a.dotty.txt. Hãy thử s/\.[^.]*$//thay thế.
roaima
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.