ubuntuusers.de

Increment in Bash Script funktioniert nicht

Status: Gelöst | Ubuntu-Version: Ubuntu 18.04 (Bionic Beaver)
Antworten |

burli

Avatar von burli

Anmeldungsdatum:
27. April 2007

Beiträge: 9066

Wohnort: Petersberg

Hi, ich bin ein Bash Noob und komme nicht weiter. Ich habe dieses Script

1
2
3
4
5
6
7
#!/bin/bash
a=1
for i in `ls -rt *.jpg`; do
new=$(printf "%04d.jpg" ${a})
cp ${i} ./renamed/${new}
let "a=a+2"
done

Das Script läuft die Schleife durch. Ich bekomme aber die Fehlermeldung "let: not found". Auch die Schreibweise [1] oder ähnliches funktioniert nicht. Hab auch andere Varianten von dieser Seite versucht, aber ohne Erfolg.

Was ist hier falsch?

  • 1: a++

Mooi

Anmeldungsdatum:
15. August 2014

Beiträge: 187

1
((a=a+2))

burli

(Themenstarter)
Avatar von burli

Anmeldungsdatum:
27. April 2007

Beiträge: 9066

Wohnort: Petersberg

Ich habe vermutlich den Grund gefunden. Ich hab das Script mit sh gestartet. Jetzt habe ich das Script ausfürbar gemacht. Damit geht es

ich habe inzwischen aber auch eine Alternative gefunden. Soll vor allem bei vielen Files besser funktionieren

1
find . -name '*.jpg' | awk 'BEGIN{ a=1 }{ printf "mv \"%s\" planar_%05d.jpg\n", $0, a++ }' | bash

Seebär

Avatar von Seebär

Anmeldungsdatum:
2. Mai 2009

Beiträge: 833

Vor allem ist es besser. Die for-Loop mit ls zu verwenden ist ein bekannter Fehler. Niemals ls bei so etwas einsetzen, auch wenn es verlockend ist / schön wäre. Siehe da http://mywiki.wooledge.org/BashPitfalls und dann gleich bei Punkt 1.

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11260

Wohnort: München

Der Ansatz mit find sortiert dann aber die Dateien dann aber nicht wie ls -rt (da kommen die zuletzt bearbeiteten Dateien zuerst), sondern alphabetisch.

Außerdem muss man aufpassen, dass bestehende Dateien mit dem Schema planar_NNNNN.jpg überschrieben werden und das Konstrukt so nicht mit Zeilenumbrüchen in Dateinamen umgehen kann.

Statt das mit der Bash zu lösen, könnte man z.B. auch Python3 nehmen:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/usr/bin/env python3
import pathlib
import re
import sys

prefix = "planar_"
suffix = ".jpg"
existing_glob = f"{prefix}[0-9][0-9][0-9][0-9][0-9]{suffix}"
existing_re = re.compile(f"{prefix}\d{{5}}{suffix}")
dirs = [pathlib.Path('.')] if len(sys.argv) == 1 else sys.argv[1:]

for directory in dirs:
    try:
        n = int(str(max(directory.glob(existing_glob)))[len(prefix)+1:-len(suffix)])
    except ValueError:
        n = 0

    for f in reversed(sorted(directory.glob("*.jpg"), key=lambda x: x.stat().st_mtime)): # sort files by mtime, newest first
        if not f.is_file() or existing_re.match(str(f)): continue  # skip directories and existing files wich already have the naming schema we want
        n +=1
        target = f"{prefix}{n:05d}{suffix}"
        print(f"rename {f} to {target}")
        f.rename(directory.joinpath(target))

burli

(Themenstarter)
Avatar von burli

Anmeldungsdatum:
27. April 2007

Beiträge: 9066

Wohnort: Petersberg

Danke, werde ich mal testen.

Die originalen Dateinamen sehen im übrigen so aus

2018-09-15-102619_8.jpg

Ich mache gerade eine Serienaufnahme mit Cheese

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13205

seahawk1986 schrieb:

Der Ansatz mit find sortiert dann aber die Dateien dann aber nicht wie ls -rt (da kommen die zuletzt bearbeiteten Dateien zuerst), sondern alphabetisch.

Bei ls -rt kommen die ältesten Dateien zuerst. Die Option "-r" dreht die Reihenfolge um, und "-t" liefert zuerst die neuesten.

Ich glaube, wir müssen das mal in die Gebote mit aufnehmen:

Du sollst die Ausgabe von ls nicht parsen!

Ich stimme zu, dass so etwas in Python oder Ruby einfacher ist. Hier nochmal eine Lösung mit der Shell:

1
stat --printf '%Y %n\0' *.jpg | sort -zn | sed -z 's#^[0-9]* ##' | xargs -r0 sh -c 'a=1; for i; do new=$(printf "renamed/%04d.jpg" $a); echo cp "$i" "$new"; a=$((a+2)); done' --

Oder, etwas netter formatiert:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
stat --printf '%Y %n\0' *.jpg \
| sort -zn \
| sed -z 's#^[0-9]* ##' \
| xargs -r0 sh -c '
a=1

for i; do
  new=$(printf "renamed/%04d.jpg" $a) 
  echo cp "$i" "$new"
  a=$((a+2))
done
' --

Und: das läuft auch in der sh - man braucht keine bash dafür. Und das echo muss nach dem Testen natürlich raus.

Einziger Schönheitsfehler: wenn die Dateiliste richtig lang wird, dann passen nicht alle Dateinamen in eine Kommandozeile von sh und xargs startet die sh mehr als ein Mal. Man könnte das noch zumindest über eine Fehlererkennung einbauen, d.h., abbrechen, wenn $new auf eine Datei zeigt, die es schon gibt.

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11260

Wohnort: München

rklm schrieb:

seahawk1986 schrieb:

Der Ansatz mit find sortiert dann aber die Dateien dann aber nicht wie ls -rt (da kommen die zuletzt bearbeiteten Dateien zuerst), sondern alphabetisch.

Bei ls -rt kommen die ältesten Dateien zuerst. Die Option "-r" dreht die Reihenfolge um, und "-t" liefert zuerst die neuesten.

Ah stimmt, da bin ich durcheinander gekommen.

Antworten |