Como copiar cada quarto arquivo em uma pasta

15

Eu tenho muitos arquivos em uma pasta, chamada like 00802_Bla_Aquarium_XXXXX.jpg. Agora eu preciso copiar cada quarto arquivo para uma subpasta, dizendo selected/.

00802_Bla_Aquarium_00020.jpg <= this one
00802_Bla_Aquarium_00021.jpg
00802_Bla_Aquarium_00022.jpg
00802_Bla_Aquarium_00023.jpg
00802_Bla_Aquarium_00024.jpg <= this one
00802_Bla_Aquarium_00025.jpg
00802_Bla_Aquarium_00026.jpg
00802_Bla_Aquarium_00027.jpg
00802_Bla_Aquarium_00028.jpg <= this one
00802_Bla_Aquarium_00029.jpg

Como eu faço isso?

aebersold
fonte
Você pode fazer o mesmo que as soluções para superuser.com/q/396536/87552 em algumas saídas de ls. Consulte também stackoverflow.com/q/4553751/789593, que pergunta sobre cada segunda linha.
NN
Se eles estiverem numerados e você souber o último número, poderá considerar uma variação defor n in $(seq -w 20 4 200); do cp "00802_..._00${n}" ...; done
Ulrich Schwarz

Respostas:

12

Com o zsh, você pode fazer:

n=0; cp 00802_Bla_Aquarium_?????.jpg(^e:'((n++%4))':) /some/place

POSIX, mesma idéia, apenas um pouco mais detalhada:

# put the file list in the positional parameters ($1, $2...).
# the files are sorted in alphanumeric order by the shell globbing
set -- 00802_Bla_Aquarium_?????.jpg

n=0
# loop through the files, increasing a counter at each iteration.
for i do
  # every 4th iteration, append the current file to the end of the list
  [ "$(($n % 4))" -eq 0 ] && set -- "$@" "$i"

  # and pop the current file from the head of the list
  shift
  n=$(($n + 1))
done

# now "$@" contains the files that have been appended.
cp -- "$@" /some/place

Como esses nomes de arquivos não contêm caracteres em branco ou curinga, você também pode:

cp $(printf '%s\n' 00802_Bla_Aquarium_?????.jpg | awk 'NR%4 == 1') /some/place
Stéphane Chazelas
fonte
Você pode por favor coloque comentário também em código posix
Rahul Patil
@ChrisDown, for i doé padrão e, na verdade, mais portátil do que for i; doe é a maneira canônica de percorrer os parâmetros posicionais.
Stéphane Chazelas
7

No bash, uma possibilidade engraçada, que funcionará bastante bem aqui:

cp 00802_Bla_Aquarium_*{00..99..4}.jpg selected

Essa é definitivamente a resposta mais curta e eficiente: sem subcamadas, sem laços, sem tubos, sem awkprocesso externo na enfermaria; apenas uma bifurcação cp(que você não pode evitar de qualquer maneira) e uma expansão e glob do bash brace (dos quais você pode se livrar completamente, já que sabe quantos arquivos possui).

gniourf_gniourf
fonte
4
… E 2 suposições: os arquivos são numerados continuamente e o primeiro é divisível com 4.
manatwork
@ manatwork não tem problema se o primeiro não for divisível por 4: por exemplo, cp 00802_Bla_Aquarium_*{03..99..4}.jpg selectedse você quiser 3 mod 4. (Estou usando o maravilhoso fato de que 100 é divisível por 4). Agora, sobre arquivos numerados continuamente, todas as outras respostas assumem o mesmo.
precisa saber é o seguinte
Quais outras respostas assumem numeração contínua? Encontrei apenas o comentário de Ulrich Schwarz e nenhuma resposta.
precisa saber é o seguinte
5

Simplesmente com o bash, você pode:

n=0
for file in ./*.jpg; do
   test $n -eq 0 && cp "$file" selected/
   n=$((n+1))
   n=$((n%4))
done

O padrão ./*.jpgserá substituído por uma lista alfabética de nomes de arquivos, conforme declarado pelo bash man, portanto, ele deve se adequar ao seu objetivo.

jfg956
fonte
Em bash, a sua linha de três no interior do forloop pode ser substituído com apenas esta linha: (((++n)%4)) || cp "$file" selected/. ;-). O problema, porém, é que você está criando um cppara cada arquivo que não é realmente eficiente.
precisa saber é o seguinte
1

Se você rubyinstalou, pode usar a seguinte linha única. Observe que assume que o diretório selecionado existe.

ruby -rfileutils -e 'files = Dir.glob("*.jpg").each_slice(4) { |file| FileUtils.cp(file.first, "selected/" + file.first) }'

Ele pega uma lista de todos os arquivos com a extensão .jpg no diretório atual e as divide em listas de quatro elementos e copia o primeiro elemento de cada lista para o diretório selecionado no diretório atual.

NN
fonte
1

Se você sabe que não terá novas linhas nos nomes dos arquivos, use:

find . -maxdepth 1 -name "*.jpg" | sort | while IFS= read -r file; do
  cp "$file" selected/
  IFS= read -r; IFS= read -r; IFS= read -r
done
jfg956
fonte
@ Gilles: obrigado pela edição. Eu removi a variável de arquivo na leitura 3, porém, como quero ressaltar que elas não são necessárias.
jfg956
1

Você pode usar um gerenciador de arquivos da GUI e redimensionar a borda da janela até que cada 5 arquivos ocupem uma linha e, em seguida, use o mouse para selecionar a primeira coluna ...

John Chain
fonte
-1

Observando stackoverflow.com/q/4553751/789593, que oferece uma solução para copiar todos os segundos como o @NN mencionado, uma solução muito simples seria:

  • Remova o primeiro arquivo da pasta inicial
  • Copie cada segundo da pasta inicial para uma pasta intermediária
  • Copie todos os segundos da pasta intermediária para a pasta final
  • Adicione o primeiro arquivo manualmente

Pode não ser muito eficiente, pois exige uma etapa extra de cópia, pois deve ser muito fácil de executar.

Dennis Jaheruddin
fonte