Como permitir espaços em diretórios ao usar find -exec junto com basename?

1

Estou tentando encontrar todos os diretórios em um determinado caminho e criar links temporários dentro desses diretórios em diretórios com os mesmos nomes em outro local. Muitos dos diretórios possuem espaços em seus nomes. Eu montei o seguinte código juntos e parece funcionar desde que não haja espaços.

find /some/path/* -maxdepth 0 -exec sh -c "ln -s /some/other/path/"'$(basename {})'" {}" \;

Como devo mudar isso para lidar com espaços? Eu normalmente não os tenho em meus nomes de diretórios, mas esses diretórios espelhos no meu PC com Windows, onde eu uso espaços. Qualquer ajuda é muito apreciada!

EDITAR

Em resposta aos pontos feitos por cuonglm e Gilles:

  • A ordem dos argumentos do comando ln -s não está errada, mas o que eu queria fazer não está totalmente claro na minha explicação. Para cada diretório em /some/path/ , eu quero criar um link simbólico no diretório apontado em um diretório com o mesmo nome em /some/other/path/ . Portanto, /some/other/path/ é o source e /some/path/ é o destination . A razão pela qual eu quero fazer isso é porque /some/path/ contém um subconjunto dos diretórios em /some/other/path/ e eu quero um link do subconjunto para o conjunto completo para cada diretório.

  • Não haverá muitos diretórios no caminho, mas eu concordo que não protegê-lo é uma falha inútil.

  • O motivo pelo qual não usei -type d é que haverá apenas diretórios e não arquivos no caminho fornecido, mas percebo que incluí-lo é melhor.

por Martin Boström 28.02.2016 / 12:18

2 respostas

1

Existem alguns problemas com o seu comando:

  • /some/path/* pode causar um erro muito longo na lista de argumentos se houver muitos diretórios em /some/path

  • Não filtra diretório ou arquivo

  • É muito eficiente porque usou o script in-line sh -c , mas com -exec ... {} \;

  • Usando ln com posição de argumento incorreta, ln -s source dest criará link simbólico de source a dest , não dest a source .

Você pode fazer isso POSIXly:

find /some/path ! -path /some/path -prune -type d -exec sh -c '
  for f do
    ln -s "$f" /some/other/path/"${f##*/}"
  done
' sh {} +

Ou se o seu find suportar -maxdepth :

find /some/path -maxdepth 1 -type d -exec sh -c '...' sh {} +
    
por 28.02.2016 / 12:36
1

Primeiro, use sempre {} como um argumento separado em find -exec … . Usar stuff{}morestuff funciona com algumas (não todas) versões de find , mas não pode lidar com nomes de arquivos contendo caracteres “incomuns”, como espaço em branco, ' , " , etc. (exatamente quais caracteres causam problema depende exatamente onde você usa {} ). Passe {} como um argumento separado. O primeiro argumento depois de sh -c CODE é $0 . Não esqueça as aspas duplas em torno da expansão das variáveis .

find … -exec sh -c 'ln -s /some/other/path/$(basename "$0") "$0"' {} \;

(eu também simplifiquei as citações para usar aspas simples em todas as partes).

Existe pelo menos um outro problema com o seu comando. Você está tentando criar um link simbólico no local retornado por find , onde por construção já existe um link simbólico. Parece que você pretendia criar um link simbólico em /some/other/path , e nesse caso você precisa trocar os argumentos para ln (como com mv e cp , os arquivos existentes vêm em primeiro lugar, e o destino vem por último).

find /some/path/* -maxdepth 0 -exec sh -c 'ln -s "$0" /some/other/path/$(basename "$0")' {} \; 

Você pode tornar isso mais rápido usando construções de manipulação de string do shell em vez de basename e invocando sh em lotes por vez.

find /some/path/* -maxdepth 0 -exec sh -c '
  for x do
    ln -s "$x" /some/other/path/${x##*/};
  done' {} +

Se esse for seu comando find real, find não será útil aqui. O ponto de find é recorrer aos subdiretórios. Um achado não-recursivo às vezes pode ser útil para tirar proveito de seus filtros (por exemplo, para atuar somente em arquivos de um determinado tipo ou modificados dentro de um certo intervalo de tempo). Mas um find não recursivo sem filtragem é inútil.

for x in /some/path/*; do
  ln -s "$x" "/some/other/path/${x##*/}";
done' {} +

Ou

cd /some/path
for x in *; do
  ln -s "$PWD/$x" "/some/other/path/$x";
done' {} +

Como alternativa, com zsh :

autoload -U zmv
alias zln='zmv -L'
zln -s '/some/path/*' '/some/other/path/$f:t'
    
por 28.02.2016 / 21:14