GNU find: получить абсолютный и относительный путь в -exec

GNU find: получить абсолютный и относительный путь в -exec

У меня есть команда (не echo!), которую я хочу выполнить, и которая принимает абсолютный и относительный пути.

Как мне получить эти два аргумента?

Пытаться:

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

(Мне нравится GNU find, потому что он рекурсивен, может ограничивать по файлу, может фильтровать по имени файла и не порождает лишних оболочек)

Ожидание:

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

решение1

Export d, тогда он будет доступен в вашем встроенном скрипте. Вам также здесь совсем bashне нужно . Ваш (надеюсь, более тонкий/быстрый) тоже подойдет. Кроме того, вам не нужно запускать одну оболочку для каждого файла. Вы можете передать больше файлов в ваш встроенный скрипт с помощью варианта:bashsh-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 {} +

Который дает:

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

Но если вам нужен только относительный путь, может быть проще сделать следующее:

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

Это также сделает передаваемые аргументы команды shкороче, что позволит findпередавать больше аргументов sh.

findОбратите внимание, что в вашей команде нет ничего специфичного для GNU, как и в моей. Это должно работать в любой POSIX-совместимой findреализации, не только в GNU. Единственная не-POSIX часть в вашем вопросе, очевидно, bashи ${1:offset}оператор, который является оператором оболочки Korn, а не в POSIX sh.

Для рекурсивного поиска файлов, который позволяет указать тип файла, см. также zsh:

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

Вышеуказанный эквивалент .соответствует (только обычные файлы), а также включает скрытые файлы, как и .find-type fDfind


В качестве примечания: в общем случае:

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

Не гарантируется вывод «да», поскольку ${#var}и${var:offset} работают сперсонажи, нетбайты.

Например, в локали UTF-8 он не выведет yes со следующими значениями aи b:

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

С ними, $cбудет содержать мое имя ( Stéphane) $aсодержит половину этого éсимвола, а $bдругая половина ${#a}будет 3(2 символа и 1 байт, не образующие допустимый символ, но все равно учитываемые).

Так $dбы и было phane, нет $'\xa9phane'.

Однако в конкретном случае d=$a/$bэто должно быть нормально, поскольку ни один из наборов символов, обычно доступных в системных локалях, не будет содержать символ, отличный от /содержащего кодировку /.

Связанный контент