GNU find: absoluten und relativen Pfad in -exec abrufen

GNU find: absoluten und relativen Pfad in -exec abrufen

Ich möchte einen Befehl (nicht echo!) ausführen, der einen absoluten und einen relativen Pfad annimmt.

Wie komme ich zu diesen beiden Argumenten?

Versuchen:

d=/tmp/foo;
find "$d" -type f -exec bash -c 'echo d=${1:${#d}} 1="${1%/*}"' bash {} \;

(Ich mag GNU Find, weil es rekursiv ist, nach Dateien einschränken und nach Dateinamen filtern kann und nicht zu viele Shells erzeugt.)

Erwartung:

mkdir -p /tmp/foo/bar/can/haz; touch /tmp/foo/bar/can/haz/bzr.txt
# cmd is run, output is:
d=bar/can/haz 1=/tmp/foo/bar/can/haz

Antwort1

Export d, dann ist es in Ihrem bashInline-Skript verfügbar. Sie brauchen es hier auch gar nicht bash. Ihr (hoffentlich schlankeres/schnelleres) würde es auch tun. Außerdem müssen Sie nicht eine Shell pro Datei ausführen. Sie können Ihrem Inline-Skript mit der Variante shmehr Dateien übergeben :-exec cmd {} +

d=/tmp/foo
export d
find "$d" -type f -exec sh -c '
  for file do
    relative=${file#"$d/"}
    dir=${file%/*}
    relative_dir=${relative%/*}
    relative_dir=${relative_dir:-.}
    printf "%10s: %s\n" full "$file" \
                        relative "$relative" \
                        dir "$dir" \
                        reldir "$relative_dir"
  done' sh {} +

Das ergibt:

      full: /tmp/foo/bar/can/haz/bzr.txt
  relative: bar/can/haz/bzr.txt
       dir: /tmp/foo/bar/can/haz
    reldir: bar/can/haz

Wenn Sie jedoch nur den relativen Pfad benötigen, ist es möglicherweise einfacher, Folgendes zu tun:

(cd -P -- "$d" && find . -exec sh -c 'for file do...' sh {} +)

Dadurch würden auch die an übergebenen Befehlsargumente shkürzer und es könnten findmehr Argumente an übergeben werden sh.

Beachten Sie, dass weder Ihr noch mein Befehl GNU-spezifisch sind find. Das sollte in jeder POSIX-kompatiblen findImplementierung funktionieren, nicht nur in der GNU-Implementierung. Der einzige nicht-POSIX-Teil in Ihrer Frage war offensichtlich bashund der ${1:offset}Operator, der ein Korn-Shell-Operator ist, nicht in POSIX sh.

Informationen zu einer rekursiven Dateisuche, bei der Sie den Dateityp angeben können, finden Sie auch unter zsh:

(cd -P -- "$d" &&
  for file (**/*(ND.)) {
    dir=$file:h
    printf '%10s: %s\n' relative $file reldir $dir
  })

Oben .ist das das Äquivalent von find( -type fnur normale Dateien), während Dwie find„dos“ auch versteckte Dateien einschließt.


Als Randbemerkung, im allgemeinen Fall:

c=$a$b; d=${c:${#a}}
[ "$b" = "$d" ] && echo yes

Es ist nicht garantiert, dass "ja" ausgegeben wird, da die Operatoren ${#var}und ${var:offset}mitFiguren, nichtBytes.

Beispielsweise würde in einem UTF-8-Gebietsschema mit diesen Werten von aund nicht „yes“ ausgegeben b:

a=$'St\xc3' b=$'\xa9phane'

Damit $cwürde mein Vorname ( Stéphane) $adie Hälfte dieses éZeichens enthalten und $bdie andere Hälfte ${#a}wäre 3(2 Zeichen und 1 Byte, die kein gültiges Zeichen bilden, aber trotzdem gezählt werden).

So $dwäre es phane, nicht $'\xa9phane'.

Im speziellen Fall von d=$a/$bsollte es jedoch in Ordnung sein, da keiner der in Systemgebietsschemas allgemein verfügbaren Zeichensätze ein anderes Zeichen als /das hätte, das die Kodierung von enthält /.

verwandte Informationen