Iterar todos os arquivos na pasta com bash

Iterar todos os arquivos na pasta com bash

Eu tenho vários arquivos markdown em minha pasta que gostaria de converter para html com pandoc.

O comando que normalmente uso é

pandoc Atten.md -f markdown -t html --css pandoc.css -o Atten.html

onde Atten.mdestá o nome de um arquivo markdown típico. No entanto, gostaria de iterar todos os arquivos e converter .md em .html conforme feito acima. Como faço isso no bash ou zsh?

Responder1

Com find:

find path/to/dir -name "*.md" -type f -exec sh -c '
      pandoc -f markdown -t html --css pandoc.css -o "${1%.*}.html" "$1"
   ' find-sh {} \;

Deve funcionar em Bash, Zsh e muitos outros shells. A única tarefa do shell externo é executar findcom argumentos adequados.

Esta versão gera um único shpara processar tantos arquivos quanto possível (mais shprocessos serão gerados se necessário):

find path/to/dir -name "*.md" -type f -exec sh -c '
      for f; do
         pandoc -f markdown -t html --css pandoc.css -o "${f%.*}.html" "$f"
      done
   ' find-sh {} +

Notas:

  • O shell interno é invocado apenas porque precisamos remover a extensão antiga. Se não fosse o requisito, uma solução … -exec pandoc … -o {}.html {} \;provavelmente funcionaria. "Provavelmente" porque o POSIX exige finda substituição de um único arquivo {}. É uma "cortesia" da findimplementação que você usa, se algo mais for substituído (como {}.htmlou a segunda aparição de {}).
  • -execdirem vez de -execseria melhor, mas não é POSIX.
  • -execdirnos permitiria usar ./"$1"em vez de "$1"(respectivamente: ./"$f"em vez de "$f") para evitar que fosse interpretado por pandoccomo uma opção. Outra abordagem é--mas não sei se pandocapoia. Acho que no seu caso "$1"não é possível expandir para nada que comece com -porque deve começar com path/to/dire, se path/to/diriniciado com -, seria mal interpretado findem primeiro lugar. Em geral, embora esta preocupação seja legítima.
  • Na pandocinvocação, movi o operando para o final apenas por causa de minha preferência pela ordem mais comum de command -options operands.

  • A solução é recursiva. Para torná-lo não recursivo, especifique -maxdepth 1. Se você findnão apoia -maxdepthentão estudeesseou solte finde deixe o shell externo iterar sobre os arquivos:

    # written for Bash
    ( cd path/to/dir || exit 125
    for f in ./*.md ./.*.md; do
        [ -f "$f" ] &&
        pandoc -f markdown -t html --css pandoc.css -o "${f%.*}.html" "$f"
    done )
    

    Notas específicas para o código acima:

    • O subshell deve cdfuncionar sem afetar o shell atual.
    • Se cdfalhar por qualquer motivo, o restante não será executado. Esta é uma boa prática.
    • ./*.mdem vez de simples *.mdimpede "$f"que seja interpretado como uma opção posteriormente.
    • ./.*.mdé adicionado porque ./*.mdnão corresponde a dotfiles.
    • [ -f "$f" ]é prevenir
      • invocando pandocdiretórios, arquivos especiais e outros;
      • invocando pandocinexistente ./*.md(ou ./.*.md) quando nada corresponde ao padrão.
    • Zsh é um pouco diferente. O código deve funcionar she, como um subshell é útil de qualquer maneira, não faria muita diferença se você chamasse shexplicitamente de qualquer shell sensato:

      sh -c 'cd path/to/dir || exit 125
             for …
             done '
      

Responder2

Você deve usar finde seu -exec(vejahomem encontrar):

find YourDirectory -name \*.md | \
  while IFS= read fn; do \
  pandoc ${fn} -f markdown -t html --css pandoc.css -o ${fn%.md}.html ; done

Ou talvez -execdir.

Responder3

Você pode iterar o arquivo com um for:

for item in *.md; do CONVERT_FILE_COMMAND $item "$(basename '$item' .md).html; done

informação relacionada