Bash: move arquivos de padrão específico

1

Eu escrevo um script que faz iteração sobre um conjunto de arquivos zip, os extrai e move apenas arquivos que correspondem a um padrão de nome de arquivo específico para outra pasta.

O script:

#!/bin/bash

ARCHIVE_FILEMASK="????-??-??_E_MDT_0_0.7z"
FILEMASK="?????????_?????_A_?_????????????????-?????????"
extractDir=/path/to/dir
dest=/path/to/dest

for f in ${ARCHIVE_FILEMASK}
do
    7z e -aoa -o"${extractDir}" "$f"
    if [ $? != 0 ]
    then
        mv "${extractDir}/${FILEMASK}".xml "$dest"
    fi
done

Quando o script é executado, ocorre um erro:

mv: cannot stat '/path/to/dir/?????????_?????_A_?_????????????????-?????????.xml': No such file or directory

Eu já tentei ativar o nullglob e o extglob, mas isso não ajudou. Se eu executar o comando diretamente do shell, ele funcionará bem. As respostas de outros posts com problemas semelhantes também não ajudaram.

editar: Depois que Paweł Rumian mencionou possíveis problemas com espaços em nomes de diretórios, eu tentei a dica do AVJ e adicionei set +x antes do comando mv .

O resultado é:

+ mv '/path/to/dir/?????????_?????_A_?_????????????????-?????????.xml' /path/to/dest

Então, obviamente, eu tenho que me livrar das citações simples agora. Alguma ideia de como?

    
por twenty7 23.09.2015 / 10:40

4 respostas

0

O conselho usual para colocar aspas duplas em torno de expansões de variáveis é porque $foo de aspas externas significa "pegar o valor de foo , dividi-lo em espaços em branco" e expandir cada palavra resultante como um padrão curinga se corresponder a pelo menos um arquivo ”, em vez de“ pegar o valor de foo ”. Em particular, quando a variável foo contiver um nome de arquivo, "$foo" é o que você precisa. Mas aqui as variáveis ARCHIVE_FILEMASK e FILEMASK contêm padrões de curinga. Então, de fato, você quer expandi-los quando eles são usados, portanto você deve usá-los sem aspas. Eles serão, portanto, interpretados como uma lista de padrões separados por espaços em branco; isso não faz diferença aqui, já que você não tem espaço em branco no valor dessas variáveis.

As variáveis f e extractDir contêm nomes de arquivos, portanto devem permanecer entre aspas duplas.

for f in ${ARCHIVE_FILEMASK}
do
    if 7z e -aoa -o"${extractDir}" "$f"; then
        mv "${extractDir}/"${FILEMASK}.xml "$dest"
    fi
done

Como você está usando o bash, pode chamar shopt -s nullglob para tornar o padrão curinga "${extractDir}/"${FILEMASK}.xml expandido em uma lista vazia se não corresponder a nenhum arquivo. O comando mv irá reclamar que está faltando um arquivo se você não passar nenhum arquivo de origem; você pode resolver esse problema invocando-o apenas se houver pelo menos uma correspondência.

shopt -s nullglob
for f in ${ARCHIVE_FILEMASK}
do
    if 7z e -aoa -o"${extractDir}" "$f"; then
        matches=("${extractDir}/"${FILEMASK}.xml)
        if ((${#matches[@]})); then
            mv "${matches[@]}" "$dest"
        fi
    fi
done

¹ Ou mais geralmente conforme indicado por IFS .

    
por 24.09.2015 / 02:27
0

A solução foi remover os qoutes duplos. Então agora o comando Mover se parece com:

mv ${extractDir}/${FILEMASK}.xml "$dest"
    
por 23.09.2015 / 11:03
0

É porque o comando mv não consegue encontrar o arquivo de origem. Pode haver um caractere especial ou algo no nome do arquivo de origem.

Execute o script colocando set -x antes do comando mv e defina + x após o comando mv.

Ele mostrará o comando exato que está sendo executado durante a execução do script. Verifique executando esse comando diretamente do shell.

    
por 23.09.2015 / 15:42
0

Os arquivos .7z contêm mais de uma instância do mesmo nome de arquivo? Eu presumo que não, porque você está enganando todos eles para o mesmo diretório "$ dest".

Nesse caso, é melhor ter o comando mv fora do loop, em parte porque você executará apenas um mv do total de comandos (em vez de um mv por arquivo .7z), mas principalmente porque é mais provável que haja pelo menos um arquivo correspondente ao seu padrão. E você consegue manter as aspas duplas em volta das variáveis, o que é muito mais seguro se houver nomes de arquivos com caracteres significativos de shell ou glob neles.

Se houver muitos arquivos extraídos para um único comando mv , você poderá usar find , algo assim:

find "$extractDir" -name "$FILEMASK.xml" -exec mv {} "$dest/" +

Provavelmente, é melhor usar find - você nunca sabe com antecedência quantos arquivos serão extraídos e isso é uma programação útil defensiva. Ele também tem a vantagem de não fazer exatamente nada se não houver arquivos correspondentes encontrados, então mv não irá reclamar sobre falta de arquivos.

    
por 24.09.2015 / 00:25