AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / unix / Perguntas / 744648
Accepted
Sudoh
Sudoh
Asked: 2023-05-02 01:31:59 +0800 CST2023-05-02 01:31:59 +0800 CST 2023-05-02 01:31:59 +0800 CST

Copie os n maiores arquivos de um determinado diretório para o atual

  • 772

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 staterros para o restante dos arquivos na lista.

Por que o tubo está funcionando para o primeiro item e falhando para o resto?

pipe
  • 4 4 respostas
  • 610 Views

4 respostas

  • Voted
  1. Gilles Quénot
    2023-05-02T02:01:46+08:002023-05-02T02:01:46+08:00

    observaçãoTodas as minhas soluções tratam apenas arquivos , conforme solicitado, e podem tratar todos os tipos de arquivos
    (mesmo com caracteres especiais).

    Se você quiser usarls -S

    Faça isso da forma correta:

    ls --zero -S | head -z -n5 | xargs -r0 cp -t ./other/dir --
    

    Exigir recente GNU coreutils.

    coreutils 9.1-1aqui.

    Outra maneira, usando bashe recente GNU find:

    findutils 4.9.0-4aqui.

    Com base nisso :

    shopt -s nullglob
    cd specific/directory/ || exit
    print0 () { 
        [ "$#" -eq 0 ] || printf '%s\0' "$@"
    }
    readarray -td '' files < <(
        print0 * |
        find -files0-from - -maxdepth 0 -type f -printf '%b\t%p\0' |
        sort -rzn |
        cut -zf2 -
    ) 
    cp -av -- "${files[@]:0:5}" "$OLDPWD"/
    
    • ${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 Perlem qualquer shell

    perl -e 'rename($_, "./other/dir/$_") for ((sort { -s $b <=> -s $a } <*>))[0..4]'
    
    • 6
  2. steeldriver
    2023-05-02T02:29:42+08:002023-05-02T02:29:42+08:00

    Usando zshvocê pode evitar todas as armadilhas associadas à análise e classificação da saída de ls:

    cp -n -- specific/directory/*(.DOL[1,5]) ./
    

    ou com GNU cp(para a -topção):

    cp -n -t ./ -- specific/directory/*(.DOL[1,5])
    

    onde estão os qualificadores glob

    • .corresponde apenas a arquivos simples (não diretórios, links simbólicos, fifos, soquetes).
    • Dalterne a opção dotglob - omita isso se quiser excluir arquivos ocultos
    • OL[1,5]ordena os resultados por tamanho de arquivo (tamanho) e seleciona os 5 primeiros

    e a -nopção impede cpa destruição de arquivos existentes no caso de uma colisão de nomes.

    • 6
  3. Peter Whittaker
    2023-05-02T01:54:38+08:002023-05-02T01:54:38+08:00

    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 -Fopção to ls, que inclui caracteres à direita indicando os tipos de inodes. A seguir está uma resposta incompleta que remove os diretórios da listagem via grep; uma resposta mais completa removeria outros tipos de inode que deveriam ser excluídos. Os sedcomandos removem os *executáveis ​​adicionados por -F.

    source="<some directory name>"
    destination='.'
    someCount=5 # e.g.
    while IFS=\  read -r; do
        cp "${source}/${REPLY}" "${destination}"
    done <<<"$(ls "${source}" -Ft | grep -v '/$' | head -5 | sed 's/\*$//')"
    

    RESPOSTA ORIGINAL:

    Suponha que os maiores arquivos sejam um, dois, três e quatro. O comando na pergunta acaba sendo

    cp specific/directory/one two three four .
    

    Como dois, três e quatro não saem em ., o comando falha. Algo semelhante a

    source=specific/directory
    set -f # disable globbing
    IFS='
    '      # split on newlines only
    for file in $(ls -S $source); do
       cp "${source}/${file}" .
    done
    

    faria isso.

    AVISO: Isso será interrompido se houver novas linhas em qualquer um dos nomes de arquivo (ou se seus lsnomes de arquivo forem alterados, mesmo quando não estiverem sendo impressos no terminal).

    • 2
  4. Best Answer
    LL3
    2023-05-03T02:29:33+08:002023-05-03T02:29:33+08:00

    Para integrar as outras respostas:


    TL; DR: Veja abaixo soluções viáveis ​​para bashe shells POSIX.


    Por que o tubo está funcionando para o primeiro item e falhando para o resto?

    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 do cp specific/directorytrecho, mas:

    1. porque você não colocou aspas duplas (o que é errado na maioria das vezes), a saída da Substituição de Comando passa por Divisão de Palavra de acordo com a IFSvariável; este último é definido por padrão como (um único espaço) mais caracteres <tab> mais <nova linha> e um <nova linha> é o que os ls -S | headcomandos usam para separar cada nome de arquivo; portanto, cada nome acaba sendo um caminho independente separado ao seu cpcomando; observe que, neste caso, colocar aspas duplas na substituição de comando não ajudaria, como você provavelmente já descobriu
    2. o shell também não duplica o specific/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 por cp, enquanto os outros 4 nomes devem estar presentes no diretório atual, mas obviamente eles não estão (e mesmo se estivessem, cpteriam 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 IFSvariável; pior ainda, se combinado com um descontrolado, evalpode ser usado para a mais clássica das injeções de comando se você não tiver controle total dos nomes de arquivo em specific/directory. (Além disso, veja a nota 1 abaixo).


    Possíveis soluções para bashe shells POSIX

    Além da ls --zerosoluçã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 GNU lsde coreutils v8.25 (por volta de 2016) em diante, que fornece as --quoting-stylevariantes para shells. Para isso precisamos usar eval, pois esta é de fato a única forma de se beneficiar dessa lsopção que de fato foi projetada para funcionar eval .

    Como de costume, evalprecisa ser manuseado com cuidado extra, se for o caso. Aqui, estamos usando-o exclusivamente lsapenas para o comando e confiando em lscitar nomes de arquivos corretamente para o shell conforme o comportamento documentado. Para cuidados adicionais, pode-se invocar, por exemplo, /bin/lso caminho completo explícito para um lsexecutável fornecendo a --quoting-styleopção desejada, em vez de arriscar usar quem sabe qual lsestá $PATHou talvez quem sabe qual função não autorizada exportada (ou mesmo alias) nomeada propositalmente ls.

    Assim, com bash:

    (
      set -o pipefail \
        && o="$(/bin/ls -S --quoting-style=shell-escape-always | head -n 5)" \
        && eval "set -- $o" \
        && (("$#")) && cp -n -- "${@/#/specific/directory/}" .
    )
    

    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 lsversão e não tiver nenhum motivo real para falhar ou exibir caracteres perdidos .

    (cd specific/directory && \
     eval "cp -n -- $(ls -S --quoting-style=shell-escape-always | head -n 5)"' "$OLDPWD"')
    

    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 lscomando. 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 o cpcomando:

    (
      set -- && cd specific/directory \
        && o="$(/bin/ls -rSxw 0 --quoting-style=shell-always)" && eval "set -- $o" \
        && [ "$#" -gt 0 ] && n="$(($# - 5))" && shift "$(($n > 0 ? $n : 0))" \
        && cp -n -- "$@" "$OLDPWD"
    )
    

    Aqui você altera o número dos primeiros n-arquivos alterando o $(($# - 5))bit.

    Assim como na bashversã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 da bashversão reduzida, também precisa de pelo menos n-arquivos para estar realmente presente no diretório de origem, ou então o shiftcomando falhará fazendo o shell abortar prematuramente (por exemplo, se você tiver menos de 5 arquivos em specific/directory, este versão reduzida não irá copiá-los).

    (
      set -- && cd specific/directory \
        && eval "set -- $(ls -rSxw 0 --quoting-style=shell-always)" \
        && shift "$(($# - 5))" && cp -n -- "$@" "$OLDPWD"
    )
    

    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 cpcomando 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 os bash shells POSIX são capazes.

    • 2

relate perguntas

  • Como canalizar um 'sim' ou 'y' em um programa enquanto é invocado com 'sudo' no bash?

  • Passe o identificador para o pipeline stdin down

  • Como canalizar um comando bash e manter Ctrl + C funcionando?

  • Por que canalizar `mysql` para 'tail' altera o formato de saída?

  • ordem de substituição de processos `te` e `bash`

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Possível firmware ausente /lib/firmware/i915/* para o módulo i915

    • 3 respostas
  • Marko Smith

    Falha ao buscar o repositório de backports jessie

    • 4 respostas
  • Marko Smith

    Como exportar uma chave privada GPG e uma chave pública para um arquivo

    • 4 respostas
  • Marko Smith

    Como podemos executar um comando armazenado em uma variável?

    • 5 respostas
  • Marko Smith

    Como configurar o systemd-resolved e o systemd-networkd para usar o servidor DNS local para resolver domínios locais e o servidor DNS remoto para domínios remotos?

    • 3 respostas
  • Marko Smith

    apt-get update error no Kali Linux após a atualização do dist [duplicado]

    • 2 respostas
  • Marko Smith

    Como ver as últimas linhas x do log de serviço systemctl

    • 5 respostas
  • Marko Smith

    Nano - pule para o final do arquivo

    • 8 respostas
  • Marko Smith

    erro grub: você precisa carregar o kernel primeiro

    • 4 respostas
  • Marko Smith

    Como baixar o pacote não instalá-lo com o comando apt-get?

    • 7 respostas
  • Martin Hope
    user12345 Falha ao buscar o repositório de backports jessie 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl Por que a maioria dos exemplos do systemd contém WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky Como exportar uma chave privada GPG e uma chave pública para um arquivo 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll status systemctl mostra: "Estado: degradado" 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim Como podemos executar um comando armazenado em uma variável? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S Por que /dev/null é um arquivo? Por que sua função não é implementada como um programa simples? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 Como ver as últimas linhas x do log de serviço systemctl 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - pule para o final do arquivo 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla Por que verdadeiro e falso são tão grandes? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis Substitua a string em um arquivo de texto enorme (70 GB), uma linha 2017-12-30 06:58:33 +0800 CST

Hot tag

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve