Deseja criar arquivos txt para cada png na pasta

12

Eu tenho esse script

#!/bin/bash

folder='/home/data/mnist/training'

for filePng in $folder/*
do
touch $filePng.txt
done

Funciona, exatamente isso, para um arquivo chamado 001.png, ele cria em 001.png.txtvez de 001.txt.

Como posso modificar isso?

Qubix
fonte
4
É um bom hábito entrar para citar suas variáveis. O shell script é uma linguagem estranha que evoluiu com o tempo, em vez de ser perfeitamente projetada desde o início, então, infelizmente, algumas coisas irritantes como essa se tornam necessárias. Sem citar suas variáveis, espaços em branco ou asteriscos no conteúdo das variáveis ​​farão com que as coisas quebrem de maneiras estranhas. Para tornar seus scripts mais robustos, sempre envolva o uso de suas variáveis ​​entre aspas duplas. Aqui, você diria for filePng in "$folder"/*e touch "$filePng".txt - observe que apenas as cita quando precedido por a $.
Muzer
3
Isso parece um problema XY ... Por que você está tentando fazer isso?
precisa saber é o seguinte

Respostas:

16

Você pode usar o basenamecomando aqui:

touch "$folder/$(basename "$filePng" .png).txt"

Observe o adicional $folder/. Isso é necessário, pois o comando basename remove o caminho.

Wayne_Yux
fonte
Posso sugerir que você citou sua expansão de parâmetro e substituição de comando?
precisa
@ TomFenech sim, provavelmente uma boa idéia para citar toda a string. Eu editei minha resposta.
#
Não sei por que você removeu as aspas internas $filePng- elas também foram úteis.
21417 Tom Tomech
1
Não, porque $( )estabelece um novo contexto de cotação.
21417 Tom Tomech
2
Oh, você está certo - aprendeu algo novo hoje ;-)
Wayne_Yux
31

Você pode remover a extensão existente usando os recursos de expansão de parâmetro do shell

${parameter%pattern}O 'padrão' corresponde ao final de 'parâmetro'. O resultado é o valor expandido de 'parâmetro' com a menor correspondência excluída.

Portanto, no seu caso, substitua $filePng.txtpor"${filePng%.png}.txt"

chave de aço
fonte
10

Com a variação do que a chave de aço já mencionou - expansão de parâmetros -, podemos usar a substituição de cordas para fazer o trabalho. Além disso, você deve citar variáveis. Abaixo está o seu script editado.

#!/bin/bash

folder='/home/data/mnist/training'

for filePng in "$folder"/*
do
    touch "${filePng/.png/.txt}"
done
Sergiy Kolodyazhnyy
fonte
9

Se você tiver muitos arquivos para criar, vale a pena "tocar" mais de um arquivo por vez, para que você não precise bifurcar um novo processo para cada um deles (o que leva algum tempo se for realizado vários mil vezes).

Opção 1: substituição de padrão + xargs

Essa opção fornecerá vários caminhos para o touchcomando de uma só vez, geralmente alguns milhares ou o que o sistema puder caber em uma única linha de comando.

find "$folder" -mindepth 1 -maxdepth 1 -name '*.png' -print0 |
sed -ze 's/\.png$/.txt/' |
xargs -r0 -- touch --

Opção 2: expansão de parâmetro + redirecionamento de saída de comando

Essa opção não é executada touch, mas usa os recursos de shell Bash / Bourne / POSIX, que não exigem subprocessos.

for f in "$folder"/*.png; do
    : >> "${f%.png}.txt"
done
David Foerster
fonte
4

Se você tem certeza de que não possui arquivos em .pngalgum lugar no meio do nome, basta usar uma matriz com a expansão de parâmetros:

pngs=( /path/to/pngs/*.png )
touch "${pngs[@]/.png/.txt}"

Isso armazena todos os caminhos para os arquivos que terminam em .pnguma matriz e, em seguida, usa a expansão de parâmetros para criar a lista de .txtarquivos, substituindo .pngpor .txtcada um.

Lembre-se de que isso será interrompido se você tiver tantos arquivos que eles não poderão ser transmitidos como argumentos para a mesma chamada de touch.

Tom Fenech
fonte