'ls -1': comment lister les noms de fichiers sans extension


24

ls -1 répertorie mes éléments comme suit:

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

Je veux qu'il soit répertorié sans .pngsimilaire donc:

foo
bar
foobar
...

(le .pngrépertoire ne contient que des fichiers)

Quelqu'un peut-il me dire comment utiliser grepdans ce cas?

Objectif: J'ai un fichier texte où tous les noms sont répertoriés sans l'extension. Je veux faire un script qui compare le fichier texte avec le dossier pour voir quel fichier est manquant.


36
Vous voulez être prudent avec une demande comme celle-ci. Linux n'a pas d'extensions de nom de fichier. Linux a des noms de fichiers qui peuvent ou non inclure un .en eux. Bien que la convention indique de nommer vos fichiers .pngà la fin, il n'y a aucune raison pour laquelle je ne peux pas avoir de fichier png nommé foo.zipou my.picture.20160518ou simplement mypic.
hymie

2
@hymie Je sais, mais mes éléments dans ce dossier sont tous nommés avec .png à la fin.
Colin

14
Qu'est-ce qu'une "extension"? Cela ne fait pas partie de la dénomination des fichiers Unix; c'est le transfert de VMS / NT / Windows. Et vous les jeunes, descendez aussi de ma pelouse. :)
mpez0

28
Ne surestimons pas cela. Le système d'exploitation traite les extensions comme faisant simplement partie du nom du fichier, mais de nombreux programmes Unix y prêtent attention, du compilateur à l'interface graphique. Le concept n'est certainement pas étranger à Unix.
alexis

1
Il est généralement suggéré d' éviter d'analyser la sortie dels et de diriger la sortie de lset find, principalement en raison de la possibilité de générer newline`tab tab dans le nom du fichier. Si le nom de fichier est The new art of working on .png\NEWLINE files and other formatsnombreux, la solution proposée créera des problèmes.
Hastur

Réponses:


41

Vous n'avez besoin que du shell pour ce travail.

POSIX:

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

Avec zsh:

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

4
Il n'y a pas besoin de printf; echo ${f%.png}suffira.
David Conrad

11
@Conrad: utiliser echo ne fonctionnera pas correctement dans certains cas, si le nom de fichier commence par un tiret ou contient des séquences échappées.
cuonglm

3
@DavidConrad: Voir aussi unix.stackexchange.com/a/65819/38906
cuonglm

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

La sedcommande supprime (c'est-à-dire qu'elle remplace par la chaîne vide) toute chaîne .pngtrouvée à la fin d'un nom de fichier.

Le .est échappé de \.sorte qu'il est interprété par sedcomme un .caractère littéral plutôt que l'expression rationnelle .(ce qui signifie correspondre à n'importe quel caractère). L' $ancre est la fin de la ligne, donc elle ne correspond pas .pngau milieu d'un nom de fichier.


4
Je pense que l'OP veut que toute extension soit supprimée, mais probablement seulement la "dernière". Alors peut-être modifier votre bonne réponse autrement avec:sed 's/\.[^.]*$//'
Otheus

1
oui, cette expression rationnelle fonctionnerait dans ce cas ... mais si le PO le veut, il devrait le dire au lieu de dire spécifiquement qu'il "veut qu'il soit listé sans le .png"
cas

4
Le -1n'est pas nécessaire, étant la valeur par défaut ici.
jlliagre

3
@jlliagre Je suis d'accord avec cas que le -1doit être spécifié. Ce n'est que la valeur par défaut lorsque le tuyau est activé, ce qui est une surprise cachée pour certains. Donc, le rendre explicite aide à comprendre. Je le fais également dans mes scripts, donc je sais à quel type de sortie je m'attends.
Otheus

1
Avertissement Dans le cas d'un nom de fichier avec la touche ( .png) devant un caractère de nouvelle ligne, vous effacerez même cela .pnget pas seulement le dernier. Il vaut mieux éviter de piper et d'analyser la sortie de ls, il réserve souvent des surprises bien cachées ... (quelques mots et références plus dans la réponse).
Hastur

16

Si vous souhaitez simplement utiliser bash:

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

Vous devez rechercher greplorsque vous essayez de trouver des correspondances, et non pour supprimer / remplacer cela sedest plus approprié:

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

Une fois que vous avez décidé de créer des sous-répertoires pour mettre de l'ordre dans vos fichiers PNG, vous pouvez facilement changer cela en:

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

ls -1 | sed 's / .png //' fonctionne très bien. Merci!
Colin

La find canalisation de sedsolution peut présenter des problèmes si vous trouvez un fichier avec la clé ( .png) comme une partie du nom et juste avant un caractère de nouvelle ligne. Il vaut mieux éviter de piper et d'analyser la sortie de findou ls, il réserve souvent des surprises bien cachées ... (quelques mots et références plus dans la réponse).
Hastur

Remplacez probablement findpar quelque chose comme echodans le dernier exemple. Le but n'est pas clair findet les résultats dépendent de la structure du répertoire (c'est-à-dire si vous avez un répertoire files.png)

@BroSlow Mis à jour pour quelque chose de plus sensé.
Anthon

13

J'irais pour basename(en supposant la mise en œuvre de GNU):

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

Notez que si vous souhaitez l'utiliser dans un canal, il peut être utile d'utiliser l'option -z(ou --zero) du nom de base GNU pour produire une sortie séparée par NUL (au lieu de séparée par des sauts de ligne).
Toby Speight

11

Une autre réponse très similaire (je suis surpris que cette variante particulière ne soit pas encore apparue) est:

ls | sed -n 's/\.png$//p'
  • Vous n'avez pas besoin de cette -1option ls, car cela lssuppose que si la sortie standard n'est pas un terminal (c'est un canal, dans ce cas).
  • l' -noption sedsignifie «ne pas imprimer la ligne par défaut»
  • l' /poption à la fin de la substitution signifie «... et imprimez cette ligne si une substitution a été effectuée».

L'effet net de cela est d'imprimer uniquement les lignes qui se terminent .pngpar les .pngsupprimées. Autrement dit, cela répond également à la légère généralisation de la question de l'OP, où le répertoire ne contient pas uniquement des .pngfichiers.

La sed -ntechnique est souvent utile dans les cas où vous pourriez autrement utiliser grep + sed.


J'aime la façon dont vous avez pris soin d'écrire votre réponse. Cette solution posera des problèmes avec les noms de fichiers, y compris les sauts de ligne , elle n'imprimera pas la première partie du nom. Encore plus si c'est plus méchant avec la touche ( .png) avant le caractère de nouvelle ligne: dans ce cas, vous imprimerez cette partie sans png, sans effacer uniquement la dernière partie. Il est souvent conseillé d'éviter d'analyser (et de canaliser) la sortie lscar les problèmes peuvent être cachés là où vous ne pensez pas ...
Hastur

2
@Hastur Vous avez raison, en principe, et la célèbre page sur ne pas analyser ls répertorie d'autres problèmes (et solutions) lors de la remise des noms de fichiers pathologiques. Mais la meilleure façon de gérer cela est d' éviter d'avoir des noms de fichiers pathologiques (doh!); et si vous ne pouvez pas, ou si vous devez être robuste contre eux, alors soit utiliser findou - peut - être mieux - utiliser un langage plus puissant que shpour les gérer (le fait que sh peut faire tout ne signifie pas qu'il est le meilleur choix chaque cas). La coque est conçue pour être utilisable en premier.
Norman Gray

Je suis d'accord, en principe, sur l'utilisabilité, mais cette variante échoue lorsque vous avez un nom de fichier avec chaque nouvelle ligne à l'intérieur. Cela peut facilement se passer inaperçu, par exemple, lorsque vous copiez et collez une ligne d'un pdf dans une interface graphique, vous ne pensez donc qu'à éviter les noms de fichiers pathologiques .
Hastur

De plus, à mon humble avis, il est facile de commencer à analyser ls, mais il est en cours de futurs problèmes. Souvent, nous faisons des scripts que nous utiliserons plus tard, quand nous oublierons déjà leur limite ... (c'est humain, c'est habituel). J'ai proposé un findexemple (avec -execet sans pipe) même si je considère une meilleure réponse (car pure shell) à celle du cuonglm , solide et conforme au posix.
Hastur

@Hastur: ces problèmes futurs se poseront de toute façon. Beaucoup de choses dans le système ne sont pas robustes contre les fichiers avec des retours à la ligne. Par exemple, essayez d'utiliser locateou makesur eux.
reinierpost

8

Vous pouvez utiliser uniquement des commandes BASH pour ce faire (sans aucun outil externe).

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

C'est utile lorsque vous êtes sans / usr / bin et fonctionne bien pour les noms de fichiers comme this.is.image.png et pour toutes les extensions.


6

Il n'est pas sûr d'analyser lsou de canaliser find[ 1 , 2 ]

Il n'est pas sûr d'analyser (et de diriger) la sortie de lsou find, principalement parce qu'il est possible de trouver dans les noms de fichiers des caractères non habituels comme la nouvelle ligne , l' onglet ... Ici un cycle de shell pur fonctionnera [ cuonglm ] .
Même la findcommande non canalisée avec l'option -execfonctionnera:

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

Mises à jour / notes : Vous pouvez utiliser find .pour rechercher même les fichiers cachés, ou find ./*.pngpour obtenir uniquement ceux qui ne sont pas cachés. Avec find *.png -exec ...vous pouvez avoir un problème dans le cas où il était présent un fichier nommé .pngcar find l'obtiendra en option. Vous pouvez ajouter -maxdepth 0pour éviter de descendre dans des répertoires nommés comme Dir_01.png, ou find ./*.png -prune -exec ...lorsque maxdepth n'est pas autorisé (merci Stéphane). Si vous souhaitez éviter de répertorier ces répertoires, vous devez ajouter l'option -type f(qui exclurait également d'autres types de fichiers non réguliers). Jetez un œil à la manpour un panorama plus complet sur toutes les options disponibles, et n'oubliez pas de vérifier quand elles sont conformes POSIX, pour une meilleure portabilité.

Quelques mots de plus

Il peut arriver, par exemple, qu'en copiant le titre d'un document et en le collant dans le nom de fichier, une ou plusieurs nouvelles lignes se terminent dans le nom de fichier lui-même. Nous pouvons même être si malchanceux qu'un titre peut contenir même la clé que nous devons utiliser juste avant une nouvelle ligne:

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

Si vous voulez tester, vous pouvez créer des noms de fichiers comme celui-ci avec les commandes

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"

Le simple /bin/ls *pngsortira ?au lieu des caractères non imprimables

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

Dans tous les cas où vous dirigerez la sortie de lsou findla commande suivante n'aura aucune indication pour comprendre si la ligne actuelle provient d'un nouveau nom de fichier ou si elle suit un caractère de nouvelle ligne dans le nom de fichier précédent . Un nom méchant en effet, mais toujours légal.

Un cycle de shell avec une extension de paramètre de shell ,,${parameter%word} dans la variante avec printfou echofonctionnera [ cuonglm ], [ Anthon1 ] .

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

À partir de la page de manuel de l'extension des paramètres du shell [ 3 ]

$ {paramètre% mot}
$ {paramètre %% mot}

... le résultat de l'expansion est la valeur du paramètre avec le modèle de correspondance le plus court (le cas '%') ou le modèle de correspondance le plus long (le cas '%%') supprimé.


De plus, les résultats de votre findcommande sont une variable binaire (par exemple s'il existe un répertoire appelé files.png)

1
Cher @BroSlow, lorsque j'ai écrit la réponse ci-dessus, j'ai essayé 13 (toutes) les autres variantes présentes à ce moment, par ligne de commande, dans un script, lancé comme argument d'une invocation de shell. Veuillez faire de même et dites-moi s'ils se comportent de la manière que vous attendez. J'ai fait mes tests avec bash 4.3.11, dash 0.5.7-4, zsh (si nécessaire) 5.0.2. N'hésitez pas à lire cet article qui ajoute quelque chose de plus. Je suis d'accord sur la note de canalisation de la sortie de find, pour cela j'ai expressément suggéré-exec , et j'ai écrit dans le titre. :-).
Hastur

Relisez le wiki à nouveau. Je pense toujours que vous avez besoin de canaliser dans votre exemple, car c'est ce qui est discuté ici. Et pour la majorité des versions modernes, lsil n'y a aucun problème lorsque la sortie est canalisée ou redirigée, mais comme mentionné dans le wiki, cela peut ne pas fonctionner pour tous. La plupart n'insèrent les ?caractères spéciaux à la place que lorsque la sortie est envoyée au terminal. à savoir faire echo *.png | od -cet ls *.png | od -c. Le problème de nouvelle ligne n'est pas un problème avec ls, c'est un problème avec toute commande qui ne se termine pas nulle des deux côtés du tuyau.

1
printf "${f%.png}\n"est faux. Le premier argument est le format, vous ne devez pas y utiliser de données variables. Peut même être considéré comme une vulnérabilité DoS (essayez avec un %1000000000s.pngfichier par exemple).
Stéphane Chazelas

Vous auriez besoin find ./*.png -prune -exec...ou vous auriez des problèmes avec les noms de fichiers commençant par -(et les fichiers de type répertoire, notez qu'il -maxdepthn'est pas portable)
Stéphane Chazelas

4

n'était-ce pas suffisant?

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

ou en général, ce

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

supprimera toutes les extensions


C'était mais les autres solutions fonctionnent aussi.
Colin

Je voulais dire que votre question a commencé par ls -1, donc ls -1 devrait le faire. :)
Rohail Abbas

Le -1n'est pas nécessaire, étant la valeur par défaut ici.
jlliagre

@ Rohail Abbas Mais tous les systèmes n'ont pas installé sed?
Colin

1
En effet, mais lsil le fait quand même sans cette option lorsque sa sortie n'est pas un terminal, ce qui est le cas ici.
jlliagre

3

Utilisation rev:

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

revinverse toutes les chaînes (lignes); vous coupez tout après le premier "." et rev inverse le reste.

Si vous voulez grep«alma»:

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

Le -1n'est pas nécessaire, étant la valeur par défaut ici.
jlliagre

2
Cela échoue mal sur un fichier nomméMy.2016.Summer.Vacation.png
David Conrad

@DavidConrad mon mauvais: / J'ai corrigécut -f 2-
Tom Solid

Maintenant ça marche avec ce fichier mais pas encore avec un fichier avec .pnget une nouvelle ligne juste après ... Il est conseillé d'éviter d'analyser lscar il aime bien cacher les surprises ... :-)
Hastur

2

Si je savais que le répertoire n'avait que des fichiers avec l'extension .png, j'aurais simplement exécuté: ls | awk -F. '{print $1}'

Cela retournera le premier "champ" pour tout ce qui contient un nom de fichier.extension.

Exemple:

[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

Malheureusement, il échouera sur tous les noms de fichiers avec plus d'un ., comme Image.1.pnget même sur ceux avec des noms pas gentils , avec des caractères spéciaux à l'intérieur. comme le saut de ligne ou celui que vous utiliserez comme séparateur d'enregistrement (d'entrée) dansawk , RS. Il est suggéré d'éviter d'analyser la lssortie car il aime cacher le problème qui surviendra lorsque vous ne vous y attendez pas. Vous pouvez en lire plus dans ces références 1 ou 2 . BTW sympa l'idée d'utiliser awk ... J'ai mis quelques exemples dans une réponse.
Hastur

Certes, cependant, étant donné l'échantillon fourni par Colin, cela fonctionnerait très bien. Pour le faire fonctionner pour le cas que vous avez suggéré, je le changerais probablement en: [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 N'essayant pas d'être difficile, mais étant donné les besoins de Colin, je ne suis pas sûr quel serait le problème en analysant ls.
rsingh

désolé ... Je viens de réaliser que je n'ai pas montré le répertoire avec les fichiers avant de modifier sed la sortie de '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 vous devez échapper .à l' \.intérieur du sed -e 's/\.png$//', mais cela devient donc une réponse qui vient d'être écrite. :-( note2, vous pouvez essayer d'utiliserawk avec quelque chose comme ls | awk -F. '{if ($NF=="png") {for (i=1;i<NF-1;i++) printf("%s.", $i) ; printf $(NF-1)"\n"}}'... mais vous aurez toujours le problème que awk ne peut pas savoir si la ligne qui arrive suit ou non une nouvelle ligne dans le nom du fichier. J'ai essayé de dire mieux dans ma réponse .
Hastur

Merci Hastur, ça m'a manqué :). De plus, j'ai abandonné l'utilisation de awk en faveur de sed dans ce cas.
rsingh

2

selon votre commentaire "J'ai un fichier texte où tous les noms sont listés sans l'extension. Je veux faire un script PHP qui compare le fichier texte avec le dossier pour voir quel fichier est manquant":

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

et l'inverse:

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

Est la méthode la plus précise mise en évidence par @roaima. Sans l'évadé\.png fichiers nommés a_png.pngseraient énumérés comme suit: a_.


l'utilisation ls -l, comme vous le faites, donne les détails du fichier, ce n'est pas ce que l'OP a demandé.
Anthon

1

Une simple ligne de shell (ksh, bash ou zsh; pas un tiret):

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

Une fonction simple (de No Extension):

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

Ou une fonction qui supprime toute extension donnée (png par défaut):

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

Utilisé comme:

ne jpg

Si la sortie est un astérisque *, aucun fichier avec cette extension n'existe.


1

Vous pouvez essayer le flux suivant awk la sortie de ls votre superateur est le "." et puisque tous vos fichiers auront name.png, vous imprimez la première colonne:
ls | awk -F"." '{print $1}'


-1

Si vous avez accès à sed, c'est mieux car il supprimera la dernière extension de fichier, quelle qu'elle soit (png, jpg, tiff, etc ...)

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

7
Pauses pour les noms de fichiers comme this.is.a.dotty.txt. Essayez s/\.[^.]*$//plutôt.
roaima
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.