Estou tentando copiar os cinco maiores arquivos de um determinado diretório para o meu arquivo pwd
. Usar cp specific/directory$(ls -S specific/directory | head -n) ./
copia o primeiro arquivo e, em seguida, produz cannot stat
erros para o restante dos arquivos na lista.
Por que o tubo está funcionando para o primeiro item e falhando para o resto?
Todas as minhas soluções tratam apenas arquivos , conforme solicitado, e podem tratar todos os tipos de arquivos
(mesmo com caracteres especiais).
Se você quiser usar
ls -S
Faça isso da forma correta:
Exigir recente
GNU
coreutils
.coreutils 9.1-1
aqui.Outra maneira, usando
bash
e recenteGNU
find
:findutils 4.9.0-4
aqui.Com base nisso :
${files[@]:0:5}
está expandindo para os primeiros 5 elementos da matriz de arquivos cujas chaves são maiores ou iguais a 0.para ferramentas mais antigas via
Perl
em qualquer shellUsando
zsh
você pode evitar todas as armadilhas associadas à análise e classificação da saída dels
:ou com GNU
cp
(para a-t
opção):onde estão os qualificadores glob
.
corresponde apenas a arquivos simples (não diretórios, links simbólicos, fifos, soquetes).D
alterne a opção dotglob - omita isso se quiser excluir arquivos ocultosOL[1,5]
ordena os resultados por tamanho de arquivo (tamanho) e seleciona os 5 primeirose a
-n
opção impedecp
a destruição de arquivos existentes no caso de uma colisão de nomes.EDIT: Nova resposta, funciona de forma mais completa:
A razão pela qual o original falha é que o nome do diretório é adicionado apenas ao primeiro resultado, portanto, os resultados restantes, não existentes no diretório atual, causam os erros relacionados à ausência desses arquivos.
Uma forma que funciona sem
find
é aproveitar a-F
opção tols
, que inclui caracteres à direita indicando os tipos de inodes. A seguir está uma resposta incompleta que remove os diretórios da listagem viagrep
; uma resposta mais completa removeria outros tipos de inode que deveriam ser excluídos. Ossed
comandos removem os*
executáveis adicionados por-F
.RESPOSTA ORIGINAL:
Suponha que os maiores arquivos sejam um, dois, três e quatro. O comando na pergunta acaba sendo
Como dois, três e quatro não saem em ., o comando falha. Algo semelhante a
faria isso.
AVISO: Isso será interrompido se houver novas linhas em qualquer um dos nomes de arquivo (ou se seus
ls
nomes de arquivo forem alterados, mesmo quando não estiverem sendo impressos no terminal).Para integrar as outras respostas:
TL; DR: Veja abaixo soluções viáveis para
bash
e shells POSIX.Porque o shell não se comporta como seu comando supõe.
A
$(ls -S | head)
substituição de comando é realmente substituída por sua saída e é colada imediatamente adjacente à direita docp specific/directory
trecho, mas:IFS
variável; este último é definido por padrão comols -S | head
comandos usam para separar cada nome de arquivo; portanto, cada nome acaba sendo um caminho independente separado ao seucp
comando; observe que, neste caso, colocar aspas duplas na substituição de comando não ajudaria, como você provavelmente já descobriuspecific/directory/
trecho para cada um dos nomes; (esse seria o trabalho para uma expansão Brace, mas seria complicado acertar neste caso); portanto, apenas o primeiro dos nomes separados recebe o prefixo do diretório e é, portanto, acessível porcp
, enquanto os outros 4 nomes devem estar presentes no diretório atual, mas obviamente eles não estão (e mesmo se estivessem,cp
teriam então reclamou que eles eram de fato os mesmos arquivos que os do diretório de destino./
)Poderia ser feito para "funcionar"? em princípio sim, mas seria frágil porque desmoronaria assim que um dos n-files contivesse um dos caracteres especificados na
IFS
variável; pior ainda, se combinado com um descontrolado,eval
pode ser usado para a mais clássica das injeções de comando se você não tiver controle total dos nomes de arquivo emspecific/directory
. (Além disso, veja a nota 1 abaixo).Possíveis soluções para
bash
e shells POSIXAlém da
ls --zero
solução disponível ao usar GNU coreutils v9.0 em diante, conforme mencionado em outras respostas, a operação também pode ser feita com segurança 1 com GNUls
de coreutils v8.25 (por volta de 2016) em diante, que fornece as--quoting-style
variantes para shells. Para isso precisamos usareval
, pois esta é de fato a única forma de se beneficiar dessals
opção que de fato foi projetada para funcionareval
.Como de costume,
eval
precisa ser manuseado com cuidado extra, se for o caso. Aqui, estamos usando-o exclusivamentels
apenas para o comando e confiando emls
citar nomes de arquivos corretamente para o shell conforme o comportamento documentado. Para cuidados adicionais, pode-se invocar, por exemplo,/bin/ls
o caminho completo explícito para umls
executável fornecendo a--quoting-style
opção desejada, em vez de arriscar usar quem sabe qualls
está$PATH
ou talvez quem sabe qual função não autorizada exportada (ou mesmo alias) nomeada propositalmentels
.Assim, com
bash
:Você pode alterar facilmente o número dos primeiros n-arquivos alterando a extensão
head -n 5
.Observe que no trecho acima eu adicionei segurança extra e verificações de erro, mas pragmaticamente a coisa toda pode ser reduzida aos comandos essenciais, se você for absolutamente positivo sobre sua
ls
versão e não tiver nenhum motivo real para falhar ou exibir caracteres perdidos .Um equivalente da solução acima feita para shells POSIX também pode funcionar com segurança 1 embora não seja totalmente ideal, pois precisa carregar na memória toda a lista de arquivos apresentada pelo
ls
comando. Como não podemos filtrar essa lista antes de chegar ao shell, o diretório de origem não deve conter tantos arquivos para preencher a memória disponível, ou o shell morrerá antes de executar ocp
comando:Aqui você altera o número dos primeiros n-arquivos alterando o
$(($# - 5))
bit.Assim como na
bash
versão, esta também pode ser reduzida um pouco, desde que você esteja novamente positivo sobre as pré-condições necessárias. Este, além dabash
versão reduzida, também precisa de pelo menos n-arquivos para estar realmente presente no diretório de origem, ou então oshift
comando falhará fazendo o shell abortar prematuramente (por exemplo, se você tiver menos de 5 arquivos emspecific/directory
, este versão reduzida não irá copiá-los).1 NOTA: para simplificar e explicar, as soluções acima não verificam se os arquivos são apenas arquivos regulares (ou seja, não diretórios ou links simbólicos, soquetes, fifos nomeados, arquivos de dispositivo). Portanto, se o seu diretório de origem tiver "arquivos" desses tipos entre os primeiros n-arquivos maiores (mesmo que conte efetivamente 0 bytes), as soluções acima incluirão esses nomes no
cp
comando final. Isso pode ser particularmente relevante para links simbólicos e diretórios que sempre contam mais que 0, dependendo de seu conteúdo e, portanto, podem ter uma classificação mais alta do que arquivos regulares em umls -S
. Naturalmente, poderíamos repetir os nomes dos arquivos para testar os tipos de arquivo e descartar os não regulares, mas ficaria cada vez mais complexo, especialmente para substituir os descartados pelo próximo na classificação. Por favor, veja as outras respostas para lidar com esses casos de forma sã, pois minhas soluções aqui já estendem um pouco o que osbash
shells POSIX são capazes.