ubuntuusers.de

nach Dateitypen suchen, ohne Endungen

Status: Gelöst | Ubuntu-Version: Kein Ubuntu
Antworten |

Tids Team-Icon

Avatar von Tids

Anmeldungsdatum:
29. Oktober 2008

Beiträge: 3065

Wohnort: Naumburg (Saale)

Das beschäftigt mich schon ein Weilchen. Ich suche nach einer Möglichkeit bestimmte Dateiengruppen zu suchen. Dabei darf der Dateiname keine Rolle spielen, was die Endung einbezieht.

file TAKEASMILE.jpg  
TAKEASMILE.jpg: PDF document, version 1.4

Hintergrund ist der, dass ich viele viel viele Bilder habe die entweder komplett ohne Endung sind oder aber die falsche haben. Es sollte sich aber nicht auf Bilder beziehen, sondern einen von mir vorgegeben Dateityp finden.

dazu wäre es hilfreich wenn ich einen bestimmten Wert aus 'file' angeben kann und er mein $HOME durchsucht und eine Liste der gefundenen Dateien ausgibt.

ich bin mir sicher das sich das irgendwie mittels file und find lösen lässt, ich komme nur leider nicht drauf wie. Hoffentlich kann mir da jemand helfen oder mich zumindest in die richtige Richtung stoßen.

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11273

Wohnort: München

Wie wäre mit so einem Ansatz? Man lässt den Dateityp von file bestimmen und filtert mit grep nach dem gewünschten String:

find verzeichnis/ -type f -exec file {} \; | grep "PDF document"

track

Avatar von track

Anmeldungsdatum:
26. Juni 2008

Beiträge: 7174

Wohnort: Wolfen (S-A)

Vielleicht würde man den Ansatz noch um eine Untershell erweitern. In der kann man bequem den Zauber einbauen, den man braucht:

1
find verzeichnis -type f -exec bash -c 'file {} | grep -q "PDF document" && echo "zauber-zauber"' \;

LG,

track

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11273

Wohnort: München

Wäre es es nicht effizienter das nachträglich zu filtern als da jedes Mal eine neue Subshell aufzumachen?

1
find "$HOME" -type f -exec file {} \; | sed -nE 's/^(.*):.*PDF\ document.*/\1/p'

Vor allem wenn man da mehrere Aufrufe hintereinander für verschiedene Dateitypen macht, kann man sich die Liste so auch vorab erstellen lassen, in eine Datei schreiben und braucht die dann nur noch filtern.

track

Avatar von track

Anmeldungsdatum:
26. Juni 2008

Beiträge: 7174

Wohnort: Wolfen (S-A)

Es kommt eben drauf an, was Tids am Ende vorhat ...

Man muss sich halt nur dessen bewusst sein, dass Zeilenumbrüche und ähnliche Gemeinheiten evt. dann die ganze Geschichte sprengen können:

track@track:~$ file ./-test/*
./-test/8158133-Anhang.csv:                              UTF-8 Unicode text
./-test/8158133-Anhang.csv.neu:                          UTF-8 Unicode text, with CRLF line terminators
./-test/achdu�schreck!:                                    ASCII text
./-test/AVSEQ01.DAT:                                     ASCII text
./-test/AVSEQ01.DAT.BAK:                                 ASCII text
./-test/AVSEQ01.DAT.son:                                 Little-endian UTF-16 Unicode text
./-test/das hier ist gemein: \n ... kein echtes newline: ASCII text
./-test/Ers�tzz�ich�n:                                   empty
./-test/gnu    > *:                                      ASCII text
./-test/ich bin
böse:                                    ASCII text
./-test/-n:                                              UTF-8 Unicode text
./-test/test  datei
mit Gift ('Kopie").txt:              ASCII text
./-test/  vorn 2 leer:                                   empty
./-test/wort_mit_�h=öh.:                                empty 

und das kriegt man halt nur mit peinlich sauberem Arbeiten in den Griff.

LG,

track

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11273

Wohnort: München

Oder man wechselt von der Shell in eine Skriptsprache, die mit problematischen Zeichen in Pfaden leichter umgehen kann - mit ein bisschen Magie (Paket python3-magic unter Ubuntu) z.B. so:

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

import argparse
import magic
import os

m=magic.open(magic.MAGIC_NONE)
m.load()
parser = argparse.ArgumentParser(description='filter files in a directory by file type')
parser.add_argument('directories', metavar='directory', nargs='+',
    help='directory to search')
parser.add_argument('--search', '-s', default=None,
    help='string to search in file type description')
args = parser.parse_args()

for dir in args.directories:
    for root, dirnames, filenames in os.walk(dir):
        for filename in filenames:
            path = os.path.join(root, filename)
            filetype = m.file(path)
            if args.search and args.search in filetype:
                print(path)
            elif not args.search:
                print(path + ":", filetype)

Das nimmt dann einen (optionalen) Suchbegriff und einen oder mehrere Ordner, die durchsucht werden sollen:

$ python3 search_by_filedesc.py -h
usage: search_by_filedesc.py [-h] [--search SEARCH] directory [directory ...]

filter files in a directory by file type

positional arguments:
  directory             directory to search

optional arguments:
  -h, --help            show this help message and exit
  --search SEARCH, -s SEARCH
                        string to search in file type description

Ohne Suchbegriff listet es den Pfad und die Dateibeschreibung, mit Suchbegriff nur den Pfad für Treffer auf:

$ python3 search_by_filedesc.py test/
test/search_by_filedesc.py: Python script, ASCII text executable
test/original_mac.txt: ASCII text
test/lircd.org: UTF-8 Unicode text
$ python3 search_by_filedesc.py -s "UTF-8" test/
test/lircd.org

Tids Team-Icon

(Themenstarter)
Avatar von Tids

Anmeldungsdatum:
29. Oktober 2008

Beiträge: 3065

Wohnort: Naumburg (Saale)

Danke so weit erst einmal.
Was ich am Ende vor habe ich "ganz einfach". Je nachdem was für ein Dateityp will ich unterschiedliche Befehle mit dieser Datei ausführen. z.B. die einen Umbenennen, andere umwandeln. Gibts da irgendwas um für jede gefundene Datei eine Reihe von anderen Befehlen mit rein zu bauen?

nehmen wir mal einen ganz utopischen Fall, bei gefundene jpeg Dateien die ich nach *.webp umwandeln will.

das wären dann

du -h <datei>
cwebp -q80 <datei> -o <datei>.webp
rm <datei>
du -h <datei>.webp

wie gesagt, ist frei erfunden. Nur möchte ich eben auch mit den Dateien "arbeiten".

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11273

Wohnort: München

Da könnte man bei der Python-Lösung noch eine Option für die Ausführung von Befehlen einbauen:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#!/usr/bin/env python3

import argparse
import magic
import os
import subprocess

my_env = os.environ
m=magic.open(magic.MAGIC_NONE)
m.load()
parser = argparse.ArgumentParser(description='filter files in a directory by file type')
parser.add_argument('directories', metavar='directory', nargs='+',
    help='directory to search')
parser.add_argument('--search', '-s', default=None,
    help='string to search in file type description')
parser.add_argument('--command', '-c', default=None,
    help=('execute command on each filtered file - '
          'this is only executed if a search argument is given - '
          'the path is exported as environment variable "$match"'))
args = parser.parse_args()

for dir in args.directories:
    for root, dirnames, filenames in os.walk(dir):
        for filename in filenames:
            path = os.path.join(root, filename)
            filetype = m.file(path)
            if args.search and args.search in filetype:
                if not args.command:
                    print(path)
                else:
                    my_env['match'] = path
                    subprocess.call(args.command, shell=True, env=my_env)
            elif not args.search:
                print(path + ":", filetype)

Dann kannst du einen Befehl oder ein Skript ausführen lassen, das den Pfad zur jeweiligen Datei als Umgebungsvariable "${match}" nutzen kann:

$ python3 search_by_filedesc.py -s JPEG -c 'du -h "$match"; cwebp -q 80 "$match" -o "${match}.webp"; du -h "${match}.webp"' test
4,0K	test/background.jpg
Saving file 'test/background.jpg.webp'
File:      test/background.jpg
Dimension: 9 x 9
Output:    56 bytes Y-U-V-All-PSNR 44.39 99.00 99.00   46.15 dB
block count:  intra4: 0
              intra16: 1  (-> 100.00%)
              skipped block: 0 (0.00%)
bytes used:  header:              6  (10.7%)
             mode-partition:      3  (5.4%)
 Residuals bytes  |segment 1|segment 2|segment 3|segment 4|  total
    macroblocks:  |      100%|       0%|       0%|       0%|       1
      quantizer:  |      19 |      19 |      19 |      19 |
   filter level:  |       5 |       5 |       5 |       5 |
4,0K	test/background.jpg.webp

Statt einer Befehlsabfolge kannst du natürlich auch ein ausführbares Skript übergeben, dass die gefundenen Dateien manipuliert:

1
2
3
4
5
6
7
#!/bin/bash
if [[ -z "$match" ]]; then
    match="$1"
fi
du -h "$match"
cwebp -q 80 "$match" -o "${match}.webp"
du -h "${match}.webp"
$ python3 search_by_filedesc.py -s 'JPEG image data' -c './convert_to_webp.sh' test
4,0K	test/background.jpg
Saving file 'test/background.jpg.webp'
File:      test/background.jpg
Dimension: 9 x 9
Output:    56 bytes Y-U-V-All-PSNR 44.39 99.00 99.00   46.15 dB
block count:  intra4: 0
              intra16: 1  (-> 100.00%)
              skipped block: 0 (0.00%)
bytes used:  header:              6  (10.7%)
             mode-partition:      3  (5.4%)
 Residuals bytes  |segment 1|segment 2|segment 3|segment 4|  total
    macroblocks:  |      100%|       0%|       0%|       0%|       1
      quantizer:  |      19 |      19 |      19 |      19 |
   filter level:  |       5 |       5 |       5 |       5 |
4,0K	test/background.jpg.webp

Bei der Shell-Lösung von track mit find würde das z.B. so gehen:

1
$ find test -type f -exec bash -c 'file "$0" | grep -q "JPEG image data" && "./convert_to_webp.sh" "$0"' {} \;

Was für dich nützlicher ist, dürfte davon abhängen, womit du mehr Skripting-Erfahrung hast und ob es lustige Sonderzeichen in deinen Pfaden gibt, die das Shell-Skripting weniger praktikabel machen.

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13227

seahawk1986 schrieb:

Wäre es es nicht effizienter das nachträglich zu filtern als da jedes Mal eine neue Subshell aufzumachen?

1
find "$HOME" -type f -exec file {} \; | sed -nE 's/^(.*):.*PDF\ document.*/\1/p'

Was ist sed-Option "-E"? Ich kann die in keiner Manpage finden. Außerdem brauchst Du für den Ausdruck oben "-r", sonst musst Du die Klammern mit Backslash zu Meta-Zeichen machen.

Wenn Du schon am optimieren bist, dann solltest Du auch die Form nehmen, die file nicht extra für jede einzelne Datei aufruft:

1
find "$HOME" -type f -exec file {} + | sed -nre 's/^(.*): +PDF document.*$/\1/p'

Ich habe auch noch den Ausdruck etwas verändert.

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11273

Wohnort: München

rklm schrieb:

Was ist sed-Option "-E"? Ich kann die in keiner Manpage finden. Außerdem brauchst Du für den Ausdruck oben "-r", sonst musst Du die Klammern mit Backslash zu Meta-Zeichen machen.

Das stammt ursprünglich von BSD (https://www.freebsd.org/cgi/man.cgi?query=sed&sektion=&n=1), funktioniert aber auch schon länger mit GNU sed und macht laut Quellcode das selbe wie "-r". Im Quellcode war das schon bei der Version 4.1a (die müsste in etwa von 2008 gewesen sein): http://git.savannah.gnu.org/cgit/sed.git/tree/sed/sed.c?id=v4.1a#n259

Aber der Commit, der die Dokumentation dafür nachrüstet, hat es wohl noch nicht in ein offizielles Release geschafft: http://git.savannah.gnu.org/cgit/sed.git/commit/?id=8b65e07904384b529a464c89f3739d2e7e4d5135

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13227

seahawk1986 schrieb:

rklm schrieb:

Was ist sed-Option "-E"? Ich kann die in keiner Manpage finden. Außerdem brauchst Du für den Ausdruck oben "-r", sonst musst Du die Klammern mit Backslash zu Meta-Zeichen machen.

Das stammt ursprünglich von BSD (https://www.freebsd.org/cgi/man.cgi?query=sed&sektion=&n=1), funktioniert aber auch schon länger mit GNU sed und macht laut Quellcode das selbe wie "-r". Im Quellcode war das schon bei der Version 4.1a (die müsste in etwa von 2008 gewesen sein): http://git.savannah.gnu.org/cgit/sed.git/tree/sed/sed.c?id=v4.1a#n259

Aber der Commit, der die Dokumentation dafür nachrüstet, hat es wohl noch nicht in ein offizielles Release geschafft: http://git.savannah.gnu.org/cgit/sed.git/commit/?id=8b65e07904384b529a464c89f3739d2e7e4d5135

Erschreckend.

Tids Team-Icon

(Themenstarter)
Avatar von Tids

Anmeldungsdatum:
29. Oktober 2008

Beiträge: 3065

Wohnort: Naumburg (Saale)

seahawk1986 schrieb:

Bei der Shell-Lösung von track mit find würde das z.B. so gehen:

1
$ find test -type f -exec bash -c 'file "$0" | grep -q "JPEG image data" && "./convert_to_webp.sh" "$0"' {} \;

Danke. So funktioniert es perfekt ☺

Was für dich nützlicher ist, dürfte davon abhängen, womit du mehr Skripting-Erfahrung hast und ob es lustige Sonderzeichen in deinen Pfaden gibt, die das Shell-Skripting weniger praktikabel machen.

Auf jeden Fall Shellscriptiing. Mit Python kann ich nicht wirklich was anfangen :s

Vielen dank nochmal an dich und an alle anderen die geholfen haben (:

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13227

Tids schrieb:

seahawk1986 schrieb:

Bei der Shell-Lösung von track mit find würde das z.B. so gehen:

1
$ find test -type f -exec bash -c 'file "$0" | grep -q "JPEG image data" && "./convert_to_webp.sh" "$0"' {} \;

Danke. So funktioniert es perfekt ☺

Ehrlich gesagt, wenn man sowieso bereits convert_to_webp.sh hat, dann kann man die komplette Logik dort versenken und sich bash -c "..." beim Aufruf von find sparen. Dann macht man das gleich so:

1
2
3
4
5
6
7
8
9
#!/bin/sh

for f; do
  if file "$f" | fgrep -q 'JPEG image data'; then
    # do conversion
  else
    echo "Ignoring: $f" >&2
  fi
end

Der else-Zweig kann natürlich auch weggelassen werden.

Und:

1
find test -type f -exec ./convert_to_webp.sh {} +
Antworten |