Chcesz tworzyć pliki txt dla każdego pliku png w folderze

12

Mam ten skrypt

#!/bin/bash

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

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

Działa tylko tyle, że dla pliku o nazwie 001.pngtworzy 001.png.txtzamiast 001.txt.

Jak mogę to zmienić?

Qubix
źródło
4
Dobrym nawykiem jest cytowanie zmiennych. Skrypt powłoki to dziwny język, który ewoluował z czasem, a nie był doskonale zaprojektowany od samego początku, więc niestety konieczne są pewne irytujące rzeczy. Bez cytowania zmiennych, białe znaki lub gwiazdki w treści zmiennych spowodują, że rzeczy się psują. Aby skrypty były bardziej niezawodne, zawsze otaczaj użycie zmiennych podwójnymi cudzysłowami. Tutaj powiedziałbyś for filePng in "$folder"/*i touch "$filePng".txt - zauważ, że cytujesz je tylko wtedy, gdy poprzedza je znak $.
Muzer
3
Wygląda to na problem XY… Dlaczego próbujesz to zrobić?
JeromeJ

Odpowiedzi:

16

Możesz użyć basenamepolecenia tutaj:

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

Zwróć uwagę na dodatkowe $folder/. Jest to konieczne, ponieważ polecenie basename usuwa ścieżkę z.

Wayne_Yux
źródło
Czy mogę zasugerować, że zacytowałeś rozszerzenie parametrów i podstawianie poleceń?
Tom Fenech,
@TomFenech tak, prawdopodobnie dobrym pomysłem jest zacytowanie całego ciągu. Zredagowałem swoją odpowiedź.
Wayne_Yux
Nie jestem pewien, dlaczego usunąłeś wewnętrzne cytaty $filePng- one też były przydatne.
Tom Fenech,
1
Nie, ponieważ $( )ustanawia nowy kontekst cytowania.
Tom Fenech,
2
Och, masz rację - nauczyłeś się dzisiaj czegoś nowego ;-)
Wayne_Yux
31

Możesz usunąć istniejące rozszerzenie, używając funkcji rozszerzania parametrów powłoki

${parameter%pattern}„Wzór” jest dopasowywany do końca „parametru”. Wynikiem jest rozszerzona wartość „parametru” z usuniętym najkrótszym dopasowaniem.

Więc w twoim przypadku, wymienić $filePng.txtz"${filePng%.png}.txt"

steeldriver
źródło
10

Przy zmianie tego, o czym już wspominał steeldriver - rozszerzanie parametrów - możemy wykonać zamianę łańcucha. Dodatkowo powinieneś cytować zmienne. Poniżej znajduje się edytowany skrypt.

#!/bin/bash

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

for filePng in "$folder"/*
do
    touch "${filePng/.png/.txt}"
done
Sergiy Kolodyazhnyy
źródło
9

Jeśli masz dużo plików do utworzenia, warto „dotknąć” więcej niż jednego pliku na raz, aby nie trzeba było przygotowywać nowego procesu dla każdego z nich (co zajmuje sporo czasu, jeśli wykonano wiele tysiąc razy).

Opcja 1: podstawienie wzoru + xargs

Ta opcja zapewni wiele ścieżek do touchpolecenia jednocześnie, zwykle kilka tysięcy lub cokolwiek, co system może zmieścić w jednym wierszu polecenia.

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

Opcja 2: rozszerzenie parametrów + przekierowanie wyjścia polecenia

Ta opcja w ogóle nie działa, touchale zamiast tego używa funkcji powłoki Bash / Bourne / POSIX, które w ogóle nie wymagają podprocesów.

for f in "$folder"/*.png; do
    : >> "${f%.png}.txt"
done
David Foerster
źródło
4

Jeśli masz pewność, że nie masz plików .pnggdzieś pośrodku nazwy, możesz po prostu użyć tablicy z rozszerzeniem parametrów:

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

Zapisuje wszystkie ścieżki do plików kończących się .pngw tablicy, a następnie używa parametru ekspansji stworzyć listę .txtplików, zastępując .pngna .txtna każdej z nich.

Pamiętaj, że to się zepsuje, jeśli masz tak wiele plików, że nie wszystkie mogą zostać przekazane jako argumenty tego samego wywołania touch.

Tom Fenech
źródło