Copie os arquivos para o novo diretório apenas se o nome do arquivo estiver em uma matriz

2

Eu não sou muito de zsh buff, mas imagino que isso seja fácil para alguém:

Eu posso copiar arquivos em massa de sourcedir para targetdir com o seguinte:

$ find sourcedir -type f -exec cp {} targetdir \;

Como é que posso apenas cp , por isso, se o nome do ficheiro corresponder a um dos seguintes:

top directory
  fileA            <= directories have same name as single file they enclose
    fileA.pdf                    <= file to move
  another file
    another file.pdf             <= do NOT move
  still133another4544file
    still133another4544file.pdf  <= file to move

Literalmente milhares de pessoas, mas só precisam mover alguns que eu tenho os nomes e podem colocar em um arquivo ou inserir diretamente no script.
Poderia permanecer um script de shell simples "Eu também aceitaria Ruby.

    
por Meltemi 17.10.2015 / 00:44

4 respostas

2

Primeiro de tudo, aqui está a árvore de exemplo que criei:

{   pwd
    for   d in ./*/
    do    cd "$d"
          printf '\t%s\n' "${d#??}"
          printf '\t\t%s\n' *
          cd ..
    done
}
/tmp/top
        another file/
                another file.pdf
        fileA/
                fileA.pdf
        otherdir/
                1
                2
                3
        stillanother4544file/
                stillanother4544file.pdf

Agora, obviamente, você não precisa fazer nada disso - criei a árvore para espelhar sua árvore de exemplo e adicionei alguns valores discrepantes.

Mas aqui está o que eu recomendo que você faça:

pax -rwis'|^./\([^/]*\)/\(\....\)$||' -s'|.*||' . ../tgt

Chama o utilitário pax padrão POSIX no modo -r ead / -w rite - modo cópia direta, basicamente. No modo de cópia pax não cria um tar archive como usualmente faria no modo -w rite, mas copia diretamente todos os arquivos encontrados em seu [...pattern...] argumento [s] - que está aqui apenas . para a árvore com raiz no diretório atual - e / ou os nomes dos quais ele lê em seu stdin - que está vazio neste caso - para o diretório nomeado como seu último argumento - que está aqui ../tgt .

Agora, eu sei, você não quer mover a árvore inteira. Muito disso é tratado com os argumentos -s ubstitution. Você pode renomear os arquivos de saída com base na expressão regular sed -style declarações como eu faço aqui. Eu uso duas declarações -s ubstitution. O primeiro renomeia todas as correspondências de entrada para:

./dirname/samename.???

... para ...

samename.???

... na saída, achatando a árvore de saída para colocar todos os arquivos copiados em ../tgt diretamente. Se preferir manter os diretórios ./dirname/ parent para cada um, você pode substituir a parte da primeira -s ubstitution por apenas & .

Depois que um nome de arquivo for correspondido e modificado com êxito por pax -s ubstitution, ele não será tentado novamente por nenhum argumento -s ubstitution restante e apenas os arquivos que ainda não foram correspondidos serão tentados para qualquer renomeação restante expressões regulares. Isso significa que todos os arquivos com raiz no diretório atual com nomes de caminho que se ajustam ao tipo:

./dirname/samename.???

... são selecionados e despojados com sucesso de todos, exceto de seus últimos componentes de nome de caminho com a primeira instrução -s ubstitution e todos os arquivos outros com raiz no diretório atual são substituídos completamente pelo segundo -s|.*|| -s ubstitution. Isso ocorre porque pax descarta de sua lista de processamento qualquer nome de arquivo que substitua uma cadeia nula.

Uma vez pax processou tudo o que tem um último dever a cumprir antes de poder realmente copiar os arquivos selecionados - e essa é a opção -i nteractive rename que eu especifiquei, e que eu acho que provavelmente está certa seu beco.

De man pax :

-i

  • Interactively rename files or archive members. For each archive member matching a pattern operand or each file matching a file operand, pax will prompt to /dev/tty giving the name of the file, its file mode, and its modification time. pax will then read a line from /dev/tty. If this line is blank, the file or archive member is skipped. If this line consists of a single period, the file or archive member is processed with no modification to its name. Otherwise, its name is replaced with the contents of the line. pax will immediately exit with a non-zero exit status if EOF is encountered when reading a response or if /dev/tty cannot be opened for reading and writing.
ATTENTION: pax interactive file rename operation.
-rw-r--r-- Oct 17 04:30 stillanother4544file.pdf
Input new name, or a "." to keep the old name, or a "return" to skip this file.
Input > .
Processing continues, name unchanged.

ATTENTION: pax interactive file rename operation.
-rw-r--r-- Oct 17 04:30 another file.pdf
Input new name, or a "." to keep the old name, or a "return" to skip this file.
Input >
Skipping file.

ATTENTION: pax interactive file rename operation.
-rw-r--r-- Oct 17 04:30 fileA.pdf
Input new name, or a "." to keep the old name, or a "return" to skip this file.
Input > .
Processing continues, name unchanged.

Assim, dos seis arquivos no diretório atual, apenas os três passam para os prompts de renomeação -i nteractive e, desses três, apenas dois chegam a ../tgt :

ls -l ../tgt
-rw-r--r-- 1 mikeserv mikeserv 0 Oct 17 04:30 fileA.pdf
-rw-r--r-- 1 mikeserv mikeserv 0 Oct 17 04:30 stillanother4544file.pdf
    
por 17.10.2015 / 14:54
2

Se você tiver esses nomes de arquivos salvos em uma matriz e nenhum deles contiver | (já que seria necessário escapar, mas não vale a pena ser incomodado - veja a alternativa abaixo) você poderia simplesmente juntar os elementos da matriz e usar o resultado como glob:

myarr=( file1 file2 ... fileN )
lst=${(j:|:)myarr}
cp -v -- **/($~lst) targetdir

por exemplo. com esses arquivos em lugares aleatórios sob meu /tmp :

caxZN.gif
e8ApF.gif
div2k.js.lzo
cmp2jz.ini

e seus nomes salvos em uma matriz, este é o resultado:

cp -v -- **/($~lst) targetdir
‘tmp/div2k.js.lzo’ -> ‘targetdir/div2k.js.lzo’
‘tmp/other dir/e8ApF.gif’ -> ‘targetdir/e8ApF.gif’
‘tmp/other dir/one two/cmp2jz.ini’ -> ‘targetdir/cmp2jz.ini’
‘tmp/some dir/caxZN.gif’ -> ‘targetdir/caxZN.gif’

Como alternativa, desta vez assumindo que eles estão listados em um arquivo, um nome de arquivo por linha, você pode ler as linhas em uma matriz e usar qualifiers / e string e modificadores para glob / selecione apenas os nomes de arquivos em sua matriz:

mylist=(${(f)"$(<list_of_files)"})
cp -- **/*(.e_'(($mylist[(Ie)$REPLY:t]))'_) targetdir

praticamente . seleciona apenas arquivos regulares (adicione D se sua lista contiver dotfiles) e e_'expression'_ retorna apenas os nomes de arquivo para os quais expression é true, neste caso se basename ( $REPLY:t ) é um elemento da matriz mylist , por exemplo :

print -rl -- **/*(.e_'(($mylist[(Ie)$REPLY:t]))'_)
tmp/div2k.js.lzo
tmp/other dir/e8ApF.gif
tmp/other dir/one two/cmp2jz.ini
tmp/some dir/caxZN.gif

Claro, todos os itens acima assumem zsh .
De qualquer forma, sua própria solução também funciona, desde que você reserve um tempo para escrever os nomes dos arquivos e passar as opções corretas para find , por exemplo,

find . -type f \( -name caxZN.gif -o -name e8ApF.gif -o -name div2k.js.lzo -o -name cmp2jz.ini \) -exec cp -- {} targetdir \;
    
por 17.10.2015 / 17:54
1
$ find sourcedir -type f -name 'file?.pdf' -exec cp {} targetdir \;

deve corresponder apenas aos arquivos correspondentes a esse padrão.

    
por 17.10.2015 / 00:49
0

Se eu entendi corretamente, cada um dos seus diretórios tem apenas um único arquivo e você deseja mover esse arquivo se ele estiver na sua lista. Se sim, você pode fazer:

while read -r file; do cp sourcedir/*/"$file" targetdir; done < list.txt

Se você também precisar copiar os diretórios, leia os arquivos, tire a extensão do arquivo para obter o nome do diretório e copie isso:

while read -r file; do cp -r sourcedir/"${file%.*}" targetdir; done < list.txt
    
por 17.10.2015 / 12:57