Möchten Sie TXT-Dateien für jedes PNG im Ordner erstellen

12

Ich habe dieses Skript

#!/bin/bash

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

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

Es funktioniert nur, dass es für eine Datei namens 001.pngerstellt, 001.png.txtanstatt 001.txt.

Wie kann ich das ändern?

Qubix
quelle
4
Es ist eine gute Angewohnheit, Ihre Variablen zu zitieren. Shell-Skript ist eine seltsame Sprache, die sich im Laufe der Zeit entwickelt hat und nicht von Anfang an perfekt gestaltet wurde. Daher sind leider einige nervige Dinge wie diese notwendig. Ohne Ihre Variablen in Anführungszeichen zu setzen, führen Leerzeichen oder Sternchen im Variableninhalt dazu, dass die Dinge auf seltsame Weise kaputt gehen. Um Ihre Skripte robuster zu machen, umgeben Sie Verwendungen Ihrer Variablen immer mit doppelten Anführungszeichen. Hier würden Sie sagen for filePng in "$folder"/*und touch "$filePng".txt - beachten, dass Sie sie nur zitieren, wenn ein vorangestelltes $.
Muzer
3
Das sieht nach einem XY-Problem aus ... Warum versuchen Sie das?
JeromeJ

Antworten:

16

Sie können den basenameBefehl hier verwenden:

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

Beachten Sie die zusätzlichen $folder/. Dies ist erforderlich, da der Befehl basename den Pfad von entfernt.

Wayne_Yux
quelle
Könnte ich vorschlagen, dass Sie Ihre Parametererweiterung und Befehlsersetzung zitiert haben?
Tom Fenech
@TomFenech Ja, wahrscheinlich ist es eine gute Idee, die ganze Zeichenfolge zu zitieren. Ich habe meine Antwort bearbeitet.
Wayne_Yux
Ich bin mir nicht sicher, warum Sie die inneren Anführungszeichen entfernt haben $filePng- sie waren auch nützlich.
Tom Fenech
1
Nein, weil $( )es einen neuen Kontext für Zitate schafft .
Tom Fenech
2
Oh, du hast Recht - heute etwas Neues gelernt
;-)
31

Sie können die vorhandene Erweiterung mit der Shell entfernen Parameter Erweiterungsfunktionen

${parameter%pattern}Das 'Muster' wird mit dem Ende von 'Parameter' abgeglichen. Das Ergebnis ist der erweiterte Wert von 'parameter', wobei die kürzeste Übereinstimmung gelöscht wird.

Also in Ihrem Fall ersetzen $filePng.txtmit"${filePng%.png}.txt"

Stahlfahrer
quelle
10

Bei Variation dessen, was der Stahltreiber bereits erwähnt hat - Parametererweiterung - können wir String-Ersetzungen verwenden, um die Arbeit zu erledigen. Zusätzlich sollten Sie Variablen zitieren. Unten sehen Sie Ihr bearbeitetes Skript.

#!/bin/bash

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

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

Wenn Sie viele Dateien erstellen müssen, lohnt es sich, mehrere Dateien gleichzeitig zu „berühren“, damit Sie nicht für jede Datei einen neuen Prozess erstellen müssen (was bei mehreren Vorgängen einige Zeit in Anspruch nimmt) tausendmal).

Option 1: Mustersubstitution + xargs

Diese Option liefert mehrere Pfade gleichzeitig zum touchBefehl, normalerweise einige Tausend oder was auch immer das System auf eine einzelne Befehlszeile passen kann.

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

Option 2: Parametererweiterung + Befehlsausgabeumleitung

Diese Option wird überhaupt nicht ausgeführt, touchsondern verwendet stattdessen die Bash / Bourne / POSIX-Shell-Funktionen, für die überhaupt keine Unterprozesse erforderlich sind.

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

Wenn Sie sicher sind, dass sich keine Dateien .pngin der Mitte des Namens befinden, können Sie einfach ein Array mit Parametererweiterung verwenden:

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

Dadurch werden alle Pfade zu den Dateien gespeichert, die in .pngeinem Array enden. Anschließend wird mithilfe der Parametererweiterung die Liste der .txtDateien erstellt, indem .pngfür .txtjede einzelne Datei ein Ersatz verwendet wird .

Beachten Sie, dass dies fehlschlägt, wenn Sie so viele Dateien haben, dass nicht alle als Argumente an denselben Aufruf von übergeben werden können touch.

Tom Fenech
quelle