Búsqueda de GNU: obtenga la ruta absoluta y relativa en -exec

Búsqueda de GNU: obtenga la ruta absoluta y relativa en -exec

Tengo un comando (¡no echo!) que quiero ejecutar y que toma una ruta absoluta y una ruta relativa.

¿Cómo obtengo estos dos argumentos?

Intentar:

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

(Me gusta la búsqueda de GNU porque es recursiva, puede restringir por archivo, puede filtrar por nombre de archivo y no genera shells excesivos)

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

Respuesta1

Exportar d, luego estará disponible en su bashsecuencia de comandos en línea. Tampoco necesitas bashnada aquí. Tu (con suerte más delgado/más rápido) shtambién serviría. Además, no es necesario ejecutar un shell por archivo. Puedes pasar más archivos a tu script en línea con la -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 {} +

Lo que da:

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

Pero si solo necesita la ruta relativa, puede ser más sencillo hacerlo:

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

Eso también haría que los argumentos del comando pasados ​​fueran shmás cortos, por lo que permitiría findpasar más argumentos a sh.

Tenga en cuenta que no hay nada específico de GNU en su findcomando ni en el mío. Eso debería funcionar en cualquier implementación compatible con POSIX find, no solo en la GNU. La única parte que no es POSIX en su pregunta fue obviamente bashy el ${1:offset}operador, que es un operador de shell Korn, no en POSIX sh.

Para una búsqueda de archivos recursiva que le permita especificar el tipo de archivo, consulte también zsh:

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

Arriba, .es el equivalente a find's -type f(solo archivos normales), mientras que Dtambién debe incluir archivos ocultos como findlo hace.


Como nota al margen, en el caso general:

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

No se garantiza que el resultado sea "sí", porque los operadores ${#var}y ${var:offset}trabajan concaracteres, nobytes.

Por ejemplo, en una configuración regional UTF-8, no generaría sí con estos valores de ay b:

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

Con esos, $ccontendría mi nombre ( Stéphane) $acontiene la mitad de ese écarácter y $bla otra mitad ${#a}sería 3(2 caracteres y 1 byte que no forman un carácter válido pero aún se cuentan).

Así $dsería phane, no $'\xa9phane'.

Sin embargo, en el caso específico de d=$a/$b, debería estar bien, ya que ninguno de los conjuntos de caracteres generalmente disponibles en las configuraciones regionales del sistema tendría un carácter distinto /al que contiene la codificación de /.

información relacionada