Xargs para extrair o nome do arquivo

3

Eu gostaria de encontrar todos os arquivos .html em uma pasta e anexar [file](./file.html) a outro arquivo chamado index.md . Eu tentei o seguinte comando:

ls | awk "/\.html$/" | xargs -0 -I @@ -L 1 sh -c 'echo "[${@@%.*}](./@@)" >> index.md'

Mas não é possível substituir @@ no comando? O que estou fazendo errado?

Observação: o nome do arquivo pode conter caracteres válidos, como espaço

Esclarecimento:

index.md teria cada linha com [file](./file.html) , em que file é o nome real do arquivo na pasta

    
por Nikhil 03.09.2018 / 13:14

3 respostas

13

Basta fazer:

for f in *.html; do printf '%s\n' "[${f%.*}](./$f)"; done > index.md

Use set -o nullglob ( zsh , yash ) ou shopt -s nullglob ( bash ) para *.html para expandir para nada em vez de *.html (ou relatar um erro em zsh ) quando houver nenhum arquivo html . Com zsh , você também pode usar *.html(N) ou ksh93 ~(N)*.html .

Ou com uma chamada printf com zsh :

files=(*.html)
rootnames=(${files:r})
printf '[%s](./%s)\n' ${basenames:^files} > index.md

Observe que, dependendo da sintaxe de marcação que você está usando, talvez seja necessário codificar em HTML a parte título e codificar em URI a parte URI se os nomes de arquivo contiverem alguns caracteres problemáticos. Não fazê-lo pode até acabar introduzindo uma forma de vulnerabilidade XSS dependendo do contexto. Com o ksh93, você pode fazer isso com:

for f in *.html; do
  title=${ printf %H "${file%.*}"; }
  title=${title//$'\n'/"<br/>"}
  uri=${ printf '%#H' "$file"; }
  uri=${uri//$'\n'/%0A}      
  printf '%s\n' "[$title]($uri)"
done > index.md

Em que %H ¹ faz a codificação HTML e %#H da codificação URI, mas ainda precisamos abordar os caracteres de nova linha separadamente.

Ou com perl :

perl -MURI::Encode=uri_encode -MHTML::Entities -CLSA -le '
  for (<*.html>) {
     $uri = uri_encode("./$_");
     s/\.html\z//;
     $_ = encode_entities $_;
     s:\n:<br/>:g;
     print "[$_]($uri)"
  }'

Usando <br/> para caracteres de nova linha. Você pode preferir usar ␤ ou, em geral, decidir sobre alguma forma de representação alternativa para caracteres não imprimíveis.

Existem algumas coisas erradas no seu código:

  • analisando a saída de ls
  • use $ para ser literal entre aspas duplas
  • Usando awk para algo que grep pode fazer (não errado, mas exagerado)
  • use xargs -0 quando a entrada não for delimitada por NUL
  • -I está em conflito com -L 1 . -L 1 é executar um comando por linha de entrada, mas com cada palavra na linha passada como argumentos separados, enquanto -I @@ executa um comando para cada linha de entrada com a linha completa (menos os espaços à direita e citando ainda processados ) usado para substituir @@ .
  • usando {} dentro do argumento código de sh ( vulnerabilidade de injeção de comando )
  • Em sh , o var em ${var%.*} é um nome da variável , não funcionará com texto arbitrário.
  • use echo para dados arbitrários.

Se você quiser usar xargs -0 , precisará de algo como:

printf '%s
for f in *.html; do printf '%s\n' "[${f%.*}](./$f)"; done > index.md
' * | grep -z '\.html$' | xargs -r0 sh -c ' for file do printf "%s\n" "[${file%.*}](./$file)" done' sh > file.md
  • Substituindo ls por printf '%sawk' * para obter uma saída delimitada por NUL
  • grep -z com xargs -r0 (extensão GNU) para processar essa saída delimitada por NUL
  • -n (extensões GNU) sem qualquer -L / -I / sh , porque enquanto estamos gerando um xargs , também podemos processá-lo com o maior número possível de arquivos
  • tem sh passando as palavras como argumentos extras para for file do (que se tornam os parâmetros posicionais dentro do código embutido), não dentro do argumento de código. / li>
  • o que significa que podemos armazená-los mais facilmente em variáveis (aqui com ${param%pattern} , que faz um loop sobre os parâmetros posicionais por padrão) para que possamos usar o operador de expansão de parâmetro printf .
  • use echo em vez de for .

Não é preciso dizer que faz pouco sentido usar isso em vez de fazer esse *.html loop diretamente sobre os arquivos %code% , como no exemplo acima.

¹ Parece que não funciona corretamente para caracteres multibyte na minha versão do ksh93 (ksh93u + em um sistema GNU)

    
por 03.09.2018 / 13:24
9

Não analisar ls .
Você não precisa de xargs para isso, você pode usar find -exec .

tente isso,

find . -maxdepth 1 -type f -name "*.html" -exec \
    sh -c 'f=$(basename "$1"); echo "[${f%.*}]($1)" >> index.md' sh {} \;

Se você quiser usar xargs , use esta versão muito semelhante:

find . -maxdepth 1 -type f -name "*.html" -print0 | \
    xargs -0 -I{} sh -c 'f=$(basename "$1"); echo "[${f%.*}]($1)" >> index.md' sh {} \;

Outra maneira sem executar xargs ou -exec :

find . -maxdepth 1 -type f -name "*.html" -printf '[%f](./%f)\n' \
    | sed 's/\.html\]/]/' \
    > index.md
    
por 03.09.2018 / 13:23
1

Você realmente precisa de xargs ?

ls *.html | perl -pe 's/.html\n//;$_="[$_](./$_.html)\n"'

(Se você tiver mais de 100000 arquivos):

printf "%s\n" *.html | perl -pe 's/.html\n//;$_="[$_](./$_.html)\n"'

ou (mais lento, mas mais curto):

for f in *.html; do echo "[${f%.*}](./$f)"; done
    
por 03.09.2018 / 22:46