'ls -1': como listar nomes de arquivos sem extensão


24

ls -1 lista meus elementos assim:

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

Quero listado sem o seguinte .png:

foo
bar
foobar
...

(o dir contém apenas .pngarquivos)

Alguém pode me dizer como usar grepneste caso?

Objetivo: Eu tenho um arquivo de texto onde todos os nomes estão listados sem a extensão. Quero criar um script que compare o arquivo de texto com a pasta para ver qual arquivo está faltando.


36
Você quer ter cuidado com uma solicitação como esta. O Linux não possui extensões de nome de arquivo. O Linux possui nomes de arquivos que podem ou não incluir um .neles. Embora a convenção diz para nomear seus arquivos com .png, no final, não há nenhuma razão para que eu não posso ter um arquivo PNG chamado foo.zipou my.picture.20160518ou apenas mypic.
Hymie #

2
@ hymie eu sei, mas meus elementos nessa pasta são todos nomeados com .png no final.
Colin

14
O que é uma "extensão"? Isso não faz parte da nomeação de arquivos Unix; é transferência de VMS / NT / Windows, qualquer que seja. E vocês jovens também saem do meu gramado. :)
mpez0

28
Não vamos exagerar isso. O sistema operacional trata as extensões simplesmente como parte do nome do arquivo, mas muitos programas unix prestam atenção a elas, do compilador à GUI. O conceito certamente não é estranho ao unix.
Alexis

1
Geralmente, é sugerido evitar analisar a saídals e canalizar a saída de , lse findprincipalmente devido à possibilidade de incorrer em newline, `tab char no nome do arquivo. Se o nome do arquivo for The new art of working on .png\NEWLINE files and other formatsmuitas, a solução proposta criará problemas.
Hastur

Respostas:


41

Você só precisa do shell para este trabalho.

POSIXly:

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

Com zsh:

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

4
Não há necessidade printf; echo ${f%.png}será suficiente.
David Conrad

11
@Conrad: usar eco não funcionará corretamente em alguns casos, se o nome do arquivo começar com traço ou contiver seqüências de escape.
cuonglm


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

O sedcomando remove (ou seja, substitui pela sequência vazia) qualquer sequência .pngencontrada no final de um nome de arquivo.

O .é escapado da \.maneira que é interpretado por sedum .caractere literal , e não pela expressão regular .(que significa corresponder a qualquer caractere). O $é a âncora de fim de linha, portanto não corresponde .pngao meio de um nome de arquivo.


4
Eu acho que o OP quer qualquer extensão retirada, mas provavelmente apenas a "última". Então, talvez modifique sua resposta boa com:sed 's/\.[^.]*$//'
Otheus

1
sim, esse regexp funcionaria nesse caso ... mas se o OP quiser isso, eles devem dizê-lo em vez de dizerem especificamente que "querem que ele seja listado sem o .png" #
1818

4
O -1não é necessário, sendo o padrão aqui.
Jlliagre 18/05

3
@jlliagre Concordo com cas que o -1deve ser especificado. É apenas o padrão quando o pipe é ativado, o que é uma surpresa oculta para alguns. Tornar explícito ajuda a entender. Eu também faço isso nos meus scripts para saber que tipo de saída estou esperando.
Otheus

1
Aviso No caso de um nome de arquivo com a tecla ( .png) antes de um caractere de nova linha, você apagará mesmo isso .pnge não apenas o último. É melhor evitar canalizar e analisar a saída de ls, pois reserva muitas surpresas bem escondidas ... (algumas palavras e referências mais na resposta).
Hastur

16

Se você quer apenas usar o bash:

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

Você deve grepprocurar ao tentar encontrar correspondências, não para remover / substituir o que sedé mais apropriado:

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

Depois de decidir que precisa criar alguns subdiretórios para ordenar seus arquivos PNG, você pode facilmente mudar isso para:

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

ls -1 | sed / .png // 'funciona muito bem. Obrigado!
Colin

O find canalizado para sedsolução pode apresentar alguns problemas se você encontrar um arquivo com a chave ( .png) como parte do nome e pouco antes de um caractere de nova linha. É melhor evitar canalizar e analisar a saída de , findou lsreserva muitas surpresas bem escondidas ... (algumas palavras e referências mais na resposta).
Hastur

Provavelmente substitua findpor algo como echono último exemplo. Não está claro qual propósito findserve lá e os resultados dependem da estrutura de diretório (ou seja, se você tem um diretório files.png)

@BroSlow Atualizado para algo mais sensato.
Anthon

13

Eu iria basename(assumindo a implementação GNU):

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

Observe que, se você quiser usá-lo em um canal, pode ser útil usar a opção GNU basename -z(ou --zero) para produzir saída separada por NUL (em vez de separada por nova linha).
Toby Speight

11

Outra resposta muito semelhante (estou surpreso que essa variante em particular ainda não tenha aparecido) é:

ls | sed -n 's/\.png$//p'
  • Você não precisa da -1opção ls, pois lsassume que, se a saída padrão não for um terminal (neste caso, é um tubo).
  • a -nopção para sedsignifica 'não imprimir a linha por padrão'
  • a /popção no final da substituição significa '... e imprima esta linha se uma substituição foi feita'.

O efeito líquido disso é imprimir apenas as linhas que terminam .pngcom as .pngremovidas. Ou seja, isso também atende à ligeira generalização da pergunta do OP, onde o diretório não contém apenas .pngarquivos.

A sed -ntécnica geralmente é útil nos casos em que você poderia usar grep + sed.


Gosto de como você se preocupou em escrever sua resposta. Esta solução apresentará problemas com nomes de arquivos, incluindo novas linhas , e não imprimirá a primeira parte do nome. Ainda mais se for mais desagradável com a tecla ( .png) antes do caractere de nova linha: nesse caso, você imprimirá essa parte sem png, não apagando apenas a última parte. Muitas vezes, é sugpugested para evitar a parse (e pipe) a saída lsporque os problemas podem ser escondidas apenas onde você não está pensando em ...
Hastur

2
@ Hastur Você está correto, em princípio, e a famosa página sobre não analisar lista outros problemas (e soluções) ao entregar nomes de arquivos patológicos. Mas a melhor maneira de lidar com isso é evitar nomes de arquivos patológicos (doh!); e se você não puder, ou se precisar ser robusto contra eles, use findou - possivelmente melhor - use uma linguagem mais poderosa do que shpara gerenciá-los (o fato de que sh pode fazer tudo não significa que é a melhor escolha em cada caso). O shell foi projetado para usabilidade primeiro.
Norman Gray

Concordo, em princípio, sobre a usabilidade, mas essa variante falha quando você tem um nome de arquivo com cada nova linha dentro. Isso pode ocorrer facilmente despercebido, por exemplo, quando você copia e cola uma linha de um pdf em uma GUI; portanto, você pensa apenas em evitar nomes de arquivos patológicos .
Hastur

Além disso, IMHO É fácil começar a analisar ls, mas está a caminho de problemas futuros. Muitas vezes, criamos scripts que usaremos mais tarde, quando já esqueceremos seu limite ... (é humano, é usual). Eu propus um findexemplo (com -exece sem tubo), mesmo que eu considere uma resposta melhor (porque pura concha) à resposta do tipo cuonglm , sólida e posix.
Hastur

@ Hastur: esses problemas futuros surgirão de qualquer maneira. Muitas coisas no sistema não são robustas contra arquivos com novas linhas. Por exemplo, tente usar locateou makeneles.
Reinierpost

8

Você pode usar apenas comandos BASH para fazer isso (sem nenhuma ferramenta externa).

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

Isso é útil quando você está sem o / usr / bin e funciona bem para nomes de arquivos como this.is.image.png e para todas as extensões.


6

Não é seguro analisar lsou canalizar find[ 1 , 2 ]

Não é seguro analisar (e canalizar) a saída lsou find, principalmente porque é possível encontrar nos nomes dos arquivos caracteres não usuais como a nova linha , a guia ... Aqui um ciclo de shell puro funcionará [ cuonglm ] .
Mesmo o findcomando não canalizado com a opção -execfuncionará:

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

Atualizações / Notas : Você pode find .pesquisar até os arquivos ocultos ou find ./*.pngobter apenas os não ocultos. Com find *.png -exec ...você pode ter problema no caso de estar presente um arquivo nomeado .pngporque o find irá obtê-lo como uma opção. Você pode adicionar -maxdepth 0para evitar a descida nos diretórios nomeados como Dir_01.png, ou find ./*.png -prune -exec ...quando maxdepth não for permitido (obrigado Stéphane). Se você deseja evitar listar esses diretórios, adicione a opção -type f(que também excluiria outros tipos de arquivos não regulares). Dê uma olhada no manpanorama mais completo sobre todas as opções disponíveis e lembre-se de verificar quando são compatíveis com POSIX, para uma melhor portabilidade.

Algumas palavras mais

Pode acontecer, por exemplo, que, copiando o título de um documento e colando no nome do arquivo, uma ou mais novas linhas sejam finalizadas no próprio nome do arquivo. Podemos ser tão azarados que um título possa conter até a chave que precisamos usar antes de uma nova linha:

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

Se você deseja testar, pode criar nomes de arquivos como este com os 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"

O simples /bin/ls *pngserá exibido em ?vez dos caracteres não imprimíveis

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

Em todos os casos em que você canalizará a saída de lsou findo seguinte comando terá nenhum indício de entender se a atual linha vem de um novo nome do arquivo ou se segue uma nova linha de caracteres no precedente nome do arquivo . Um nome desagradável , de fato, mas ainda legal.

Um ciclo de shell com um parâmetro Parameter-Expansion,, ${parameter%word}na variante com printfou echoirá funcionar ,, [ cuonglm ], [ Anthon1 ] .

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

Na página de manual da expansão de parâmetros do shell [ 3 ]

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

... o resultado da expansão é o valor do parâmetro com o padrão de correspondência mais curto (o caso '%') ou o padrão de correspondência mais longo (o caso '%%') excluído.


Também os resultados do seu findcomando são um pouco variável (por exemplo, se existe um diretório chamado files.png)

1
Caro @BroSlow, quando escrevi a resposta acima , tentei 13 (todas) outras variantes presentes naquele momento, por linha de comando, em um script, lançadas como argumento de uma invocação de shell. Faça o mesmo e diga-me se eles se comportam da maneira que você espera. Fiz meus testes com bash 4.3.11, traço 0.5.7-4, zsh (quando necessário) 5.0.2. Sinta-se livre para ler este post que acrescenta algo mais. Concordo com a nota de canalizar a saída find, por isso sugeri expressamente-exec e escrevi no título. :-).
Hastur

Releia o wiki novamente. Ainda acho que você precisa dar o exemplo, pois é isso que está sendo discutido aqui. E para a maioria das versões modernas, lsnão há problema algum quando a saída é canalizada ou redirecionada, mas, como mencionado no wiki, pode não funcionar para todos. A maioria só inserirá o ?lugar de caracteres especiais quando a saída for enviada ao terminal. ou seja, faça echo *.png | od -ce ls *.png | od -c. O problema da nova linha não é um problema ls, é um problema com qualquer comando que não termine nulo nos dois lados do canal.

1
printf "${f%.png}\n"está errado. O primeiro argumento é o formato, você não deve usar dados variáveis ​​lá. Pode até ser visto como uma vulnerabilidade de DoS (tente com um %1000000000s.pngarquivo, por exemplo).
Stéphane Chazelas

Você precisaria find ./*.png -prune -exec...ou você teria problemas com nomes começando com -(e arquivos de tipo diretório, nota que -maxdepthnão é portátil)
Stéphane Chazelas

4

não foi o suficiente?

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

ou em geral, esse

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

irá remover todas as extensões


Mas as outras soluções também funcionam.
Colin

Eu quis dizer que sua pergunta começou com ls -1, então ls -1 deve fazer isso. :)
Rohail Abbas

O -1não é necessário, sendo o padrão aqui.
Jlliagre 18/05

@ Rohail Abbas Mas nem todos os sistemas têm instalado sed?
Colin

1
De fato, mas o lsfaz de qualquer maneira sem essa opção quando sua saída não está sendo um terminal, como é o caso aqui.
Jlliagre 18/05/19

3

Use rev:

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

revinverte todas as strings (linhas); você corta tudo depois do primeiro '.' e rev reverte o restante.

Se você quiser grep'alma':

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

O -1não é necessário, sendo o padrão aqui.
Jlliagre 18/05

2
Isso falha muito em um arquivo chamadoMy.2016.Summer.Vacation.png
David Conrad

@DavidConrad my bad: / Corrigi para #cut -f 2-
Tom Solid

Agora ele trabalha com esse arquivo, mas ainda não com um arquivo com .pnge uma nova linha logo após ... Sugere-se para evitar que analisar lsporque gostam de se esconder bem as surpresas ... :-)
Hastur

2

Se eu soubesse que o diretório tinha apenas arquivos com .png como uma extensão, eu teria acabado de executar: ls | awk -F. '{print $1}'

Isso retornará o primeiro "campo" para qualquer coisa em que exista um nome de arquivo.extensão.

Exemplo:

[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

Infelizmente, ele falhará em todos os nomes de arquivos com mais de um ., como Image.1.pnge mesmo naqueles com nomes não agradáveis , com caracteres especiais. como a nova linha ou o que você vai usar como (entrada) separador de registro em awk, RS. Sugere-se evitar analisar a lssaída, pois ela gosta de ocultar problemas que surgirão quando você não espera. Você pode ler mais nas referências 1 ou 2 . BTW agradável a idéia de usar awk ... Coloquei alguns exemplos em uma resposta.
Hastur

É verdade, porém, dada a amostra fornecida por Colin, isso funcionaria perfeitamente. Para que funcione no caso que você sugeriu, eu provavelmente mudaria para: [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ão está tentando ser difícil, mas, dada a necessidade de Colin, não tenho certeza qual seria o problema analisando sl.
21816 rsingh #

desculpe ... Acabei de perceber que não mostrei o diretório com os arquivos antes de sed modificar a saída 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 você precisa escapar do dentro .para \.dentro sed -e 's/\.png$//', mas isso se torna uma resposta que acabou de ser escrita. :-( note2 você pode tentar usar awkalgo como ls | awk -F. '{if ($NF=="png") {for (i=1;i<NF-1;i++) printf("%s.", $i) ; printf $(NF-1)"\n"}}'... mas você sempre terá o problema que o awk não pode saber se a linha está chegando está seguindo ou não uma nova linha dentro do nome do arquivo. Tentei dizer melhor na minha resposta .
Hastur

Obrigado Hastur, senti falta disso :). Além disso, abandonei o uso do awk em favor do sed neste caso.
21816 rsingh # 17:

2

de acordo com o seu comentário "Eu tenho um arquivo de texto em que todos os nomes estão listados sem a extensão. Quero criar um script PHP que compare o arquivo de texto com a pasta para ver qual arquivo está faltando":

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 o inverso:

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

É o método mais preciso, conforme destacado por @roaima. Sem os fugitivos \.pngarquivos nomeados a_png.pngseriam listados como: a_.


usar ls -l, como você faz, fornece detalhes do arquivo, não é sobre isso que o OP perguntou.
Anthon

1

Uma linha de shell simples (ksh, bash ou zsh; não traço):

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

Uma função simples (de No Extension):

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

Ou uma função que remove qualquer extensão fornecida (png por padrão):

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

Use como:

ne jpg

Se a saída for um asterisco *, não haverá arquivo com essa extensão.


1

Você pode tentar o seguinte feed para ativar a saída de seu superator é o "." e como todos os seus arquivos terão name.png, você imprime a primeira coluna:
ls | awk -F"." '{print $1}'


-1

Se você tiver acesso ao sed, é melhor, pois removerá a última extensão do arquivo, não importa qual seja (png, jpg, tiff, etc ...)

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

7
Quebras para nomes de arquivos como this.is.a.dotty.txt. Tente em s/\.[^.]*$//vez disso.
roaima 18/05
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.