Por que o 'zip' em um loop for funciona quando o arquivo existe, mas não quando não existe?

7

Eu tenho um diretório que contém vários subdiretórios. Existe uma pergunta sobre compactar os arquivos que contém uma resposta que eu modifiquei para as minhas necessidades.

for i in */; do zip "zips/${i%/}.zip" "$i*.csv"; done

No entanto, me deparo com um problema bizarro. Para o primeiro conjunto de pastas, em que zips/<name>.zip não existe, recebo este erro:

zip error: Nothing to do! (zips/2014-10.zip)
        zip warning: name not matched: 2014-11/*.csv

no entanto quando eu apenas echo as declarações zip:

for i in */; do echo zip "zips/${i%/}.zip" "$i*.csv"; done

Em seguida, execute o comando echoed ( zip zips/2014-10.zip 2014-10/*.csv ), ele funciona bem e fecha a pasta. Então, a parte divertida disso é que as execuções subseqüentes do comando original irão zipar pastas que não funcionaram na primeira vez!

Para testar esse comportamento você mesmo:

cd /tmp
mkdir -p 2016-01 2016-02 2016-03 zips
for i in 2*/; do touch "$i"/one.csv; done
for i in 2*/; do touch "$i"/two.csv; done
zip zips/2016-03.zip 2016-03/*.csv
for i in 2*/; do echo zip "zips/${i%/}.zip" "$i*.csv"; done
for i in 2*/; do zip "zips/${i%/}.zip" "$i*.csv"; done

Você verá que o eco imprime estas declarações:

zip zips/2016-01.zip 2016-01/*.csv
zip zips/2016-02.zip 2016-02/*.csv
zip zips/2016-03.zip 2016-03/*.csv

No entanto, o comando zip atual informa:

        zip warning: name not matched: 2016-01/*.csv

zip error: Nothing to do! (zips/2016-01.zip)
        zip warning: name not matched: 2016-02/*.csv

zip error: Nothing to do! (zips/2016-02.zip)
updating: 2016-03/one.csv (stored 0%)
updating: 2016-03/two.csv (stored 0%)

Portanto, ele está realmente atualizando o arquivo zip com o .csv s onde o arquivo zip existe, mas não quando o arquivo zip é criado. E se você copiar um dos comandos zip:

$ zip zips/2016-02.zip 2016-02/*.csv
adding: 2016-02/one.csv (stored 0%)
adding: 2016-02/two.csv (stored 0%)

Em seguida, execute novamente o zip-all-the-things:

for i in 2*/; do zip "zips/${i%/}.zip" "$i*.csv"; done

Você verá que ele é atualizado para 2016-02 e 2016-03 . Aqui está minha saída de tree :

.
├── 2016-01
│   ├── one.csv
│   └── two.csv
├── 2016-02
│   ├── one.csv
│   └── two.csv
├── 2016-03
│   ├── one.csv
│   └── two.csv
└── zips
    ├── 2016-02.zip
    └── 2016-03.zip

Além disso, (des) surpreendentemente, isso funciona bem:

zsh -c "$(for i in 2*/; do echo zip "zips/${i%/}.zip" "$i*.csv"; done)"

O que estou fazendo de errado aqui? (note que estou usando zsh em vez de bash, se isso faz alguma diferença)

    
por Wayne Werner 17.08.2016 / 14:32

1 resposta

14

Expansão pelo shell

As aspas em torno de "$i*.csv" fazem a diferença. Com as aspas, o shell expande essa string para "2014-11 / *. Csv". Esse arquivo exato não existe e zip relata um erro. Sem aspas, o * também se expande (via expansão de nome de arquivo / "globbing"), e o comando zip resultante é uma lista completa de arquivos correspondentes, cada um como um argumento separado. Você pode obter o segundo comportamento, dentro do for loop, com:

for i in */ ; do zip "zips/${i%/}.zip" "$i"*.csv ; done

Expansão por zip

zip também pode expandir curingas para si mesmo, mas não em todas as situações. Do manual do zip :

The zip program can do the same matching on names that are in the zip archive being modified or, in the case of the -x (exclude) or -i (include) options, on the list of files to be operated on, by using backslashes or quotes to tell the shell not to do the name expansion.

O comando original funciona em tentativas subsequentes, depois de você ter criado um arquivo com êxito, porque zip tenta corresponder os curingas ao conteúdo do arquivo existente. Eles existem lá e ainda existem no sistema de arquivos, então eles são reportados com updating: .

Para obter zip para manipular os curingas quando criar o arquivo morto, use a opção -r (recurse) para recursar no diretório solicitado e -i (include) para limitá-lo para arquivos que correspondem ao padrão:

 for i in */ ; do zip -r "zips/${i%/}.zip" "$i" -i '*.csv' ; done
    
por 17.08.2016 / 15:21