GNU find: obtenha o caminho absoluto e relativo em -exec

GNU find: obtenha o caminho absoluto e relativo em -exec

Eu tenho um comando (não echo!) que desejo executar, que segue um caminho absoluto e um caminho relativo.

Como obtenho esses dois argumentos?

Tentar:

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

(Eu gosto do GNU find porque é recursivo, pode restringir por arquivo, pode filtrar por nome de arquivo e não gera shells excessivos)

Expectativa:

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

Responder1

Export d, então ele estará disponível em seu bashscript embutido. Você também não precisa bashde nada aqui. O seu (espero que seja mais fino/mais rápido) shtambém funcionaria. Além disso, você não precisa executar um shell por arquivo. Você pode passar mais arquivos para seu script embutido com a -exec cmd {} +variante:

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 {} +

Que dá:

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

Mas se você precisar apenas do caminho relativo, pode ser mais simples fazer isso:

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

Isso também tornaria os argumentos do comando passados ​​para shmais curtos, permitindo findpassar mais argumentos para sh.

Observe que não há nada específico do GNU no seu findcomando nem no meu. Isso deve funcionar em qualquer implementação compatível com POSIX find, não apenas na GNU. A única parte não POSIX na sua pergunta foi obviamente bashe o ${1:offset}operador que é um operador Korn Shell, não em POSIX sh.

Para uma pesquisa recursiva de arquivo que permite especificar o tipo de arquivo, consulte também zsh:

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

Acima, o .é equivalente a find's -type f(apenas arquivos regulares), mas Dtambém inclui arquivos ocultos, como findfaz.


Como observação lateral, no caso geral:

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

Não é garantido que a saída seja "sim", porque os operadores ${#var}e ${var:offset}trabalham compersonagens, nãobytes.

Por exemplo, em um código de idioma UTF-8, não seria gerado yes com estes valores de ae b:

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

Com eles, $cconteria meu primeiro nome ( Stéphane) $acontém metade desse écaractere e $ba outra metade ${#a}seria 3(2 caracteres e 1 byte não formando um caractere válido, mas ainda contado).

Assim $dseria phane, não $'\xa9phane'.

Porém, no caso específico d=$a/$b, deve estar tudo bem, já que nenhum conjunto de caracteres geralmente disponível nas localidades do sistema teria um caractere diferente /daquele que contém a codificação de /.

informação relacionada