'ls -1': wie man Dateinamen ohne Erweiterung auflistet


24

ls -1 listet meine Elemente so auf:

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

Ich möchte, dass es ohne .pngähnliches aufgelistet wird :

foo
bar
foobar
...

(das Verzeichnis enthält nur .pngDateien)

Kann mir jemand sagen, wie man grepin diesem Fall verwendet?

Zweck: Ich habe eine Textdatei, in der alle Namen ohne die Erweiterung aufgelistet sind. Ich möchte ein Skript erstellen, das die Textdatei mit dem Ordner vergleicht, um festzustellen, welche Datei fehlt.


36
Sie möchten mit einer solchen Anfrage vorsichtig sein. Linux hat keine Dateinamenerweiterungen. Linux hat Dateinamen, die ein enthalten können oder nicht .. Obwohl die Konvention besagt, dass Sie Ihre Dateien .pngam Ende mit einem Namen versehen sollen, gibt es keinen Grund, warum ich keine PNG-Datei mit dem Namen foo.zipoder my.picture.20160518oder einfach haben kann mypic.
Hymie

2
@hymie Ich weiß, aber meine Elemente in diesem Ordner sind alle mit .png am Ende benannt.
Colin

14
Was ist eine "Erweiterung"? Das gehört nicht zur Benennung von Unix-Dateien. es ist Übertragung von VMS / NT / Windows, was auch immer. Und Sie Kinder steigen auch von meinem Rasen. :)
mpez0

28
Lassen Sie uns das nicht übertreiben. Das Betriebssystem betrachtet Erweiterungen einfach als Teil des Dateinamens, aber viele Unix-Programme beachten sie, vom Compiler bis zur GUI. Das Konzept ist für Unix mit Sicherheit nicht fremd.
Alexis

1
Es wird in der Regel vorgeschlagen , vermeiden die Ausgabe zu analysierenls und zu Rohr des Ausgangs lsund find, vor allem , weil die Möglichkeit , in entstehen newline, `Tab Zeichen im Dateinamen. Wenn der Dateiname The new art of working on .png\NEWLINE files and other formatsviele der vorgeschlagenen Lösungen enthält, treten Probleme auf.
Hastur

Antworten:


41

Sie benötigen nur die Shell für diesen Job.

POSIXly:

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

Mit zsh:

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

4
Es gibt keine Notwendigkeit dafür printf; echo ${f%.png}wird genügen.
David Conrad

11
@Conrad: Die Verwendung von Echo funktioniert in einigen Fällen nicht richtig, wenn der Dateiname mit einem Bindestrich beginnt oder maskierte Sequenzen enthält.
Donnerstag,

3
@ DavidConrad: Siehe auch unix.stackexchange.com/a/65819/38906
cuonglm

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

Der sedBefehl entfernt (dh ersetzt durch die leere Zeichenfolge) alle Zeichenfolgen, .pngdie am Ende eines Dateinamens stehen.

Das .wird \.so maskiert , dass es als Literalzeichen und nicht als regulärer Ausdruck interpretiert wird (was bedeutet , dass es mit sedeinem beliebigen Zeichen übereinstimmt). Das ist der Zeilenende-Anker, daher stimmt er nicht in der Mitte eines Dateinamens überein ...$.png


4
Ich denke das OP will keine Nebenstelle abisolieren, sondern wohl nur die "letzte". Also ändern Sie vielleicht Ihre ansonsten gute Antwort mit:sed 's/\.[^.]*$//'
Otheus

1
ja, das regexp würde in diesem Fall funktionieren ... aber wenn das OP das will, sollten sie es sagen, anstatt ausdrücklich zu sagen, dass sie "es ohne die .png aufgelistet haben wollen"
cas

4
Dies -1ist nicht erforderlich, da dies die Standardeinstellung ist.
Juli

3
@jlliagre Ich stimme cas zu, dass das -1spezifiziert werden sollte. Dies ist nur die Standardeinstellung, wenn die Pipe eingeschaltet ist, was für manche eine versteckte Überraschung ist. Das Ausdrücken hilft also zu verstehen. Ich mache das auch in meinen Skripten, damit ich weiß, welche Art von Ausgabe ich erwarte.
Otheus

1
Warnung Bei einem Dateinamen mit der Taste ( .png) vor einem Zeilenumbruchzeichen wird auch dieser .pngund nicht nur der letzte gelöscht . Es ist besser zu vermeiden, die Ausgabe von ls zu leiten und zu analysieren, da dies häufig gut versteckte Überraschungen birgt ... (einige Wörter und Verweise mehr in der Antwort).
Hastur

16

Wenn Sie nur bash verwenden möchten:

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

Sie sollten grepbei der Suche nach Übereinstimmungen nach greifen , nicht nach Entfernen / Ersetzen sed:

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

Sobald Sie sich entschlossen haben, einige Unterverzeichnisse zu erstellen, um Ordnung in Ihre PNG-Dateien zu bringen, können Sie dies leicht ändern in:

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

ls -1 | sed 's / .png //' funktioniert super. Vielen Dank!
Colin

Die find Weiterleitung zur sedLösung kann einige Probleme verursachen, wenn Sie eine Datei mit dem Schlüssel ( .png) als Teil des Namens und kurz vor einem Zeilenumbruch finden. Es ist besser zu vermeiden, die Ausgabe von findoder zu leiten und zu analysieren ls, es behält sich oft gut versteckte Überraschungen vor ... (einige Wörter und Verweise mehr in der Antwort).
Hastur

Wahrscheinlich durch so findetwas wie echoim letzten Beispiel ersetzen . Nicht klar, welchen Zweck es findgibt und die Ergebnisse hängen von der Verzeichnisstruktur ab (dh, wenn Sie ein Verzeichnis haben files.png)

@BroSlow Auf etwas Vernünftigeres aktualisiert.
Anthon

13

Ich würde gehen basename(unter der Annahme der GNU-Implementierung):

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

Wenn Sie es in einer Pipe verwenden möchten, kann es hilfreich sein, die Option -z(oder --zero) des GNU-Basisnamens zu verwenden, um eine durch NUL getrennte (anstelle einer durch Zeilenumbrüche getrennten) Ausgabe zu erzeugen.
Toby Speight

11

Eine andere sehr ähnliche Antwort (ich bin überrascht, dass diese bestimmte Variante noch nicht erschienen ist) ist:

ls | sed -n 's/\.png$//p'
  • Sie müssen die -1Option nicht aktivieren ls, da lsdavon ausgegangen wird, dass die Standardausgabe kein Terminal ist (in diesem Fall eine Pipe).
  • Die -nOption sedbedeutet "Die Zeile nicht standardmäßig drucken".
  • Die /pOption am Ende der Ersetzung bedeutet "... und diese Zeile ausdrucken, wenn eine Ersetzung vorgenommen wurde".

Der Nettoeffekt davon besteht darin, nur die Zeilen auszudrucken, die .pngmit dem .pngentfernten enden . Das heißt, dies trägt auch der leichten Verallgemeinerung der Frage des OP Rechnung, bei der das Verzeichnis nicht nur .pngDateien enthält.

Die sed -nTechnik ist häufig in Fällen nützlich, in denen Sie andernfalls grep + sed verwenden könnten.


Mir gefällt, wie sorgfältig Sie Ihre Antwort verfasst haben. Bei dieser Lösung treten Probleme mit Dateinamen einschließlich Zeilenumbrüchen auf. Der erste Teil des Namens wird nicht gedruckt. Noch mehr, wenn es mit der Taste ( .png) vor dem Zeilenumbruchzeichen böser ist: In diesem Fall drucken Sie diesen Teil ohne png und löschen nicht nur den letzten Teil. Es wird oftmals empfohlen, die Ausgabe nicht zu analysieren (und zu leiten), lsda die Probleme nur dort versteckt werden können, wo Sie nicht darüber nachdenken ...
Hastur

2
@Hastur Sie haben im Prinzip Recht, und die berühmte Seite über " Nicht analysieren" listet weitere Probleme (und Lösungen) bei der Übergabe von pathologischen Dateinamen auf. Aber die beste Art, damit umzugehen , besteht darin, keine pathologischen Dateinamen zu haben (doh!). und wenn Sie nicht können, oder wenn Sie müssen gegen sie robust sein, dann entweder nutzen findoder - möglicherweise besser - nutzen Sie eine leistungsfähigere Sprache als shsie zu verwalten (die Tatsache , dass sh kann alles tun , bedeutet nicht , dass es die beste Wahl in jeder Fall). Die Shell ist zunächst auf Benutzerfreundlichkeit ausgelegt.
Norman Gray

Ich stimme im Prinzip der Benutzerfreundlichkeit zu, aber diese Variante schlägt fehl, wenn Sie einen Dateinamen mit jeder neuen Zeile darin haben. Dies kann leicht unbemerkt auftreten, wenn Sie beispielsweise eine Zeile aus einem PDF in eine GUI kopieren und einfügen. Sie denken also nur, dass pathologische Dateinamen vermieden werden .
Hastur

Darüber hinaus ist es einfach, mit dem Parsen zu beginnen ls, aber es gibt Probleme für die Zukunft. Oft machen wir Skripte, die wir später verwenden werden, wenn wir ihr Limit schon vergessen haben ... (es ist menschlich, es ist üblich). Ich schlug ein findBeispiel (mit -execund ohne Pfeife) vor, auch wenn ich der Meinung bin, dass eine bessere (weil reine Schale) Antwort auf die eine , feste und posix-konforme Antwort des Cuonglms ist .
Hastur

@Hastur: diese zukünftigen Probleme werden sowieso auftauchen. Viele Dinge im System sind nicht robust gegen Dateien mit Zeilenumbrüchen. Versuchen Sie es zB mit locateoder make.
Reinierpost

8

Sie können dazu nur BASH-Befehle verwenden (ohne externe Tools).

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

Dies ist nützlich, wenn Sie ohne / usr / bin sind und für Dateinamen wie this.is.image.png und für alle Erweiterungen geeignet sind.


6

Es ist nicht sicher zu analysieren lsoder zu leiten find[ 1 , 2 ]

Es ist nicht sicher, die Ausgabe von lsoder zu analysieren (und zu leiten) find, hauptsächlich, weil es möglich ist, in den Dateinamen nicht übliche Zeichen als Zeilenumbruch zu finden , die Registerkarte ... Hier funktioniert ein reiner Shell-Zyklus [ cuonglm ] .
Auch der findBefehl verrohrt nicht mit der Option -execfunktioniert:

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

Updates / Hinweise : Mit können Sie find .auch nach versteckten Dateien suchen oder find ./*.pngnur nach nicht versteckten. Bei find *.png -exec ...Ihnen kann es zu Problemen kommen, falls eine Datei mit dem Namen " .pngfind" vorhanden war. Diese wird von find als Option abgerufen. Sie können hinzufügen -maxdepth 0, um zu vermeiden, dass in Verzeichnissen mit dem Namen Dir_01.pngoder find ./*.png -prune -exec ...wenn maxdepth nicht zulässig ist (danke Stéphane) , absteigend verfahren wird. Wenn Sie vermeiden möchten, diese Verzeichnisse aufzulisten, sollten Sie die Option hinzufügen -type f(die auch andere Arten von nicht regulären Dateien ausschließt). Werfen Sie einen Blick auf, manum ein umfassenderes Panorama über alle verfügbaren Optionen zu erhalten, und prüfen Sie, ob diese POSIX-kompatibel sind, um die Portabilität zu verbessern.

Noch ein paar Worte

Es kann beispielsweise vorkommen, dass beim Kopieren des Titels aus einem Dokument und Einfügen in den Dateinamen eine oder mehrere Zeilenvorschübe im Dateinamen selbst abgeschlossen werden. Wir können sogar so unglücklich sein, dass ein Titel sogar den Schlüssel enthalten kann, den wir kurz vor einer neuen Zeile verwenden müssen:

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

Wenn Sie testen möchten, können Sie mit den Befehlen solche Dateinamen erstellen

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"

Das einfache /bin/ls *pngwird ?anstelle der nicht druckbaren Zeichen ausgegeben

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

In allen Fällen, in denen Sie die Ausgabe von lsoder findden folgenden Befehl weiterleiten, gibt es keinen Hinweis darauf, ob die aktuelle Zeile aus einem neuen Dateinamen stammt oder einem Zeilenumbruchzeichen im vorhergehenden Dateinamen folgt . Ein böser Name, aber immer noch ein legaler.

Ein Shell-Zyklus mit einer Shell-Parameter-Erweiterung, ${parameter%word}in beiden Varianten mit printfoder echowird funktionieren [ cuonglm ], [ Anthon1 ] .

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

Aus der Manpage der Shell Parameter Expansion [ 3 ]

$ {parameter% word}
$ {parameter %% word}

... das Ergebnis der Erweiterung ist der Wert des Parameters, bei dem das kürzeste Übereinstimmungsmuster (der Fall '%') oder das längste Übereinstimmungsmuster (der Fall '%%') gelöscht wurde.


Auch die Ergebnisse Ihres findBefehls sind ein wenig variabel (zum Beispiel, wenn es ein Verzeichnis namens gibt files.png)

1
Lieber @BroSlow, als ich die obige Antwort schrieb, habe ich 13 (alle) der anderen in diesem Moment vorhandenen Varianten über eine Befehlszeile in einem Skript ausprobiert , das als Argument eines Shell-Aufrufs gestartet wurde. Bitte machen Sie dasselbe und sagen Sie mir, ob sie sich so verhalten, wie Sie es erwarten. Ich habe meine Tests mit bash 4.3.11, dash 0.5.7-4, zsh (wenn nötig) 5.0.2 durchgeführt. Fühlen Sie sich frei, diesen Beitrag zu lesen , der etwas mehr hinzufügt. Ich stimme dem Hinweis zu, die Ausgabe von zu leitenfind , was ich ausdrücklich vorgeschlagen habe-exec , und habe im Titel geschrieben. :-).
Hastur

Lies das Wiki noch einmal. Ich denke immer noch, dass Sie Ihr Beispiel einbauen müssen, da das hier besprochen wird. Und für die meisten modernen Versionen von lsgibt es keinerlei Probleme, wenn die Ausgabe weitergeleitet oder umgeleitet wird, aber wie im Wiki erwähnt, funktioniert dies möglicherweise nicht für alle. Die meisten fügen ?Sonderzeichen nur ein, wenn die Ausgabe an das Terminal gesendet wird. dh tun echo *.png | od -cund ls *.png | od -c. Das Newline-Problem ist kein Problem ls, es ist ein Problem mit jedem Befehl, der nicht auf beiden Seiten der Pipe mit Null endet.

1
printf "${f%.png}\n"ist falsch. Das erste Argument ist das Format, Sie sollten dort keine variablen Daten verwenden. Kann sogar als DoS-Sicherheitslücke angesehen werden (versuchen Sie es beispielsweise mit einer %1000000000s.pngDatei).
Stéphane Chazelas

Sie brauchen find ./*.png -prune -exec...oder Sie hätten Probleme mit Dateinamen beginnend mit -(und Dateien vom Typ Verzeichnis, beachten Sie, dass -maxdepthnicht portierbar ist)
Stéphane Chazelas

4

war es nicht genug

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

oder im Allgemeinen dies

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

entfernt alle Erweiterungen


Es hat aber auch die anderen Lösungen geklappt.
Colin

Ich wollte damit sagen, dass Ihre Frage mit ls -1 begann, also sollte ls -1 das tun. :)
Rohail Abbas

Dies -1ist nicht erforderlich, da dies die Standardeinstellung ist.
Juli

@Rohail Abbas Hat aber nicht jedes System sed installiert?
Colin

1
In der Tat, aber lses geht trotzdem ohne diese Option, wenn sein Ausgang kein Terminal ist, was hier der Fall ist.
Juli

3

Verwendung rev:

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

revkehrt alle Zeichenfolgen (Linien) um; du schneidest alles nach dem ersten '.' und rev kehrt den Rest um.

Wenn du grep'alma' willst :

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

Dies -1ist nicht erforderlich, da dies die Standardeinstellung ist.
Juli

2
Dies scheitert schlecht auf eine Datei mit dem NamenMy.2016.Summer.Vacation.png
David Conrad

@ DavidConrad meine schlechte: / Ich habe korrigiertcut -f 2-
Tom Solid

Jetzt funktioniert es mit dieser Datei, aber noch nicht mit einer Datei mit .pngund einer neuen Zeile gleich danach ... Es wird empfohlen, das Parsen zu vermeiden, lsda es die Überraschungen gut verbergen mag ... :-)
Hastur

2

Wenn ich gewusst hätte, dass das Verzeichnis nur Dateien mit der Erweiterung .png enthält, hätte ich Folgendes ausgeführt: ls | awk -F. '{print $1}'

Dies gibt das erste "Feld" für alles zurück, wo sich eine Dateinamenerweiterung befindet.

Beispiel:

[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

Leider wird es auf alle scheitern die Dateinamen mit mehr als einer ., wie Image.1.pngund auch auf diejenigen , die mit nicht schön Namen , mit Sonderzeichen im Inneren. als Neue - Zeile oder diejenige , die man als (Eingangs-) -Eintrag Separator in der Anwendung wird awk, RS. Es wird empfohlen, das Parsen der lsAusgabe zu vermeiden, da Probleme, die auftreten, wenn Sie sie nicht erwarten, gerne ausgeblendet werden. Sie können mehr in den Referenzen 1 oder 2 lesen . BTW nice die Idee, awk zu verwenden ... Ich habe einige Beispiele in einer Antwort.
Hastur

Bei der von Colin zur Verfügung gestellten Stichprobe würde dies jedoch gut funktionieren. Damit es für den von Ihnen vorgeschlagenen Fall funktioniert, würde ich es wahrscheinlich in [rsingh @ rule51 TESTDIR] $ ls | ändern sed -e 's / .png $ //' 10 1 2 3 4 5 6 7 8 9 harry.the.bunny whats.a.png.filename Ich versuche nicht schwierig zu sein, aber da Colin es braucht, bin ich mir nicht sicher was das Problem wäre ls analysieren.
Rsingh

Entschuldigung ... Ich habe gerade festgestellt, dass ich das Verzeichnis mit den Dateien nicht angezeigt habe, bevor ich die Ausgabe von 'ls' [rsingh @ rule51 TESTDIR] $ ls 10.png 2.png 4.png 6.png 8.png geändert habe 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 du musst dem .in \.in der entkommen sed -e 's/\.png$//', aber so wird es eine Antwort, die gerade geschrieben wurde. :-( note2 Sie können versuchen, es awkmit so etwas wie ls | awk -F. '{if ($NF=="png") {for (i=1;i<NF-1;i++) printf("%s.", $i) ; printf $(NF-1)"\n"}}'... zu verwenden, aber Sie werden immer das Problem haben, dass awk nicht wissen kann, ob die angekommene Zeile einer neuen Zeile im Dateinamen folgt oder nicht. Ich habe versucht, dies in meiner Antwort besser auszudrücken .
Hastur

Danke Hastur, das habe ich verpasst :). Außerdem habe ich in diesem Fall die Verwendung von awk zugunsten von sed aufgegeben.
Rsingh

2

Laut Ihrem Kommentar "Ich habe eine Textdatei, in der alle Namen ohne die Erweiterung aufgeführt sind. Ich möchte ein PHP-Skript erstellen, das die Textdatei mit dem Ordner vergleicht, um festzustellen, welche Datei fehlt":

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

und umgekehrt:

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

Ist die genaueste Methode, die von @roaima hervorgehoben wird. Ohne die entkam \.pngDateien mit dem Namen a_png.pngwürde wie folgt aufgelistet: a_.


mit ls -l, wie Sie es tun, gibt die Datei Details, das ist nicht, was das OP gefragt hat.
Anthon

1

Eine einfache Shell-Zeile (ksh, bash oder zsh; nicht dash):

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

Eine einfache Funktion (von No Extension):

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

Oder eine Funktion, die alle angegebenen Erweiterungen entfernt (standardmäßig png):

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

Benutzen als:

ne jpg

Wenn es sich bei der Ausgabe um ein Sternchen handelt *, ist keine Datei mit dieser Erweiterung vorhanden.


1

Sie können den folgenden Feed probieren, wenn die Ausgabe Ihres Superators das "." Ist. Und da alle Ihre Dateien name.png haben, drucken Sie die erste Spalte:
ls | awk -F"." '{print $1}'


-1

Wenn Sie Zugriff auf sed haben, ist dies besser, da es die letzte Dateierweiterung entfernt, egal was es ist (png, jpg, tiff, etc ...)

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

7
Pausen für Dateinamen wie this.is.a.dotty.txt. Versuchen Sie es s/\.[^.]*$//stattdessen.
Roaima
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.