'ls -1': come elencare i nomi dei file senza estensione


24

ls -1 elenca i miei elementi in questo modo:

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

Lo voglio elencato senza .pngcosì:

foo
bar
foobar
...

(la .pngdirectory contiene solo file)

Qualcuno può dirmi come usare grepin questo caso?

Scopo: ho un file di testo in cui sono elencati tutti i nomi senza estensione. Voglio fare uno script che confronta il file di testo con la cartella per vedere quale file manca.


36
Vuoi stare attento con una richiesta come questa. Linux non ha estensioni di file. Linux ha nomi di file che possono o meno includere un .in loro. Anche se la convenzione dice di nominare i tuoi file .pngalla fine, non c'è motivo per cui non posso avere un file png chiamato foo.zipo my.picture.20160518o solo mypic.
hymie,

2
@hymie lo so, ma i miei elementi in quella cartella sono tutti chiamati con .png alla fine.
Colin,

14
Che cos'è un'estensione? Non fa parte della denominazione dei file Unix; è carryover da VMS / NT / Windows qualunque. E voi giovani scendete anche dal mio prato. :)
mpez0

28
Non esageriamo. Il sistema operativo considera le estensioni semplicemente come parte del nome del file, ma molti programmi unix le prestano attenzione, dal compilatore alla GUI. Il concetto non è certamente estraneo a unix.
alexis,

1
Di solito si suggerisce di evitare di analizzare l'output dils e di pipe l'output di lse find, principalmente perché la possibilità di incorrere in newline`tab char nel nome del file. Se il nome file è The new art of working on .png\NEWLINE files and other formatsmolte delle soluzioni proposte creerà problemi.
Hastur,

Risposte:


41

Hai solo bisogno della shell per questo lavoro.

POSIXly:

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

Con zsh:

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

4
Non è necessario printf; echo ${f%.png}sarà sufficiente.
David Conrad,

11
@Conrad: l'uso dell'eco non funzionerà correttamente in alcuni casi, se il nome del file inizia con un trattino o contiene sequenze di escape.
cuonglm

3
@DavidConrad: vedi anche unix.stackexchange.com/a/65819/38906
cuonglm

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

Il sedcomando rimuove (ovvero sostituisce con la stringa vuota) qualsiasi stringa.png trovata alla fine di un nome file.

Il .fuggiasco come \.in modo che sia interpretato da sedun letterale .carattere piuttosto che l'espressione regolare .(che corrispondono mezzi qualsiasi carattere). Il $è l'ancora di fine linea, in modo che non corrisponde .pngnel bel mezzo di un nome di file.


4
Penso che l'OP voglia eliminare qualsiasi estensione, ma probabilmente solo "l'ultimo". Quindi forse modifica la tua risposta altrimenti buona con:sed 's/\.[^.]*$//'
Otheus,

1
sì, quel regexp funzionerebbe in quel caso ... ma se l'OP lo vuole, dovrebbero dirlo invece di dire espressamente che "lo vogliono elencato senza il .png"
cas

4
Non -1è necessario, essendo qui l'impostazione predefinita.
jlliagre,

3
@jlliagre Concordo con cas sul fatto che -1dovrebbe essere specificato. È solo l'impostazione predefinita quando viene attivata la pipe, che è una sorpresa nascosta per alcuni. Quindi renderlo esplicito aiuta a capire. Lo faccio anche nei miei script, quindi so che tipo di output mi aspetto.
Otheus,

1
Avvertenza Nel caso di un nome file con la chiave ( .png) prima di un carattere di nuova riga, si cancellerà anche quello .pnge non solo l'ultimo. È meglio evitare di convogliare e analizzare l'output di ls, riserva spesso sorprese ben nascoste ... (alcune parole e riferimenti più nella risposta).
Hastur,

16

Se vuoi solo usare bash:

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

Si dovrebbe grepcercare quando si cerca di trovare corrispondenze, non per rimuovere / sostituire quello che sedè più appropriato:

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

Una volta deciso che è necessario creare alcune sottodirectory per mettere ordine nei file PNG, è possibile modificarlo facilmente in:

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

ls -1 | sed 's / .png //' funziona alla grande. Grazie!
Colin,

Il find piping alla sedsoluzione può presentare alcuni problemi se trovi un file con key ( .png) come parte del nome e appena prima di un carattere di nuova riga. È meglio evitare di convogliare e analizzare l'output di findo ls, riserva spesso sorprese ben nascoste ... (alcune parole e riferimenti più nella risposta).
Hastur,

Probabilmente sostituirlo findcon qualcosa come echonell'ultimo esempio. Non è chiaro quale scopo findserve lì e risultati dipendono da struttura di directory (ad esempio se si dispone di una directory files.png)

@BroSlow Aggiornato a qualcosa di più sensato.
Anthon,

13

Vorrei basename(supponendo l'implementazione GNU):

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

Nota che se vuoi usarlo in una pipe, potresti trovare utile usare l' opzione -z(o --zero) di GNU basename per produrre un output separato da NUL (anziché separato da newline).
Toby Speight,

11

Un'altra risposta molto simile (sono sorpreso che questa particolare variante non sia ancora apparsa) è:

ls | sed -n 's/\.png$//p'
  • Non è necessaria l' -1opzione ls, poiché lspresuppone che se l'output standard non è un terminale (in questo caso è una pipe).
  • l' -nopzione sedsignifica "non stampare la linea per impostazione predefinita"
  • l' /popzione alla fine della sostituzione significa "... e stampa questa riga se è stata effettuata una sostituzione".

L'effetto netto è quello di stampare solo quelle righe che finiscono .png, con la .pngrimozione. Cioè, questo si rivolge anche alla leggera generalizzazione della domanda del PO, in cui la directory non contiene solo .pngfile.

La sed -ntecnica è spesso utile nei casi in cui altrimenti potresti usare grep + sed.


Mi piace come le cure hai usato per scrivere la tua risposta. Questa soluzione presenterà problemi con i nomi dei file, compresi i newline , non stamperà la prima parte del nome. Ancora di più se è più cattivo con il tasto ( .png) prima del carattere newline: in tal caso stamperai quella parte senza png, non cancellando solo l'ultima parte. Spesso viene suggerito di evitare di analizzare (e convogliare) l'output di lsperché i problemi possono essere nascosti proprio dove non si sta pensando ...
Hastur,

2
@Hastur Hai ragione, in linea di principio, e la famosa pagina di non analizzare elenca ulteriori problemi (e soluzioni) quando si passano nomi di file patologici. Ma il modo migliore di gestire è quello di evitare di avere nomi di file patologici (doh!); e se non puoi, o se devi essere forte contro di loro, allora usa findo - forse meglio - usa un linguaggio più potente che shgestirli (il fatto che sh può fare tutto non significa che sia la scelta migliore in ogni caso). La shell è stata progettata per essere prima utilizzabile.
Norman Gray,

Concordo, in linea di principio, sull'usabilità, ma questa variante fallisce quando si dispone di un nome file con ogni nuova riga all'interno. Questo può facilmente accadere inosservato, ad esempio, quando copi e incolli una linea da un pdf in una GUI, quindi pensi solo di essere evitato dai nomi di file patologici .
Hastur,

Inoltre, IMHO È facile iniziare ad analizzare ls, ma è alla base di problemi futuri. Spesso realizziamo degli script che useremo in seguito, quando dimenticheremo già il loro limite ... (è umano, è normale). Ho proposto un findesempio (con -exece senza pipe) anche se ritengo una risposta migliore (perché pure shell) a quella del cuonglm , solida e posix conforme.
Hastur,

@Hastur: quei problemi futuri sorgeranno comunque. Molte cose nel sistema non sono robuste rispetto ai file con newline. Ad esempio, prova a usarli locateo makesu di essi.
reinierpost,

8

È possibile utilizzare solo i comandi BASH per farlo (senza strumenti esterni).

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

Questo è utile quando sei senza / usr / bin e funziona bene con nomi di file come this.is.image.png e per tutte le estensioni.


6

Non è sicuro analizzare lso eseguire il pipe find[ 1 , 2 ]

Non è sicuro analizzare (e reindirizzare) l'output di lso find, principalmente perché è possibile trovare nei nomi dei file caratteri non usuali come la nuova riga , la scheda ... Qui funzionerà un ciclo di shell puro [ cuonglm ] .
Anche il findcomando non convogliato con l'opzione -execfunzionerà:

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

Aggiornamenti / Note : è possibile utilizzare find .per cercare anche i file nascosti o find ./*.pngper ottenere solo quelli non nascosti. Con find *.png -exec ...te puoi avere problemi nel caso fosse presente un file chiamato .pngperché find lo otterrà come opzione. È possibile aggiungere -maxdepth 0per evitare di scendere nelle directory denominate come Dir_01.pngo find ./*.png -prune -exec ...quando maxdepth non è consentito (grazie a Stéphane). Se si desidera evitare di elencare quelle directory, è necessario aggiungere l'opzione -type f(che escluderebbe anche altri tipi di file non regolari). Dai un'occhiata a manper un panorama più completo su tutte le opzioni disponibili e ricordati di controllare quando sono conformi a POSIX, per una migliore portabilità.

Qualche parola in più

Può succedere, ad esempio, che copiando il titolo da un documento e incollandolo nel nome file, una o più newline finiscano nel nome file stesso. Possiamo anche essere così sfortunati che un titolo può contenere anche la chiave che dobbiamo usare appena prima di una nuova riga:

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

Se vuoi testare, puoi creare nomi di file come questo con i comandi

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"

L' /bin/ls *pngoutput semplice ?invece dei caratteri non stampabili

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

In tutti i casi in cui si vuole inviare l'output del lso findil seguente comando dovranno nessun accenno per capire se l'attuale linea deriva da un nuovo nome di file o se si segue una nuova riga carattere nel precedente nome del file . Un cattivo nome davvero , ma ancora legale.

Un ciclo di shell con shell Parameter-Expansion, ${parameter%word}sia nella variante con printfo echofunzionerà [ cuonglm ], [ Anthon1 ] .

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

Dalla pagina man di Shell Parameter Expansion [ 3 ]

$ {parametro% word}
$ {parametro %% word}

... il risultato dell'espansione è il valore del parametro con il modello di corrispondenza più breve (il caso '%') o il modello di corrispondenza più lungo (il caso '%%') eliminato.


Anche i risultati del tuo findcomando sono un po 'variabili (ad esempio se c'è una directory chiamata files.png)

1
Caro @BroSlow, quando ho scritto la risposta sopra ho provato 13 (tutte) le altre varianti presenti in quel momento, dalla riga di comando, in uno script, lanciato come argomento di una chiamata alla shell. Per favore, fai lo stesso e dimmi se si comportano come ti aspetti. Ho fatto i miei test con bash 4.3.11, trattino 0.5.7-4, zsh (quando necessario) 5.0.2. Sentiti libero di leggere questo post che aggiunge qualcosa in più. Sono d'accordo sulla nota di convogliare l'output di find, per questo ho espressamente suggerito-exec , e ho scritto nel titolo. :-).
Hastur,

Rileggi di nuovo il wiki. Continuo a pensare che tu debba inserire il tuo esempio, dato che è quello che stiamo discutendo qui. E per la maggior parte delle versioni moderne lsnon c'è alcun problema quando l'output viene reindirizzato o reindirizzato, ma come menzionato nel wiki potrebbe non funzionare per tutti. La maggior parte inserirà il ?posto al posto di caratteri speciali quando l'output viene inviato al terminale. cioè fare echo *.png | od -ce ls *.png | od -c. Il problema newline non è un problema ls, è un problema con qualsiasi comando che non termina con null su entrambi i lati della pipe.

1
printf "${f%.png}\n"è sbagliato. Il primo argomento è il formato, non dovresti usare dati variabili lì dentro. Può anche essere visto come una vulnerabilità DoS ( %1000000000s.pngad esempio, provare con un file).
Stéphane Chazelas,

Avresti bisogno find ./*.png -prune -exec...o avresti problemi con nomi di file che iniziano con -(e file di tipo directory, nota che -maxdepthnon è portatile)
Stéphane Chazelas

4

non era abbastanza?

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

o in generale, questo

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

rimuoverà tutte le estensioni


Lo era, ma anche le altre soluzioni funzionano.
Colin,

Volevo dire che la tua domanda è iniziata con ls -1, quindi ls -1 dovrebbe farlo. :)
Rohail Abbas,

Non -1è necessario, essendo qui l'impostazione predefinita.
jlliagre,

@Rohail Abbas Ma non tutti i sistemi hanno installato sed?
Colin,

1
Anzi, ma lo lsfa comunque senza quell'opzione quando il suo output non è un terminale, come in questo caso.
jlliagre,

3

Utilizzare rev:

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

revinverte tutte le stringhe (linee); hai tagliato tutto dopo il primo '.' e rev ripristina il resto.

Se vuoi grep"alma":

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

Non -1è necessario, essendo qui l'impostazione predefinita.
jlliagre,

2
Questo fallisce gravemente su un file chiamatoMy.2016.Summer.Vacation.png
David Conrad,

@DavidConrad my bad: / Ho corretto acut -f 2-
Tom Solid,

Ora funziona con quel file ma non ancora con un file con .pnge una nuova riga subito dopo ... Si suggerisce di evitare di analizzare lsperché ama nascondere bene le sorprese ... :-)
Hastur

2

Se avessi saputo che la directory aveva solo file con estensione .png come estensione, avrei appena eseguito: ls | awk -F. '{print $1}'

Ciò restituirà il primo "campo" per qualsiasi cosa in cui sia presente un nome file. Estensione.

Esempio:

[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

Sfortunatamente fallirà su tutti i nomi di file con più di uno ., come Image.1.pnge anche su quelli con nomi non carini , con caratteri speciali all'interno. come il ritorno a capo o quello che si intende utilizzare come (input) separatore di record in awk, RS. Si consiglia di evitare di analizzare l' lsoutput perché ama nascondere il problema che sorgerà quando non ci si aspetta. Puoi leggere di più in quei riferimenti 1 o 2 . A proposito bella l'idea di usare awk ... ho messo alcuni esempi in una risposta.
Hastur,

È vero, tuttavia, dato il campione fornito da Colin, funzionerebbe perfettamente. Per farlo funzionare nel caso da te suggerito, probabilmente lo cambierei in: [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 Non cercando di essere difficile, ma dato il bisogno di Colin, non sono sicuro quale sarebbe il problema analizzando ls.
Rsingh,

scusa ... mi sono appena reso conto di non aver mostrato la directory con i file prima di modificare l'output di '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

Nota 1 è necessario per sfuggire alla .nella \.all'interno del sed -e 's/\.png$//', ma così diventa una risposta appena scritto. :-( note2 puoi provare a usare awkqualcosa come ls | awk -F. '{if ($NF=="png") {for (i=1;i<NF-1;i++) printf("%s.", $i) ; printf $(NF-1)"\n"}}'... ma avrai sempre il problema che awk non può sapere se la linea sta arrivando sta seguendo o meno una nuova riga all'interno del nome del file. Ho provato a dire meglio nella mia risposta .
Hastur

Grazie Hastur, mi sono perso :). Inoltre, ho abbandonato l'uso di awk in favore di sed in questo caso.
Rsingh,

2

secondo il tuo commento "Ho un file di testo in cui sono elencati tutti i nomi senza estensione. Voglio creare uno script PHP che confronta il file di testo con la cartella per vedere quale file manca":

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

e il contrario:

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

È il metodo più preciso come evidenziato da @roaima. Senza i fuoriusciti \.pngfile denominati a_png.pngsarebbero elencati come: a_.


usando ls -l, come si fa, si forniscono i dettagli del file, non è quello che l'OP ha chiesto.
Anthon,

1

Una semplice linea di shell (ksh, bash o zsh; non trattino):

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

Una semplice funzione (da No Extension):

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

O una funzione che rimuove qualsiasi estensione fornita (png di default):

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

Usare come:

ne jpg

Se l'output è un asterisco *, non esiste alcun file con tale estensione.


1

Puoi provare il seguente feed awk l'output di ls il tuo superatore è il "." e poiché tutti i tuoi file avranno name.png stamperai la prima colonna:
ls | awk -F"." '{print $1}'


-1

Se hai accesso a sed, è meglio in quanto rimuoverà l'ultima estensione del file, non importa quale sia (png, jpg, tiff, ecc ...)

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

7
Pause per nomi di file come this.is.a.dotty.txt. Prova s/\.[^.]*$//invece.
roaima,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.