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 / 482893
Accepted
Vlastimil Burián
Vlastimil Burián
Asked: 2018-11-20 22:40:20 +0800 CST2018-11-20 22:40:20 +0800 CST 2018-11-20 22:40:20 +0800 CST

Como contar POSIX-ly o número de linhas em uma variável de string?

  • 772

Eu sei que posso fazer isso no Bash:

wc -l <<< "${string_variable}"

Basicamente, tudo que encontrei envolvia o <<<operador Bash.

Mas no shell POSIX, <<<é indefinido e não consegui encontrar uma abordagem alternativa por horas. Tenho certeza de que existe uma solução simples para isso, mas infelizmente não a encontrei até agora.

shell-script variable
  • 4 4 respostas
  • 2518 Views

4 respostas

  • Voted
  1. Best Answer
    Gilles 'SO- stop being evil'
    2018-11-20T23:14:30+08:002018-11-20T23:14:30+08:00

    A resposta simples é que wc -l <<< "${string_variable}"é um atalho ksh/bash/zsh para arquivos printf "%s\n" "${string_variable}" | wc -l.

    Na verdade existem diferenças na forma <<<como um pipe funciona: <<<cria um arquivo temporário que é passado como entrada para o comando, enquanto |cria um pipe. Em bash e pdksh/mksh (mas não em ksh93 ou zsh), o comando no lado direito do pipe é executado em um subshell. Mas essas diferenças não importam neste caso em particular.

    Observe que em termos de contagem de linhas, isso pressupõe que a variável não esteja vazia e não termine com uma nova linha. Não terminar com uma nova linha é o caso quando a variável é o resultado de uma substituição de comando, então você obterá o resultado correto na maioria dos casos, mas obterá 1 para a string vazia.

    Existem duas diferenças entre var=$(somecommand); wc -l <<<"$var"e somecommand | wc -l: usar uma substituição de comando e uma variável temporária remove as linhas em branco no final, esquece se a última linha de saída terminou em uma nova linha ou não (sempre faz se o comando gerar um arquivo de texto não vazio válido) , e superconta em um se a saída estiver vazia. Se você deseja preservar o resultado e as linhas de contagem, pode fazê-lo anexando algum texto conhecido e removendo-o no final:

    output=$(somecommand; echo .)
    line_count=$(($(printf "%s\n" "$output" | wc -l) - 1))
    printf "The exact output is:\n%s" "${output%.}"
    
    • 11
  2. Inian
    2018-11-20T22:56:33+08:002018-11-20T22:56:33+08:00

    Não está em conformidade com os embutidos do shell, usando utilitários externos como grepe awkcom opções compatíveis com POSIX,

    string_variable="one
    two
    three
    four"
    

    Fazendo com greppara corresponder ao início das linhas

    printf '%s' "${string_variable}" | grep -c '^'
    4
    

    E comawk

    printf '%s' "${string_variable}" | awk 'BEGIN { count=0 } NF { count++ } END { print count }'
    

    Note que algumas das ferramentas GNU, especialmente, GNU grepnão respeitam POSIXLY_CORRECT=1a opção de rodar a versão POSIX da ferramenta. No grepúnico comportamento afetado pela configuração da variável será a diferença no processamento da ordem dos sinalizadores de linha de comando. A partir da documentação ( grepmanual GNU), parece que

    POSIXLY_CORRECT

    Se definido, o grep se comporta como o POSIX requer; caso contrário, grepse comporta mais como outros programas GNU. O POSIX requer que as opções que seguem os nomes dos arquivos sejam tratadas como nomes de arquivos; por padrão, tais opções são permutadas na frente da lista de operandos e são tratadas como opções.

    Veja Como usar POSIXLY_CORRECT no grep?

    • 3
  3. ilkkachu
    2018-11-21T02:44:00+08:002018-11-21T02:44:00+08:00

    A string here <<<é praticamente uma versão de uma linha do here-document <<. O primeiro não é um recurso padrão, mas o último é. Você pode usar <<também neste caso. Estes devem ser equivalentes:

    wc -l <<< "$somevar"
    
    wc -l << EOF
    $somevar
    EOF
    

    No entanto, observe que ambos adicionam uma nova linha extra no final de $somevar, por exemplo, isso é impresso 6, mesmo que a variável tenha apenas cinco linhas:

    s=$'foo\n\n\nbar\n\n'
    wc -l <<< "$s"
    

    Com printf, você pode decidir se deseja a nova linha adicional ou não:

    printf "%s\n" "$s" | wc -l         # 6
    printf "%s"   "$s" | wc -l         # 5
    

    Mas então, observe que wcapenas conta linhas completas (ou o número de caracteres de nova linha na string). grep -c ^também deve contar o fragmento de linha final.

    s='foo'
    printf "%s" "$s" | wc -l           # 0 !
    
    printf "%s" "$s" | grep -c ^       # 1
    

    (É claro que você também pode contar as linhas inteiramente no shell usando a ${var%...}expansão para removê-las uma de cada vez em um loop ...)

    • 1
  4. flabdablet
    2019-11-22T03:48:20+08:002019-11-22T03:48:20+08:00

    Nesses casos surpreendentemente frequentes em que o que você realmente precisa fazer é processar todas as linhas não vazias dentro de uma variável de alguma forma (incluindo contá-las), você pode definir IFS para apenas uma nova linha e usar o mecanismo de divisão de palavras do shell para quebrar as linhas não vazias separadas.

    Por exemplo, aqui está uma pequena função shell que totaliza as linhas não vazias dentro de todos os argumentos fornecidos:

    lines() (
    IFS='
    '
    set -f #disable pathname expansion
    set -- $*
    echo $#
    )
    

    Parênteses, em vez de chaves, são usados ​​aqui para formar o comando composto para o corpo da função. Isso faz com que a função seja executada em um subshell para que não polua a variável IFS do mundo externo e a configuração de expansão do nome do caminho em cada chamada.

    Se você deseja iterar em linhas não vazias, pode fazê-lo da mesma forma:

    IFS='
    '
    set -f
    for line in $lines
    do
        printf '[%s]\n' $line
    done
    

    Manipular o IFS dessa maneira é uma técnica frequentemente negligenciada, também útil para fazer coisas como analisar nomes de caminho que podem conter espaços de entrada colunar delimitada por tabulação. No entanto, você precisa estar ciente de que remover deliberadamente o caractere de espaço geralmente incluído na configuração padrão do IFS de space-tab-newline pode acabar desabilitando a divisão de palavras em locais onde você normalmente esperaria vê-lo.

    Por exemplo, se você estiver usando variáveis ​​para criar uma linha de comando complicada para algo como ffmpeg, talvez queira incluir -vf scale=$scaleapenas quando a variável scaleestiver definida como algo não vazio. Normalmente você pode conseguir isso com ${scale:+-vf scale=$scale}, mas se o IFS não incluir seu caractere de espaço usual no momento em que essa expansão de parâmetro for feita, o espaço entre -vfe scale=não será usado como um separador de palavras e ffmpegserá passado -vf scale=$scalecomo um único argumento, que não vai entender.

    Para corrigir isso, você precisa ter certeza de que o IFS foi definido mais normalmente antes de fazer a ${scale}expansão ou fazer duas expansões: ${scale:+-vf} ${scale:+scale=$scale}. A divisão de palavras que o shell faz no processo de análise inicial das linhas de comando, em oposição à divisão que faz durante a fase de expansão do processamento dessas linhas de comando, não depende do IFS.

    Outra coisa que pode valer a pena se você for fazer esse tipo de coisa seria criar duas variáveis ​​globais do shell para manter apenas uma guia e apenas uma nova linha:

    t=' '
    n='
    '
    

    Dessa forma, você pode apenas incluir $te $nem expansões onde precisar de guias e novas linhas, em vez de encher todo o seu código com espaços em branco citados. Se você preferir evitar completamente o espaço em branco citado em um shell POSIX que não possui outro mecanismo para fazer isso, printfpode ajudar, embora você precise de um pouco de manipulação para contornar a remoção de novas linhas à direita em expansões de comando:

    nt=$(printf '\n\t')
    n=${nt%?}
    t=${nt#?}
    

    Às vezes, definir IFS como se fosse uma variável de ambiente por comando funciona bem. Por exemplo, aqui está um loop que lê um nome de caminho que pode conter espaços e um fator de escala de cada linha de um arquivo de entrada delimitado por tabulação:

    while IFS=$t read -r path scale
    do
        ffmpeg -i "$path" ${scale:+-vf scale=$scale} "${path%.*}.out.mkv"
    done <recode-queue.txt
    

    Nesse caso, o readbuiltin vê o IFS definido como apenas uma guia, portanto, também não dividirá a linha de entrada que lê em espaços. Mas IFS=$t set -- $lines não funciona: o shell se expande à $linesmedida que constrói os setargumentos do builtin antes de executar o comando, então a configuração temporária do IFS de uma maneira que se aplica apenas durante a execução do próprio builtin vem tarde demais. É por isso que os trechos de código que dei acima definem o IFS em uma etapa separada e por que eles precisam lidar com a questão de preservá-lo.

    • 0

relate perguntas

  • Um script que imprime as linhas de um arquivo com seu comprimento [fechado]

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

  • Dividir por delimitador e concatenar problema de string

  • Como salvar um caminho com ~ em uma variável?

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

Sidebar

Stats

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

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

    • 4 respostas
  • Marko Smith

    ssh Não é possível negociar: "nenhuma cifra correspondente encontrada", está rejeitando o cbc

    • 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

    Como descarregar o módulo do kernel 'nvidia-drm'?

    • 13 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
    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
    Wong Jia Hau ssh-add retorna com: "Erro ao conectar ao agente: nenhum arquivo ou diretório" 2018-08-24 23:28:13 +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
  • Martin Hope
    Bagas Sanjaya Por que o Linux usa LF como caractere de nova linha? 2017-12-20 05:48:21 +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