Expansão da substring composta de Bash [duplicado]

1

Estou ciente de que posso usar awk para analisar vários delims, mas isso gera subprocessos. Eu queria saber se a expansão do parâmetro bash composto / aninhado é possível.

Eu tenho PDFs em um diretório chamado "Px_MM-DD-YY_SSSSSSSSSS.pdf", onde:

  • "Px" significa "Página x" e x não tem zeros à esquerda.
  • "MM" corresponde ao mês de dois dígitos, com um zero à esquerda, se aplicável.
  • "DD" corresponde ao dia de dois dígitos, com um zero à esquerda, se aplicável.
  • "YY" corresponde ao ano de dois dígitos, com um zero à esquerda, se aplicável.
  • "SSSSSSSSSS" corresponde ao tempo de dez dígitos da data em que o PDF foi criado, o que me permite manter as revisões das páginas em PDF.

Eu tenho um loop for (vou soltar "-mtime" quando estiver pronto para operar em todos os PDFs)

for file in $(find -type f -iname '*_??????????.pdf' -mtime -1)
do
    echo $file
done

onde quero repetir apenas o tempo da época.

Eu posso usar isso para loop

for file in $(find -type f -iname '*_??????????.pdf' -mtime -1)
do
    echo ${file##*_}
done

e para o arquivo com o nome "./P14_07-21-18_4X_1532144458.pdf", "1532144458.pdf" é exibido na tela.

Eu posso usar isso para loop

for file in $(find -type f -iname '*_??????????.pdf' -mtime -1)
do
    echo ${file%.*}
done

e para o arquivo com o nome "./P14_07-21-18_4X_1532144458.pdf", "./P14_07-21-18_4X_1532144458" é exibido na tela.

Se eu substituir a linha echo ... por qualquer um dos formatos abaixo

echo ${${file##*_}:0:10}
echo ${(${file##*_}):0:10}
echo ${${file##*_}%.*}
echo ${{file%.*}##_}
echo ${${file%.*}##_}

Eu recebo -bash: ... : bad substitution . Eu não estou obtendo a sintaxe correta ou a expansão bash aninhada / composta não é possível?

    
por user208145 21.07.2018 / 06:05

3 respostas

2

Você não pode realizar uma substituição aninhada com a variável na parte mais à esquerda . Então você pode fazer ${foo#$bar} , mas não o que você mostra.

Coloque o resultado da substituição em uma variável se você quiser usá-la em outras substituições.

    
por 21.07.2018 / 06:12
2

Você não pode ter uma substituição de parâmetro agindo sobre o resultado de outra substituição de parâmetro sem primeiro salvar o resultado inicial em uma variável e aplicando a segunda substituição a ele.

Você também faz um loop pela saída de find , que não é recomendado .

A maneira correta de fornecer um loop com o resultado de find é chamar um shell filho e fazer o loop lá:

find . -type f -iname '*_??????????.pdf' -mtime -1 -exec sh -c '
    for pathname do
        timestamp=${pathname##*_}   # remove up to last _
        timestamp=${timestamp%.pdf} # remove .pdf
        printf "pathname=%s\ttimestamp=%s\n" "$pathname" "$timestamp"
    done' sh {} +

Dessa forma, você não precisa se preocupar com os nomes dos caminhos reais. Nomes de arquivos (ou seja, nomes de arquivos e diretórios e outros tipos de arquivos) no Unix podem conter qualquer caractere diferente de / e find , por exemplo, espaço e nova linha. Usando uma substituição de comando em find , você força o shell a primeiro fazer a divisão de palavras (por padrão em espaços, tabulações e novas linhas) e em segundo lugar a geração de nome de arquivo nos padrões encontrados nos nomes de caminho retornados de %code% . Seu loop original pode, portanto, acabar dando um loop em palavras bem diferentes do que você esperaria.

Relacionados:

por 21.07.2018 / 10:10
0

Se eu entendi corretamente, o problema de usar o Awk é que você está invocando um processo Awk para cada arquivo PDF (suponho que você tenha um grande número deles).

Você pode executar algo nos moldes de

find . ...... -print0 | perl -0nE '/.*_(\d{10}).pdf/ and say "$1.pdf"'

Ou se você mantém sua estrutura:

for file in $(find .....| perl ....)
do 
  ...
done

(e, claro, substitua o comando Perl por qualquer Awk, sed, equivalente ao Python)

(Se você tiver a oportunidade de tentar esta abordagem, diga-nos o time .... obtido)

    
por 21.07.2018 / 12:41