'ls -1': cómo listar nombres de archivo sin extensión

24

ls -1 enumera mis elementos así:

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

Quiero que aparezca en la lista sin los siguientes .png:

foo
bar
foobar
...

(el directorio solo contiene .pngarchivos)

¿Alguien puede decirme cómo usar grepen este caso?

Propósito: Tengo un archivo de texto donde se enumeran todos los nombres sin la extensión. Quiero hacer un script que compare el archivo de texto con la carpeta para ver qué archivo falta.

Colin
fuente
36
Desea tener cuidado con una solicitud como esta. Linux no tiene extensiones de nombre de archivo. Linux tiene nombres de archivo que pueden incluir o no a .en ellos. Aunque la convención dice para nombrar sus archivos con .pngal final, no hay ninguna razón por qué no puedo tener un archivo PNG nombrado foo.zipo my.picture.20160518o simplemente mypic.
hymie
2
@hymie Lo sé, pero mis elementos en esa carpeta se nombran con .png al final.
Colin
14
¿Qué es una "extensión"? Eso no es parte de la denominación de archivos Unix; es transferencia de VMS / NT / Windows lo que sea. Y ustedes, jóvenes, salgan de mi césped también. :)
mpez0
28
No exageremos esto. El sistema operativo trata las extensiones simplemente como parte del nombre del archivo, pero muchos programas de Unix les prestan atención, desde el compilador hasta la GUI. El concepto ciertamente no es ajeno a Unix.
alexis
1
Por lo general, se sugiere evitar analizar la salidals y canalizar la salida de , lsy findprincipalmente debido a la posibilidad de incurrir en newline`tab char en el nombre del archivo. Si el nombre de archivo es The new art of working on .png\NEWLINE files and other formatsmuchas de las soluciones propuestas creará problemas.
Hastur

Respuestas:

41

Solo necesita el shell para este trabajo.

POSIXY:

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

Con zsh:

print -rl -- *.png(:r)
Cuonglm
fuente
44
No hay necesidad de printf; echo ${f%.png}Será suficiente.
David Conrad
11
@Conrad: el uso de echo no funcionará correctamente en algunos casos, si el nombre del archivo comienza con un guión o contiene secuencias escapadas.
Cuonglm
3
@DavidConrad: Ver también unix.stackexchange.com/a/65819/38906
cuonglm
35
ls -1 | sed -e 's/\.png$//'

El sedcomando elimina (es decir, reemplaza con la cadena vacía) cualquier cadena que se .pngencuentre al final de un nombre de archivo.

Se .escapa como \.para que se interprete sedcomo un .carácter literal en lugar de la expresión regular .(lo que significa que coincide con cualquier carácter). El $es el ancla de fin de línea, por lo que no coincide .pngen el medio de un nombre de archivo.

cas
fuente
44
Creo que el OP quiere eliminar cualquier extensión, pero probablemente solo la "última". Así que tal vez modificar de otro modo con buena respuesta:sed 's/\.[^.]*$//'
Otheus
1
sí, esa expresión regular funcionaría en ese caso ... pero si el OP quiere eso, deberían decirlo en lugar de decir específicamente que "quieren que aparezca sin el .png"
cas
44
El -1no es necesario, siendo el predeterminado aquí.
jlliagre
3
@jlliagre Estoy de acuerdo con cas en que -1debe especificarse. Es solo el valor predeterminado cuando la tubería está encendida, lo cual es una sorpresa oculta para algunos. Entonces hacerlo explícito ayuda a la comprensión. También hago esto en mis scripts para saber qué tipo de salida estoy esperando.
Oteo
1
Advertencia En el caso de un nombre de archivo con la tecla ( .png) antes de un carácter de nueva línea, borrará incluso eso .pngy no solo el último. Es mejor evitar canalizar y analizar la salida de ls, reserva sorpresas a menudo bien ocultas ... (algunas palabras y referencias más en la respuesta).
Hastur
16

Si solo quieres usar bash:

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

Debe buscar grepcuando intente encontrar coincidencias, no para eliminar / sustituir eso sedes más apropiado:

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

Una vez que decida que necesita crear algunos subdirectorios para poner orden en sus archivos PNG, puede cambiarlo fácilmente a:

find . -name "*.png"  | sed 's/\.png$//'
Anthon
fuente
ls -1 | sed 's / .png //' funciona muy bien. ¡Gracias!
Colin
El find hilo de sedsolución puede presentar algunos problemas si encuentra un archivo con la tecla ( .png) como parte del nombre y justo antes de un carácter de nueva línea. Es mejor evitar canalizar y analizar la salida de findo ls, reserva sorpresas a menudo bien escondidas ... (algunas palabras y referencias más en la respuesta).
Hastur
Probablemente reemplace findcon algo como echoen el último ejemplo. No está claro qué propósito findsirve allí y los resultados dependen de la estructura del directorio (es decir, si tiene un directorio files.png)
@BroSlow Actualizado a algo más sensato.
Anthon
13

Yo iría por basename(asumiendo la implementación de GNU):

basename --suffix=.png -- *.png
hennr
fuente
Tenga en cuenta que si desea usarlo en una tubería, puede resultarle útil usar la opción -z(o --zero) de GNU basename para producir una salida separada por NUL (en lugar de nueva línea).
Toby Speight
11

Otra respuesta muy similar (me sorprende que esta variante en particular aún no haya aparecido) es:

ls | sed -n 's/\.png$//p'
  • No necesita la -1opción ls, ya lsque supone que si la salida estándar no es una terminal (es una tubería, en este caso).
  • la -nopción a sedsignifica 'no imprimir la línea por defecto'
  • la /popción al final de la sustitución significa '... e imprima esta línea si se realizó una sustitución'.

El efecto neto de eso es imprimir solo aquellas líneas que terminan en .png, con el .pngeliminado. Es decir, esto también atiende a la leve generalización de la pregunta del OP, donde el directorio no contiene solo .pngarchivos.

La sed -ntécnica a menudo es útil en casos en los que de otro modo podría usar grep + sed.

Gris normando
fuente
Me gusta cómo la atención que solía escribir su respuesta. Esta solución presentará problemas con los nombres de archivo, incluidas las nuevas líneas , no imprimirá la primera parte del nombre. Aún más si es más desagradable con la tecla ( .png) antes del carácter de nueva línea: en ese caso, imprimirá esa parte sin png, sin borrar solo la última parte. A menudo se sugiere evitar analizar (y canalizar) la salida lsporque los problemas pueden ocultarse justo donde no estás pensando ...
Hastur
2
@Hastur Tienes razón, en principio, y la famosa página sobre no analizar ls enumera más problemas (y soluciones) al entregar nombres de archivos patológicos. Pero la mejor manera de manejar eso es evitar tener nombres de archivo patológicos (¡doh!); y si no puede, o si debe ser robusto contra ellos, entonces use findo, posiblemente mejor, use un lenguaje más poderoso que shpara administrarlos (el hecho de que sh pueda hacer todo no significa que sea la mejor opción en cada caso). La carcasa está diseñada para usabilidad primero.
Norman Gray
Estoy de acuerdo, en principio, sobre la usabilidad, pero esta variante falla cuando tienes un nombre de archivo con cada nueva línea dentro. Esto puede pasar desapercibido fácilmente, por ejemplo, cuando copia y pega una línea desde un pdf en una GUI, por lo que solo cree que debe evitarse los nombres de archivos patológicos .
Hastur
Además, en mi humilde opinión , es fácil comenzar a analizar ls, pero se trata de problemas futuros. A menudo creamos scripts que usaremos más tarde, cuando ya olvidaremos su límite ... (es humano, es habitual). I propuesto un findejemplo (con -execy sin tubo), incluso si considero una mejor (debido shell puro) responde a la de uno cuonglm , sólida y posix compatible.
Hastur
@Hastur: esos problemas futuros surgirán de todos modos. Muchas cosas en el sistema no son robustas contra archivos con nuevas líneas. Por ejemplo, intente usarlos locateo makesobre ellos.
reinierpost
8

Puede usar solo comandos BASH para hacer eso (sin herramientas externas).

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

Esto es útil cuando no tiene / usr / bin y funciona bien para nombres de archivo como this.is.image.png y para todas las extensiones.

Luciano Andress Martini
fuente
6

No es seguro analizar lso canalizar find[ 1 , 2 ]

No es seguro analizar (y canalizar) la salida de lso find, principalmente porque es posible encontrar en los nombres de archivo caracteres no habituales como la nueva línea , la pestaña ... Aquí funcionará un ciclo de shell puro [ cuonglm ] .
Incluso el findcomando no conectado con la opción -execfuncionará:

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

Actualizaciones / Notas : puede usar find .para buscar incluso los archivos ocultos, o find ./*.pngpara obtener solo los no ocultos. Con find *.png -exec ...usted puede tener un problema en el caso de que estuviera presente un archivo llamado .pngporque find lo obtendrá como una opción. Puede agregar -maxdepth 0para evitar descender en directorios nombrados como Dir_01.png, o find ./*.png -prune -exec ...cuando maxdepth no está permitido (gracias Stéphane). Si desea evitar enumerar esos directorios, debe agregar la opción -type f(que también excluiría otros tipos de archivos no regulares). Eche un vistazo al manpara obtener un panorama más completo sobre todas las opciones disponibles, y recuerde verificar cuándo son compatibles con POSIX, para una mejor portabilidad.

Algunas palabras mas

Puede suceder, por ejemplo, que al copiar el título de un documento y pegarlo en el nombre del archivo, una o más líneas nuevas terminen en el nombre del archivo. Podemos ser tan desafortunados que un título puede contener incluso la clave que tenemos que usar justo antes de una nueva línea:

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

Si desea probar, puede crear nombres de archivo como este con los comandos

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"

El simple /bin/ls *pngsaldrá en ?lugar de los caracteres no imprimibles

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

En todos los casos en los que se quiere canalizar la salida del lso findel comando siguiente tendrán ningún indicio de entender si la actual línea proviene de un nuevo nombre de archivo o si se sigue una nueva línea carácter en el precedente nombre de archivo . Un nombre desagradable en verdad, pero aún legal.

Un ciclo de shell con un parámetro Parameter-Expansion, ${parameter%word}en la variante con printfo echofuncionará [ cuonglm ], [ Anthon1 ] .

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

Desde la página del manual de la expansión de parámetros de Shell [ 3 ]

$ {parámetro% palabra}
$ {parámetro %% palabra}

... el resultado de la expansión es el valor del parámetro con el patrón de coincidencia más corto (el caso '%') o el patrón de coincidencia más largo (el caso '%%') eliminado.

Hastur
fuente
Además, los resultados de su findcomando son una variable de bit (por ejemplo, si hay un directorio llamado files.png)
1
Estimado @BroSlow, cuando escribí la respuesta anterior probé 13 (todas) las otras variantes presentes en ese momento, por línea de comando, en un script, lanzado como argumento de una invocación de shell. Haga lo mismo y dígame si se comportan de la manera que espera. Hice mis pruebas con bash 4.3.11, dash 0.5.7-4, zsh (cuando sea necesario) 5.0.2. Siéntase libre de leer esta publicación que agrega algo más. Estoy de acuerdo con la nota de canalizar la salida de find, para esto sugerí expresamente-exec , y escribí en el título. :-).
Hastur
Vuelve a leer el wiki nuevamente. Todavía creo que necesita canalizar su ejemplo, ya que eso es lo que se está discutiendo aquí. Y para la mayoría de las versiones modernas lsno hay ningún problema cuando la salida se canaliza o se redirige, pero como se menciona en wiki, puede que no funcione para todos. La mayoría solo insertará los ?caracteres especiales en lugar de cuando la salida se envíe al terminal. es decir, hacer echo *.png | od -cy ls *.png | od -c. El problema de la nueva línea no es un problema ls, es un problema con cualquier comando que no termina nula en ambos lados de la tubería.
1
printf "${f%.png}\n"Está Mal. El primer argumento es el formato, no debe usar datos variables allí. Incluso puede verse como una vulnerabilidad DoS ( %1000000000s.pngpor ejemplo, intente con un archivo).
Stéphane Chazelas
Necesitaría find ./*.png -prune -exec...o tendría problemas con los nombres de archivo que comienzan con -(y los archivos de tipo directorio, tenga en cuenta que -maxdepthno es portátil)
Stéphane Chazelas
4

no fue suficiente?

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

o en general, esto

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

eliminará todas las extensiones

Rohail Abbas
fuente
Lo fue, pero las otras soluciones también funcionan.
Colin
Quise decir que tu pregunta comenzó con ls -1, por lo que ls -1 debería hacer eso. :)
Rohail Abbas
El -1no es necesario, siendo el predeterminado aquí.
jlliagre
@Rohail Abbas ¿Pero no todos los sistemas se han instalado?
Colin
1
De hecho, pero lo lshace de todos modos sin esa opción cuando su salida no es una terminal, que es el caso aquí.
jlliagre
3

Uso rev:

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

revinvierte todas las cadenas (líneas); cortas todo después del primer '.' y rev revierte el remanente.

Si quieres grep'alma':

ls -1 | rev | cut -f 2- -d "." | rev | grep 'alma'
Tom Solid
fuente
El -1no es necesario, siendo el predeterminado aquí.
jlliagre
2
Esto falla gravemente en un archivo llamadoMy.2016.Summer.Vacation.png
David Conrad
@DavidConrad mi mal: / Me he corregido acut -f 2-
Tom Solid
Ahora funciona con ese archivo pero aún no con un archivo .pngy una nueva línea justo después ... Se sugiere evitar analizar lsporque le encanta ocultar bien las sorpresas ... :-)
Hastur
2

Si supiera que el directorio solo tiene archivos con .png como extensión, simplemente habría ejecutado: ls | awk -F. '{print $1}'

Esto devolverá el primer "campo" para cualquier cosa donde haya un nombre de archivo. Extensión.

Ejemplo:

[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
rsingh
fuente
Desafortunadamente, fallará en todos los nombres de archivo con más de uno ., Image.1.pnge incluso en los que no tienen nombres agradables , con caracteres especiales dentro. como el salto de línea o el que va a utilizar como (entrada) separador de registro en awk, RS. Se sugiere evitar analizar la lssalida porque le encanta ocultar el problema que surgirá cuando no lo espere. Puede leer más en las referencias 1 o 2 . Por cierto, es agradable la idea de usar awk ... Puse algunos ejemplos en una respuesta.
Hastur
Es cierto, sin embargo, dada la muestra proporcionada por Colin, funcionaría bien. Para que funcione para el caso que sugirió, probablemente lo cambiaría a: [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 No intento ser difícil, pero dada la necesidad de Colin, no estoy seguro cuál sería el problema analizando ls.
rsingh
lo siento ... Me acabo de dar cuenta de que no mostraba el directorio con los archivos antes de modificar la salida 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
nota1 que necesita para escapar de la .en \.el interior de la sed -e 's/\.png$//', pero por lo que se convierte en una respuesta acaba de escribir. :-( nota2 puede intentar usar awkcon algo como ls | awk -F. '{if ($NF=="png") {for (i=1;i<NF-1;i++) printf("%s.", $i) ; printf $(NF-1)"\n"}}'... pero siempre tendrá el problema de que awk no puede saber si la línea que está llegando sigue o no una nueva línea dentro del nombre del archivo. Intenté decir mejor en mi respuesta .
Hastur
Gracias Hastur, me perdí eso :). Además, abandoné el uso de awk a favor de sed en este caso.
rsingh
2

de acuerdo con su comentario "Tengo un archivo de texto donde se enumeran todos los nombres sin la extensión. Quiero hacer un script PHP que compare el archivo de texto con la carpeta para ver qué archivo falta":

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

y al revés:

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)
Olivier Dulac
fuente
1

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

Es el método más preciso como lo destaca @roaima. Sin los escapados \.pngarchivos con el nombre a_png.pngsería catalogado como: a_.

alabar
fuente
utilizando ls -l, como lo hace, da los detalles del archivo, eso no es lo que el OP preguntó.
Anthon
1

Una línea de shell simple (ksh, bash o zsh; no dash):

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

Una función simple (desde Sin extensión):

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

O una función que elimina cualquier extensión dada (png por defecto):

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

Usar como:

ne jpg

Si la salida es un asterisco *, no existe ningún archivo con esa extensión.


fuente
1

Puede probar el siguiente feed awk la salida de ls su superator es el "." y como todos tus archivos tendrán nombre.png imprimes la primera columna:
ls | awk -F"." '{print $1}'

igiannak
fuente
-1

Si tiene acceso a sed, esto es mejor ya que eliminará la última extensión de archivo, sin importar cuál sea (png, jpg, tiff, etc.)

ls | sed -e 's/\..*$//'
Hristo Mohamed
fuente
77
Saltos para nombres de archivo como this.is.a.dotty.txt. Intenta en su s/\.[^.]*$//lugar.
roaima