Você tem dois problemas, ambos relacionados à ordem em que as coisas acontecem.
find . -name *.md -exec ln -vs $(dirname {}) "$OUTPUT_DIR" \; é um único comando. O shell analisa antes de executá-lo. Entre o passo em análise:
-
$(…)é uma substituição de comando, portanto,dirname {}é executado, gerando.(não há uma parte do diretório em{}). -
$OUTPUT_DIRé uma substituição de variável, então ela é substituída pelo seu valor. -
*.mdé um padrão glob, sendo substituído pela lista de arquivos correspondentes. Se não houver correspondências, o padrão permanece no lugar.
Isso conclui o trabalho necessário para determinar qual comando executar.
- Se não houver arquivos correspondentes a
*.mdno diretório atual, o seguinte comando será executado com os argumentos fornecidos:find,.,-name,*.md,-exec,ln,-vs,.,/Users/me/OfflineFolder,;. - Se
*.mdcorresponder abar.mdefoo.md, o comando seráfind,.,-name,foo.md,-exec,… (efindperderá qualquer arquivo chamadosomething-other-than-foo.mdem subdiretórios). - Se o
*.mdcorresponder abar.mdefoo.md, o comando seráfind,.,-name,bar.md,foo.md,-exec,… (efindserá reclamar de um erro de sintaxe).
Você deseja executar dirname nos resultados da pesquisa. Isso significa que você precisa instruir find para executar dirname . Você pode fazer isso, mas encontrar não possui nenhum mecanismo para reunir a saída de dirname e passá-la como um argumento para ln . Para isso você precisa de uma ferramenta como um shell, onde é uma substituição de comando.
Então aqui está a estratégia: tell find para invocar um shell e dizer ao shell para executar o comando envolvendo ln e dirname . Você precisa cuidar de citar. Coloque o comando shell entre aspas simples para evitar que seus caracteres especiais sejam interpretados pelo shell externo. Coloque também o padrão para -name entre aspas, de modo que seja passado para find e não expandido pela camada externa.
find . -name '*.md' -exec sh -c 'ln -vs …' \;
O próximo passo é completar o … . Não use {} dentro do comando shell: isso apenas colocaria o nome do arquivo como um trecho de código shell e qualquer caractere especial seria analisado pelo shell interno. Em vez disso, passe o nome do arquivo dado por find como um argumento para o script de shell. O primeiro argumento após sh -c CODE é o nome da instância do shell ( $0 ), mas você pode usá-lo para qualquer propósito que desejar; argumento subseqüente são os parâmetros posicionais ( $1 , $2 ,…).
find . -name '*.md' -exec sh -c 'ln -vs "$(dirname "$0")" "$1"' {} "$OUTPUT_DIR" \;
Eu passei $OUTPUT_DIR como argumento para o script. Não importa aqui, porque o valor não contém caracteres especiais de shell, mas é um bom hábito de entrar, você nunca sabe quando alguém pode mudar o caminho para incluir espaços. Outra possibilidade seria passá-lo pelo meio ambiente:
export OUTPUT_DIR
find . -name '*.md' -exec sh -c 'ln -vs "$(dirname "$0")" "$OUTPUT_DIR"' {} \;
Em vez de dirname , você pode usar uma substituição textual: remova tudo após a última barra. Você não precisa se preocupar com o caso especial em que não há uma parte do diretório, pois isso não acontece com um nome de arquivo passado por find .
export OUTPUT_DIR
find . -name '*.md' -exec sh -c 'ln -vs "${0%/*}" "$OUTPUT_DIR"' {} \;
Você pode usar a forma + de -exec para acelerar um pouco as coisas. Eu passo _ as $0 e os argumentos subseqüentes são os nomes dos arquivos que o loop for itera.
export OUTPUT_DIR
find . -name '*.md' -exec sh -c 'for x; do ln -vs "${x%/*}" "$OUTPUT_DIR"; done' _ {} +