Хотите сделать текстовые файлы для каждого PNG в папке

12

У меня есть этот сценарий

#!/bin/bash

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

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

Он работает, только для файла с именем 001.png, он создает 001.png.txtвместо 001.txt.

Как я могу изменить это?

Qubix
источник
4
Это хорошая привычка заключать в кавычки ваши переменные. Сценарий оболочки - это странный язык, который эволюционировал с течением времени, а не был идеально разработан с самого начала, поэтому, к сожалению, некоторые раздражающие вещи, подобные этой, становятся необходимыми. Без кавычек ваших переменных, пробелы или звездочки в содержимом переменных приведут к странным путям. Чтобы сделать ваши сценарии более надежными, всегда окружайте использование ваших переменных двойными кавычками. Здесь вы можете сказать for filePng in "$folder"/*и touch "$filePng".txt - обратите внимание, что вы цитируете их только тогда, когда им предшествует $.
Музер
3
Это похоже на проблему XY ... Почему вы пытаетесь это сделать?
JeromeJ

Ответы:

16

Вы можете использовать basenameкоманду здесь:

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

Обратите внимание на дополнительное $folder/. Это необходимо, поскольку команда basename удаляет путь из.

Wayne_Yux
источник
Могу ли я предположить, что вы процитировали расширение параметров и подстановку команд?
Том Фенек
@ TomFenech да, наверное, хорошая идея процитировать всю строку. Я отредактировал свой ответ.
Wayne_Yux
Я не уверен, почему вы удалили внутренние кавычки $filePng- они тоже были полезны.
Том Фенек,
1
Нет, потому что $( )устанавливает новый контекст цитирования.
Том Фенек
2
О, ты прав - узнал что-то новое сегодня ;-)
Wayne_Yux
31

Вы можете удалить существующее расширение, используя функции расширения параметров оболочки

${parameter%pattern}«Шаблон» сопоставляется с концом «параметра». Результатом является расширенное значение параметра с удалением кратчайшего соответствия.

Так что в вашем случае замените $filePng.txtна"${filePng%.png}.txt"

steeldriver
источник
10

В зависимости от того, что уже упоминалось в Steeldriver - расширение параметров - мы можем использовать замену строк для выполнения этой работы. Кроме того, вы должны заключать в кавычки переменные. Ниже ваш отредактированный скрипт.

#!/bin/bash

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

for filePng in "$folder"/*
do
    touch "${filePng/.png/.txt}"
done
Сергей Колодяжный
источник
9

Если у вас есть много файлов для создания, было бы целесообразно «дотронуться» более чем до одного файла за раз, чтобы вам не нужно было форкировать новый процесс для каждого из них (который занимает довольно много времени, если выполняется несколько тысячу раз).

Вариант 1: замена шаблона + xargs

Эта опция будет предоставлять несколько путей к touchкоманде одновременно, обычно несколько тысяч или что угодно, что система может поместиться в одной командной строке.

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

Вариант 2: расширение параметра + перенаправление вывода команды

Эта опция вообще не запускается, touchа использует функции оболочки Bash / Bourne / POSIX, которые вообще не требуют подпроцессов.

for f in "$folder"/*.png; do
    : >> "${f%.png}.txt"
done
Дэвид Фёрстер
источник
4

Если вы уверены, что у вас нет файлов с .pngсерединой имени, вы можете просто использовать массив с расширением параметров:

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

Это сохраняет все пути к файлам , заканчивающихся .pngв массиве , а затем использует параметр расширения для создания списка .txtфайлов, путем замены .pngна .txtна каждом из них.

Имейте в виду, что это сломается, если у вас будет так много файлов, что все они не могут быть переданы в качестве аргументов для одного и того же вызова touch.

Том Фенек
источник