„ls -1”: jak wyświetlić listę nazw plików bez rozszerzenia


24

ls -1 wyświetla moje elementy w następujący sposób:

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

Chcę go umieścić na liście bez tego .pngtypu:

foo
bar
foobar
...

(katalog zawiera tylko .pngpliki)

Czy ktoś może mi powiedzieć, jak korzystać grepw tym przypadku?

Cel: Mam plik tekstowy, w którym wszystkie nazwy są wymienione bez rozszerzenia. Chcę utworzyć skrypt, który porównuje plik tekstowy z folderem, aby zobaczyć, którego pliku brakuje.


36
Chcesz być ostrożny z taką prośbą. Linux nie ma rozszerzeń nazw plików. Linux ma nazwy plików, które mogą zawierać lub nie .. Choć konwencja mówi, aby wymienić swoje pliki .pngna końcu, nie ma powodu, dlaczego nie mogę mieć plik png nazwie foo.ziplub my.picture.20160518czy po prostu mypic.
hymie

2
@ hymie Wiem, ale wszystkie moje elementy w tym folderze mają na końcu nazwę .png.
Colin

14
Co to jest „rozszerzenie”? To nie jest część nazewnictwa plików Uniksa; jest to przeniesienie z VMS / NT / Windows cokolwiek. I wy, młodzi, też zejście z mojego trawnika. :)
mpez0

28
Nie zawyżajmy tego. System operacyjny traktuje rozszerzenia jako po prostu część nazwy pliku, ale wiele programów uniksowych zwraca na nie uwagę, od kompilatora po GUI. Ta koncepcja z pewnością nie jest obca uniksowi.
Alexis

1
Zazwyczaj zalecals się unikanie analizowania i przesyłania danych wyjściowych lsoraz find, głównie dlatego, że istnieje możliwość wprowadzenia newlineznaku tab tab w nazwie pliku. Jeśli nazwa pliku jest The new art of working on .png\NEWLINE files and other formatsduża, proponowane rozwiązanie spowoduje problemy.
Hastur

Odpowiedzi:


41

Do tego zadania potrzebujesz tylko powłoki.

POSIXly:

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

Z zsh:

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

4
Nie ma takiej potrzeby printf; echo ${f%.png}wystarczy.
David Conrad,

11
@ Conrad: użycie echa nie działa poprawnie w niektórych przypadkach, jeśli nazwa pliku zaczyna się od myślnika lub zawiera sekwencje specjalne.
cuonglm

3
@DavidConrad: Zobacz także unix.stackexchange.com/a/65819/38906
cuonglm

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

W sedusuwa polecenie (to znaczy, że zastępuje z pustym ciągiem) dowolny ciąg .pngznaleźć na końcu nazwy pliku.

Znak .ucieczki jest \.tak interpretowany, że jest interpretowany sedjako dosłowny .znak, a nie wyrażenie regularne .(co oznacza dopasowanie dowolnego znaku). Jest $to kotwica końca linii, więc nie pasuje .pngdo środka nazwy pliku.


4
Myślę, że OP chce, aby każde rozszerzenie zostało pozbawione, ale prawdopodobnie tylko „ostatni”. Więc może zmodyfikuj swoją dobrą odpowiedź za pomocą:sed 's/\.[^.]*$//'
Otheus

1
tak, wyrażenie regularne zadziałałoby w takim przypadku ... ale jeśli OP tego chce, powinni powiedzieć tak, a nie konkretnie powiedzieć, że „chcą, żeby nie było na liście bez pliku .png”
cas

4
Nie -1jest to konieczne, ponieważ jest to ustawienie domyślne.
jlliagre

3
@jlliagre Zgadzam się z cas, że -1należy określić. Jest to ustawienie domyślne tylko po włączeniu potoku, co dla niektórych jest ukrytą niespodzianką. Wyjaśnienie tego pomaga zrozumieć. Robię to również w moich skryptach, więc wiem, jakiego rodzaju wyników oczekuję.
Otheus

1
Ostrzeżenie W przypadku nazwy pliku z klawiszem ( .png) przed znakiem nowego wiersza usuniesz nawet to .pngi nie tylko ostatni. Lepiej jest unikać potokowania i analizowania wyników ls, rezerwuje często dobrze ukryte niespodzianki ... (kilka słów i odniesień więcej w odpowiedzi).
Hastur

16

Jeśli chcesz tylko użyć bash:

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

Powinieneś sięgać greppo próbę znalezienia dopasowań, a nie usuwanie / zastępowanie tego sedjest bardziej odpowiednie:

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

Po podjęciu decyzji o utworzeniu podkatalogów w celu uporządkowania plików PNG można łatwo zmienić to na:

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

ls -1 | sed 's / .png //' działa świetnie. Dziękuję Ci!
Colin

Rozwiązanie find potokowe do sedrozwiązania może powodować pewne problemy, jeśli znajdziesz plik z kluczem ( .png) jako częścią nazwy i tuż przed znakiem nowego wiersza. Lepiej unikać robienia potoków i analizowania wyników findlub ls, często rezerwuje często dobrze ukryte niespodzianki ... (kilka słów i odniesień więcej w odpowiedzi).
Hastur

Prawdopodobnie zamień na findcoś takiego jak echow ostatnim przykładzie. Nie jest jasne, jaki findjest tam cel, a wyniki zależą od struktury katalogów (tj. Jeśli masz katalog files.png)

@BroSlow Zaktualizowano do czegoś bardziej sensownego.
Anthon

13

Wybrałbym basename(przy założeniu implementacji GNU):

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

Zauważ, że jeśli chcesz używać go w potoku, pomocne może być użycie opcji GNU basename -z(lub --zero) do generowania danych wyjściowych rozdzielonych przez NUL (zamiast rozdzielonych znakiem nowej linii).
Toby Speight

11

Inna bardzo podobna odpowiedź (jestem zaskoczona, że ​​ten konkretny wariant jeszcze się nie pojawił) to:

ls | sed -n 's/\.png$//p'
  • Nie potrzebujesz tej -1opcji ls, ponieważ lszakłada się, że jeśli standardowe wyjście nie jest terminalem (w tym przypadku jest to potok).
  • -nopcja sedpomocą „nie drukować linię domyślnie”
  • /popcja na koniec środków zastępczych „... i wydrukować tę linię, jeśli się na zmianę powstał”.

Efektem tego jest wydrukowanie tylko tych linii, które kończą się .png, po .pngusunięciu. Oznacza to, że dotyczy to również niewielkiego uogólnienia pytania OP, w którym katalog nie zawiera tylko .pngplików.

Ta sed -ntechnika jest często przydatna w przypadkach, w których można użyć grep + sed.


Podoba mi się sposób, w jaki napisałeś swoją odpowiedź. To rozwiązanie spowoduje problemy z nazwami plików, w tym znakami nowej linii , nie drukuje pierwszej części nazwy. Co więcej, jeśli jest to nieprzyjemne z klawiszem ( .png) przed znakiem nowej linii: w takim przypadku wydrukujesz tę część bez png, nie usuwając tylko ostatniej części. Często sugeruje się, aby uniknąć parsowania (i potoku) danych wyjściowych, lsponieważ problemy można ukryć tam, gdzie nie myślisz o tym ...
Hastur

2
@Hastur Zasadniczo masz rację, a słynna strona o „ nie analizuj” zawiera listę dalszych problemów (i rozwiązań) przy podawaniu patologicznych nazw plików. Ale najlepszym sposobem postępowania jest unikanie patologicznych nazw plików (doh!); a jeśli nie możesz lub musisz być wobec nich odporny, użyj findalbo - lub lepiej - użyj mocniejszego języka niż shdo zarządzania nimi (fakt, że sh potrafisz zrobić wszystko, nie oznacza, że ​​jest to najlepszy wybór w każda sprawa). Skorupa została zaprojektowana przede wszystkim pod kątem użyteczności.
Norman Gray

Zgadzam się co do zasady z użytecznością, ale ten wariant zawodzi, gdy masz nazwę pliku z każdą nową linią w środku. Może to łatwo wystąpić niezauważone, na przykład podczas kopiowania i wklejania wiersza z pliku pdf w interfejsie GUI, więc myślisz tylko, że można uniknąć patologicznych nazw plików .
Hastur

Co więcej, IMHO Łatwo jest zacząć analizować ls, ale ma wiele problemów. Często tworzymy skrypty, których będziemy używać później, kiedy już zapomnimy ich limitu ... (to ludzkie, to normalne). I proponuje się findprzykład (z -execi bez rury), nawet jeśli uzna lepiej (ponieważ czyste powłoki) Odpowiedź na jednym cuonglm na stałe i POSIX zgodny.
Hastur

@Hastur: te przyszłe problemy i tak się pojawią. Wiele rzeczy w systemie nie jest odpornych na pliki z nowymi liniami. Np. Spróbuj użyć locatelub makena nich.
reinierpost

8

W tym celu można użyć tylko poleceń BASH (bez żadnych zewnętrznych narzędzi).

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

Jest to przydatne, gdy nie masz / usr / bin i działa dobrze dla nazw plików takich jak this.is.image.png i dla wszystkich rozszerzeń.


6

Nie jest bezpiecznie parsować lsani potokować find[ 1 , 2 ]

Nie jest bezpiecznie parsować (i przesyłać strumieniowo) danych wyjściowych lslub find, głównie dlatego, że w nazwach plików można znaleźć nietypowe znaki jako znak nowej linii , tab ... Tutaj działa czysty cykl powłoki [ cuonglm ] .
Działa nawet findpolecenie, które nie zostało potokowane z opcją -exec:

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

Aktualizacje / Notatki : Możesz użyć, find .aby wyszukać nawet ukryte pliki lub find ./*.pnguzyskać tylko te nie ukryte. Ze find *.png -exec ...można mieć problem w przypadku był obecny plik o nazwie .pngponieważ znalezisko dostanie go jako opcja. Możesz dodać, -maxdepth 0aby uniknąć schodzenia w katalogach o nazwie as Dir_01.pnglub find ./*.png -prune -exec ...gdy maxdepth nie jest dozwolone (dzięki Stéphane). Jeśli chcesz uniknąć wyświetlania tych katalogów, powinieneś dodać opcję -type f(która wykluczałaby również inne typy nieregularnych plików). Spójrz na manpełniejszą panoramę wszystkich dostępnych opcji i pamiętaj, aby sprawdzić, czy są one zgodne z POSIX, aby uzyskać lepszą przenośność.

Jeszcze kilka słów

Może się zdarzyć na przykład, że po skopiowaniu tytułu z dokumentu i wklejeniu do nazwy pliku jeden lub więcej znaków nowej linii zakończy się w samej nazwie pliku. Możemy mieć tak pecha, że ​​tytuł może zawierać nawet klucz, którego musimy użyć tuż przed nową linią:

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

Jeśli chcesz przetestować, możesz utworzyć takie nazwy plików za pomocą poleceń

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"

Prosty /bin/ls *pngwyświetli ?zamiast znaków niedrukowalnych

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

We wszystkich przypadkach, w których będzie rura wyjście z lslub findnastępująca komenda będzie miała żadnej wskazówki do zrozumienia, jeżeli obecna linia pochodzi z nowej nazwy pliku lub jeżeli wynika to z nowej linii znak w precedens nazwy pliku . Naprawdę paskudne imię, ale wciąż legalne.

Cykl powłoki z rozszerzeniem parametru powłoki ${parameter%word}, zarówno w wariancie z printflub echobędzie działał [ cuonglm ], [ Anthon1 ] .

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

Ze strony podręcznika rozszerzania parametrów powłoki [ 3 ]

$ {parametr% słowo}
$ {parametr %% słowo}

... wynikiem rozszerzenia jest wartość parametru z usuniętym najkrótszym wzorcem dopasowania (przypadek „%”) lub najdłuższym wzorcem dopasowania (przypadek „%%”).


Również wyniki twojego findpolecenia są nieco zmienne (na przykład, jeśli istnieje katalog o nazwie files.png)

1
Drogi @BroSlow, kiedy napisałem powyższą odpowiedź , wypróbowałem 13 (wszystkich) innych wariantów obecnych w tym momencie, z wiersza poleceń, w skrypcie, uruchomionych jako argument wywołania powłoki. Zrób to samo i powiedz mi, czy zachowują się tak, jak oczekujesz. Testy bash 4.3.11wykonałem z myślnikiem 0.5.7-4, zsh (w razie potrzeby) 5.0.2. Zapraszam do lektury tego postu, który dodaje coś więcej. Zgadzam o nucie potokiem wyjście find, w tym I wyraźnie sugerował-exec , i napisałem w tytule. :-).
Hastur

Ponownie przeczytaj wiki. Nadal uważam, że musisz podać swój przykład, ponieważ o tym tutaj dyskutujemy. I dla większości współczesnych wersji lsnie ma żadnego problemu, gdy wyjście jest przesyłane potokowo lub przekierowywane, ale jak wspomniano w wiki, może nie działać dla wszystkich. Większość wstawi ?znaki specjalne w miejsce, gdy dane wyjściowe są wysyłane do terminala. tj. Do echo *.png | od -ci ls *.png | od -c. Problem z nową linią nie stanowi problemu ls, jest to problem z dowolnymi poleceniami, które nie kończą się po obu stronach potoku.

1
printf "${f%.png}\n"jest źle. Pierwszym argumentem jest format, nie powinieneś używać tam zmiennych danych. Może być nawet postrzegany jako luka DoS (spróbuj %1000000000s.pngna przykład z plikiem).
Stéphane Chazelas

Potrzebujesz find ./*.png -prune -exec...lub miałbyś problemy z nazwami plików zaczynającymi się od -(i plikami katalogu typu, pamiętaj, że -maxdepthnie jest przenośny)
Stéphane Chazelas

4

czy to nie wystarczyło?

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

lub ogólnie to

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

usunie wszystkie rozszerzenia


Tak było, ale inne rozwiązania też działają.
Colin

Chciałem powiedzieć, że twoje pytanie zaczęło się od ls -1, więc ls -1 powinno to zrobić. :)
Rohail Abbas

Nie -1jest to konieczne, ponieważ jest to ustawienie domyślne.
jlliagre

@Rohail Abbas Ale nie każdy system jest zainstalowany?
Colin

1
Rzeczywiście, ale lsrobi to i tak bez tej opcji, gdy jego wyjście nie jest terminalem, co ma miejsce w tym przypadku.
jlliagre

3

Użyj rev:

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

revodwraca wszystkie ciągi (linie); przecinasz wszystko po pierwszym „.” i rev przywraca resztkę.

Jeśli chcesz grep„alma”:

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

Nie -1jest to konieczne, ponieważ jest to ustawienie domyślne.
jlliagre

2
Nie udaje się to w przypadku pliku o nazwieMy.2016.Summer.Vacation.png
David Conrad,

@DavidConrad my bad: / Poprawiłemcut -f 2-
Tom Solid

Teraz działa z tym plikiem, ale jeszcze nie z plikiem z .pngi nową linią zaraz po ... Sugeruje się, aby unikać parsowania, lsponieważ uwielbia dobrze ukrywać niespodzianki ... :-)
Hastur

2

Gdybym wiedział, że katalog zawiera tylko pliki z rozszerzeniem .png, po prostu uruchomiłbym: ls | awk -F. '{print $1}'

Zwróci to pierwsze „pole” dla wszystkiego, co zawiera nazwę pliku. Rozszerzenie.

Przykład:

[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

Niestety, nie powiedzie się na wszystkich nazwach plików z więcej niż jednym ., Image.1.pnga nawet na tych o nieprzyjemnych nazwach , ze znakami specjalnymi w środku. jako nowej linii lub taki, który będzie używany jako separator (wejściowego) rekordowym awk, RS. Sugeruje się, aby unikać analizowania danych lswyjściowych, ponieważ uwielbia ukrywać problemy, które pojawią się, gdy się nie spodziewasz. Możesz przeczytać więcej w tych odnośnikach 1 lub 2 . BTW fajny pomysł na użycie awk ... W jednej odpowiedzi podałem kilka przykładów.
Hastur

To prawda, że ​​biorąc pod uwagę próbkę dostarczoną przez Colina, to zadziałałoby dobrze. Aby działało w przypadku, który zasugerowałeś, prawdopodobnie zmieniłbym to na: [rsingh @ rules51 TESTDIR] $ ls | sed -e 's / .png $ //' 10 1 2 3 4 5 6 7 8 9 harry.the.bunny whats.a.png. nazwa pliku Nie próbuje być trudny, ale biorąc pod uwagę potrzebę Colina, nie jestem pewien jaki byłby problem z analizą ls.
rsingh

przepraszam ... Właśnie zdałem sobie sprawę, że nie pokazałem katalogu z plikami przed sedem modyfikującym wyjście '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. nazwa pliku.png [rsingh @ rules51 TESTDIR] $ ls | sed -e 's / .png $ //' 10 1 2 3 4 5 6 7 8 9 harry.the.bunny whats.a.png. nazwa pliku
rsingh

Note1 trzeba uciec .się \.wewnątrz sed -e 's/\.png$//', ale tak, że staje się odpowiedź tylko napisane. :-( note2 możesz spróbować użyć awkz czymś takim jak ls | awk -F. '{if ($NF=="png") {for (i=1;i<NF-1;i++) printf("%s.", $i) ; printf $(NF-1)"\n"}}'... ale zawsze będziesz mieć problem, którego awk nie może wiedzieć, czy linia nadchodzi jest następująca, czy nie jest nowa linia w nazwie pliku. Próbowałem powiedzieć lepiej w mojej odpowiedzi .
Hastur

Dzięki Hastur, tęskniłem :). Poza tym w tym przypadku porzuciłem użycie awk na korzyść sed.
rsingh

2

zgodnie z komentarzem „Mam plik tekstowy, w którym wszystkie nazwy są wymienione bez rozszerzenia. Chcę stworzyć skrypt PHP, który porówna plik tekstowy z folderem, aby zobaczyć, którego pliku brakuje”:

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

i odwrotnie:

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$//'

Jest najdokładniejszą metodą podkreśloną przez @roaima. Bez uciekły \.pngplików nazwanych a_png.pngbędzie na liście: a_.


używając ls -l, jak to robisz, podaje szczegóły pliku, nie o to pytał OP.
Anthon

1

Prosta linia powłoki (ksh, bash lub zsh; nie myślnik):

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

Prosta funkcja (z Bez rozszerzenia):

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

Lub funkcja, która usuwa dowolne podane rozszerzenie (domyślnie png):

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

Użyj jako:

ne jpg

Jeśli wynik jest gwiazdką *, nie istnieje plik z tym rozszerzeniem.


1

Możesz wypróbować następujący kanał awk, wyjściem ls twojego superatora jest „.” a ponieważ wszystkie twoje pliki będą miały nazwę.png wydrukujesz pierwszą kolumnę:
ls | awk -F"." '{print $1}'


-1

Jeśli masz dostęp do sed, jest to lepsze, ponieważ usunie ostatnie rozszerzenie pliku, bez względu na to, co to jest (png, jpg, tiff itp.)

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

7
Przerwy dla nazw plików takich jak this.is.a.dotty.txt. Spróbuj s/\.[^.]*$//zamiast tego.
roaima
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.