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 / 409462
Accepted
anton_rh
anton_rh
Asked: 2017-12-08 04:28:08 +0800 CST2017-12-08 04:28:08 +0800 CST 2017-12-08 04:28:08 +0800 CST

cabeça come personagens extras

  • 772

Esperava-se que o seguinte comando shell imprimisse apenas linhas ímpares do fluxo de entrada:

echo -e "aaa\nbbb\nccc\nddd\n" | (while true; do head -n 1; head -n 1 >/dev/null; done)

Mas, em vez disso, apenas imprime a primeira linha: aaa.

O mesmo não acontece quando é usado com a opção -c( --bytes):

echo 12345678901234567890 | (while true; do head -c 5; head -c 5 >/dev/null; done)

Esse comando é gerado 1234512345conforme o esperado. Mas isso funciona apenas na implementação coreutilshead do utilitário. A implementação do busybox ainda consome caracteres extras, então a saída é apenas 12345.

Acho que essa forma específica de implementação é feita para fins de otimização. Você não pode saber onde a linha termina, então não sabe quantos caracteres precisa ler. A única maneira de não consumir caracteres extras do fluxo de entrada é ler o fluxo byte por byte. Mas a leitura do fluxo um byte por vez pode ser lenta. Então, acho que headlê o fluxo de entrada em um buffer grande o suficiente e, em seguida, conta as linhas nesse buffer.

O mesmo não pode ser dito para o caso em que a --bytesopção é usada. Neste caso, você sabe quantos bytes precisa ler. Portanto, você pode ler exatamente esse número de bytes e não mais do que isso. A implementação corelibs usa essa oportunidade, mas a busybox não, ela ainda lê mais bytes do que o necessário em um buffer. Provavelmente é feito para simplificar a implementação.

Então a pergunta. É correto que o headutilitário consuma mais caracteres do fluxo de entrada do que o solicitado? Existe algum tipo de padrão para utilitários Unix? E se houver, ele especifica esse comportamento?

PS

Você tem que pressionar Ctrl+Cpara parar os comandos acima. Os utilitários Unix não falham na leitura além de EOF. Se você não quiser pressionar, pode usar um comando mais complexo:

echo 12345678901234567890 | (while true; do head -c 5; head -c 5 | [ `wc -c` -eq 0 ] && break >/dev/null; done)

que eu não usei para simplificar.

shell-script text-processing
  • 3 3 respostas
  • 1555 Views

3 respostas

  • Voted
  1. Best Answer
    Stephen Kitt
    2017-12-08T04:53:15+08:002017-12-08T04:53:15+08:00

    É correto que o utilitário principal consuma mais caracteres do fluxo de entrada do que o solicitado?

    Sim, é permitido (veja abaixo).

    Existe algum tipo de padrão para utilitários Unix?

    Sim, POSIX volume 3, Shell & Utilities .

    E se houver, ele especifica esse comportamento?

    Ele faz, em sua introdução:

    Quando um utilitário padrão lê um arquivo de entrada pesquisável e termina sem erro antes de atingir o fim do arquivo, o utilitário deve garantir que o deslocamento do arquivo na descrição do arquivo aberto seja posicionado corretamente logo após o último byte processado pelo utilitário. Para arquivos que não são pesquisáveis, o estado do deslocamento do arquivo na descrição do arquivo aberto para esse arquivo não é especificado.

    headé um dos utilitários padrão , portanto, uma implementação em conformidade com POSIX deve implementar o comportamento descrito acima.

    O GNU tenta deixar head o descritor de arquivo na posição correta, mas é impossível buscar em pipes, portanto, em seu teste, ele falha em restaurar a posição. Você pode ver isso usando strace:

    $ echo -e "aaa\nbbb\nccc\nddd\n" | strace head -n 1
    ...
    read(0, "aaa\nbbb\nccc\nddd\n\n", 8192) = 17
    lseek(0, -13, SEEK_CUR)                 = -1 ESPIPE (Illegal seek)
    ...
    

    O readretorna 17 bytes (todas as entradas disponíveis), headprocessa quatro deles e tenta retroceder 13 bytes, mas não consegue. (Você também pode ver aqui que o GNU headusa um buffer de 8 KiB.)

    Quando você diz headpara contar bytes (o que não é padrão), ele sabe quantos bytes ler, então pode (se implementado dessa forma) limitar sua leitura de acordo. É por isso que seu head -c 5teste funciona: o GNU headlê apenas cinco bytes e, portanto, não precisa procurar restaurar a posição do descritor de arquivo.

    Se você gravar o documento em um arquivo e usá-lo, obterá o comportamento desejado:

    $ echo -e "aaa\nbbb\nccc\nddd\n" > file
    $ < file (while true; do head -n 1; head -n 1 >/dev/null; done)
    aaa
    ccc
    
    • 30
  2. ilkkachu
    2017-12-08T04:49:23+08:002017-12-08T04:49:23+08:00

    de POSIX

    O utilitário principal deve copiar seus arquivos de entrada para a saída padrão, finalizando a saída de cada arquivo em um ponto designado.

    Não diz nada sobre quanto head deve ler da entrada. Exigir que ele leia byte por byte seria bobagem, pois seria extremamente lento na maioria dos casos.

    Isso é, no entanto, abordado no readbuiltin/utility: todos os shells que posso encontrar readde pipes um byte por vez e o texto padrão pode ser interpretado como significando que isso deve ser feito, para poder ler apenas uma única linha:

    O utilitário de leitura deve ler uma única linha lógica da entrada padrão em uma ou mais variáveis ​​de shell.

    No caso de read, que é usado em scripts de shell, um caso de uso comum seria algo assim:

    read someline
    if something ; then 
        someprogram ...
    fi
    

    Aqui, a entrada padrão de someprogramé a mesma do shell, mas pode-se esperar que someprogramleia tudo o que vem após a primeira linha de entrada consumida pelo reade não o que sobrou após uma leitura em buffer de read. Por outro lado, usar headcomo no seu exemplo é muito mais incomum.


    Se você realmente deseja excluir todas as outras linhas, seria melhor (e mais rápido) usar alguma ferramenta que possa lidar com toda a entrada de uma só vez, por exemplo

    $ seq 1 10 | sed -ne '1~2p'   # GNU sed
    $ seq 1 10 | sed -e 'n;d'     # works in GNU sed and the BSD sed on macOS
    
    $ seq 1 10 | awk 'NR % 2' 
    $ seq 1 10 | perl -ne 'print if $. % 2'
    
    • 6
  3. ijbalazs
    2017-12-09T08:14:18+08:002017-12-09T08:14:18+08:00
    awk '{if (NR%2) == 1) print;}'
    
    • 1

relate perguntas

  • exportar variáveis ​​​​env programaticamente, via stdout do comando [duplicado]

  • Dividir por delimitador e concatenar problema de string

  • Embaralhamento de arquivo de várias linhas

  • MySQL Select com função IN () com array bash

  • como posso alterar o caso do caractere (de baixo para cima e vice-versa)? ao mesmo tempo [duplicado]

Sidebar

Stats

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

    Matriz JSON para bash variáveis ​​usando jq

    • 4 respostas
  • Marko Smith

    A data pode formatar a hora atual para o fuso horário GMT? [duplicado]

    • 2 respostas
  • Marko Smith

    bash + lê variáveis ​​e valores do arquivo pelo script bash

    • 4 respostas
  • Marko Smith

    Como posso copiar um diretório e renomeá-lo no mesmo comando?

    • 4 respostas
  • Marko Smith

    conexão ssh. Conexão X11 rejeitada devido a autenticação incorreta

    • 3 respostas
  • Marko Smith

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

    • 7 respostas
  • Marko Smith

    comando systemctl não funciona no RHEL 6

    • 3 respostas
  • Marko Smith

    rsync porta 22 e 873 uso

    • 2 respostas
  • Marko Smith

    snap /dev/loop em 100% de utilização -- sem espaço livre

    • 1 respostas
  • Marko Smith

    chave de impressão jq e valor para todos no subobjeto

    • 2 respostas
  • Martin Hope
    EHerman Matriz JSON para bash variáveis ​​usando jq 2017-12-31 14:50:58 +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
  • Martin Hope
    Drux A data pode formatar a hora atual para o fuso horário GMT? [duplicado] 2017-12-26 11:35:07 +0800 CST
  • Martin Hope
    AllisonC Como posso copiar um diretório e renomeá-lo no mesmo comando? 2017-12-22 05:28:06 +0800 CST
  • Martin Hope
    Steve Como as permissões de arquivo funcionam para o usuário "root"? 2017-12-22 02:46:01 +0800 CST
  • Martin Hope
    Bagas Sanjaya Por que o Linux usa LF como caractere de nova linha? 2017-12-20 05:48:21 +0800 CST
  • Martin Hope
    Cbhihe Altere o editor padrão para vim para _ sudo systemctl edit [unit-file] _ 2017-12-03 10:11:38 +0800 CST
  • Martin Hope
    showkey Como baixar o pacote não instalá-lo com o comando apt-get? 2017-12-03 02:15:02 +0800 CST
  • Martin Hope
    youxiao Por que os diretórios /home, /usr, /var, etc. têm o mesmo número de inode (2)? 2017-12-02 05:33:41 +0800 CST
  • Martin Hope
    user223600 gpg — o comando list-keys gera uid [ desconhecido ] depois de importar a chave privada para uma instalação limpa 2017-11-26 18:26:02 +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