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 / 696761
Accepted
Daniel Kaplan
Daniel Kaplan
Asked: 2022-03-25 22:20:44 +0800 CST2022-03-25 22:20:44 +0800 CST 2022-03-25 22:20:44 +0800 CST

Remover o padrão correspondente da saída do grep?

  • 772

Com o bash, estou executando isto: declare -p | grep 'declare -- 'Isso imprime linhas inteiras. Quero imprimir essas mesmas linhas, mas queria excluir a correspondência em si. ou seja, posso fazer ... | grep pattern | sed 's/pattern//'como um único comando? Seria como o oposto da -oopção.

Meu comando gera isso:

...
declare -- MAX_CONNECTIONS_PER_SERVER="6"
declare -- OPTERR="1"
declare -- OSTYPE="linux-gnu"
...

Mas eu quero produzir isso:

...
MAX_CONNECTIONS_PER_SERVER="6"
OPTERR="1"
OSTYPE="linux-gnu"
...

Normalmente, eu apenas canalizaria isso para sed, mas coincidentemente, eu queria fazer isso duas vezes hoje. Eu olhei para a página de manual, mas não vi nenhuma opção que pudesse fazer isso.

Retornar apenas a parte de uma linha após um padrão de correspondência é uma pergunta muito semelhante. Talvez seja até uma duplicata. Pode-se argumentar que o meu é um pouco mais estreito: é garantido que o padrão que estou grepping e o padrão que estou removendo serão os mesmos. Eu quero remover x. A questão quer remover .*x.

grep sed
  • 5 5 respostas
  • 1176 Views

5 respostas

  • Voted
  1. Stéphane Chazelas
    2022-03-26T00:17:00+08:002022-03-26T00:17:00+08:00

    Isso deveria ser apenas:

    declare -p | sed -n 's/^declare -- //p'
    

    Mas esse tipo de abordagem ao analisar a saída de declare -pé falho.

    E se houver um VAR=$'\ndeclare -- reboot #'no ambiente, por exemplo, e você alimentar essa saída em um shell para interpretação?

    Observe também que, para variáveis ​​declaradas, mas sem valor atribuído, bashimprime: declare -- VARNAME, portanto, depois declare rebootde , o código acima também será gerado reboot.

    Você poderia alterá-lo para:

    declare -p | LC_ALL=C sed -n 's/^declare -- \(.*=\)/\1/p'
    

    Para restringir a variáveis ​​atribuídas, mas isso ainda não resolveria problemas com variáveis ​​que contêm novas linhas (o que é comum mesmo que apenas para a $TERMCAPvariável).

    Em vez disso, você pode usar esse tipo de hack:

    (
      export LC_ALL=C
      eval '
        declare()
          if [[ $1 = -- ]]; then
            printf "%s=%q\n" "${2%%=*}" "${2#*=}"
          fi'"
        $(declare -p | sed 's/[][()]/\\&/g')"
    )
    

    Onde avaliamos a saída de declare -pwhere escapamos (, ), [e ]usamos em representações de array e array associativo, depois de ter redefinido declarecomo uma função que imprime seu segundo argumento se o primeiro for --. Use por sua conta e risco, a saída do bash é declare -pconhecida por ser insegura para avaliação no passado, também é possível que adicionar esse escape com sedadiciona complicações, especialmente com sedimplementações que têm um limite no comprimento das linhas que aceitam.


    em zsh, você poderia fazer:

    typeset ${(k)parameters[(R)scalar]}
    

    Imprimir as definições de variáveis ​​que são escalares e não possuem nenhum atributo (nem mesmo especial nem vinculado ) como parece ser sua intenção.

    Observe, no entanto, que não funciona se for chamado dentro de uma função (nesse caso, ele declararia todas essas variáveis ​​como locais) ou se a typesetsilentopção estiver ativada.

    Outra abordagem que contornaria isso e lhe daria mais controle sobre como o valor é cotado seria:

    () for 1 do print -r -- $1=${(Pq+)1}; done ${(k)parameters[(R)scalar]}
    

    q+fornece um estilo de citação semelhante ao usado por typeset. Use qqpara um estilo de citação mais seguro se você pretende usar essa saída em um shell (que pode não ser zsh, ou não da mesma versão, ou não na mesma localidade ou OS/libc).

    • 3
  2. Best Answer
    pLumo
    2022-03-25T22:45:27+08:002022-03-25T22:45:27+08:00

    Se disponível, use grep -P:

    declare -p | grep -Po 'declare -- \K.*'
    

    Observe que sua abordagem geralmente não funcionará bem, porque as variáveis ​​podem conter novas linhas com as quais você cortará grepe obterá erros de sintaxe.

    Veja por exemplo:

    declare -- IFS="    
    "
    
    • 2
  3. gilaro
    2022-03-29T09:40:41+08:002022-03-29T09:40:41+08:00

    A. sedegrep

    O shell exibe os valores de algumas variáveis ​​em várias linhas porque elas contêm novas linhas incorporadas.

    grepe sedsão projetados para pesquisar padrões na mesma linha, linha por linha (o caractere de nova linha é usado como um delimitador embutido em código).


    Transporte em ônibusawk

    O awk pode selecionar um padrão em uma linha, mas também aplicar regras condicionalmente.

    1. Selecione as linhas correspondentes

    • Exibir linhas começando com as palavras do shelldeclare --
    /^declare --/
    
    • Exibir linhas que não começam com a palavradeclare
    !/^declare/
    

    As duas regras anteriores permitem 1. exibir as variáveis ​​que não possuem atributos e 2. os valores exibidos nas linhas subsequentes para valores de várias linhas.

    Podemos usar uma entrada de amostra para mostrar uma visão geral da correspondência de padrões.

    declare -- HOSTNAME="retro-
    host"
    declare -a GROUPS=()
    declare -x GREETINGS="Hello
    World!"
    declare -i HISTCMD
    declare -- PROMPT_COMMAND="printf \"\\033]0;%s@%s:%s\\007\" \"\${USER}\" \"\${HOSTNAME%%.*}\" \"\${PWD/#\$HOME/\\~}\""
    

    O valor da variável HOSTNAMEé exibido com sucesso porque as duas regras correspondem consecutivamente: uma regra corresponde em uma linha e a outra na próxima linha. No entanto, também vemos que o GREETINGSvalor da variável é exibido parcialmente. De fato, embora a linha não comece com o padrão declare --, a linha subsequente (uma substring do valor da variável, cf World!") é exibida porque a linha (ou "registro") corresponde à segunda regra !/^declare/.

    declare -- HOSTNAME="retro-
    host"
    World!"
    declare -- PROMPT_COMMAND="printf \"\\033]0;%s@%s:%s\\007\" \"\${USER}\" \"\${HOSTNAME%%.*}\" \"\${PWD/#\$HOME/\\~}\""
    

    Como os valores de várias linhas são subsequentes, é necessário exibir apenas algumas linhas consecutivas.

    2. Verifique o valor da variável shell

    É oportuno apresentar agora o algoritmo utilizado para entender melhor a complexidade do código.

    Antes de tudo, você deve saber que o Awk verifica todas as regras cada vez que lê um registro (por padrão, a linha atual). No entanto, você deve considerar o processamento feito anteriormente (no registro anterior). Em outras palavras, o programa precisará conhecer seu estado enquanto processa a próxima linha: qual é a variável do shell analisada? Para isso, definimos uma variável "booleana" chamada scan.

    O código é, sem dúvida, complexo. É uma sequência ordenada de instruções condicionais mutuamente exclusivas (como o XOR lógico). Para obter mais informações, leia a seção "Por que o código é tão complicado?".

    A. A variável não tem valor

    if ($3 !~ /=/) {
        scan = 0
        print $3
    }
    

    O terceiro campo não é uma atribuição de variável shell se não contiver um sinal de igual, é apenas um nome de variável.

    B. A variável tem um valor

    O valor de uma variável pode conter caracteres especiais que são escapados usando aspas duplas.

    Existem dois tipos de atribuição de variáveis: a declaração de uma variável escalar e a declaração de uma variável de matriz.

    # A common shell variable
    VARIABLE="VALUE"
    # A Bash array
    VARIABLE=(VALUE)
    

    De qualquer forma, o valor é delimitado por um par de caracteres específicos, ou "e "ou (e ). Se os delimitadores correspondentes estiverem na mesma linha, o valor da variável não conterá caracteres de nova linha. Caso contrário, a próxima linha deve ser exibida até o delimitador correspondente.

    O delimitador de abertura está em uma posição específica armazenada na begvariável enquanto o delimitador de fechamento deve ser pesquisado (usando a endvariável).

    match($3, /=./)
    beg = substr($3, RSTART + 1, 1)
    match($0, /.$/)
    end = substr($0, RSTART, 1)
    

    Nota: match()é uma função Awk interna que retorna as posições inicial e final da substring que correspondeu ao padrão (usando as variáveis ​​predefinidas RSTARTe RLENGTH). Aqui salvamos o caractere logo após o sinal de igual bege o último caractere do registro em end.

    uma. O valor não contém novas linhas incorporadas

    Os caracteres bege endsão delimitadores emparelhados.

    if (match($0, /[[:alpha:]_][[:alnum:]_]*=[("].*[^\\][")]$/)) {
        scan = 0
        if (beg == "(") {
    

    Se o segundo e o terceiro testes forem falsos, isso indica que não é necessário varrer a próxima linha. De fato, a atribuição da variável Awk scan = 0é equivalente ao valor de verdade "falso".

    Em termos concretos, isso significa que as linhas contendo substrings que não estão associadas aos padrões selecionados (variáveis ​​desejadas) não são exibidas. Se tomarmos o exemplo da seção "1. Selecione as linhas correspondentes", isso permite não exibir a substring World!"que é o valor de GREETINGS( declare -x).

    b. O valor contém novas linhas incorporadas

    if (beg == "(") {
        if (end != ")") {
            scan = 1
        }
    }
    

    Esta instrução condicional indica que o valor ainda não está delimitado corretamente. Portanto, o delimitador está necessariamente localizado em outra linha (uma linha subsequente que é consecutiva à linha atual). Neste caso, scan = 1indica para escanear a próxima linha, scantorna-se "true" em um teste lógico.

    else {
        scan = 0
        if (match($0, /[[:alpha:]_][[:alnum:]_]*=.*$/)) {
            scan = 1
        }
    }
    

    Estritamente falando, a atribuição da variável scana 0não é funcionalmente útil, mas é um lembrete para o leitor de que , por padrão, essa variável é definida como 0. Na verdade, temos a mesma "sequência" na ifparte correspondente, mas nesta parte a atribuição de variáveis scan = 0​​é realmente útil (consulte a seção "O valor não contém novas linhas incorporadas").

    C. Exibir a próxima (próxima...) linha

    ;; A definir

    Por que esse código é tão complicado?

    Este código é complicado porque verifica as várias combinações dos delimitadores. Isso não é complicado, mas os testes são específicos.

    O programa Awk

    /^declare --/ {
        # Display shell variables with no value
        if ($3 !~ /=/) {
            scan = 0
            print $3
        }
        else {
            # Check if the value spread on several lines
            match($3, /=./)
            beg = substr($3, RSTART + 1, 1)
            match($0, /.$/)
            end = substr($0, RSTART, 1)
    
            if (match($0, /[[:alpha:]_][[:alnum:]_]*=[("].*[^\\][")]$/)) {
                scan = 0
                if (beg == "(") {
                    if (end != ")") {
                        scan = 1
                    }
                }
                else if (beg == "\"") {
                    if (end != "\"") {
                        scan = 1
                    }
                }
            }
            else {
                scan = 0
                if (match($0, /[[:alpha:]_][[:alnum:]_]*=.*$/)) {
                    scan = 1
                }
            }
            print substr($0, RSTART, RLENGTH)
        }
    }
    
    # Display the multi-line value of a matching pattern
    !/^declare/ && scan {
        # Check if this is the last substring of the variable value
        if ($0 ~ /[^\\][")]$/ || $0 ~ /\\\\[")]$/ || $0 ~ /^[")]$/) {
            match($0, /.$/)
            end = substr($0, RSTART, 1)
            if (end == ")") {
                if (beg == "(") {
                    scan = 0
                }
            }
            else if (end == "\"") {
                if (beg == "\"") {
                    scan = 0
                }
            }
        }
        print $0
    }
    
    • 1
  4. Andy Lester
    2022-04-06T13:07:15+08:002022-04-06T13:07:15+08:00

    Você pode fazer isso facilmente com Perl:

    declare -p | perl -lne'/^declare -- (.*)/ && print $1'
    

    Se você usar ack , você pode fazer isso:

    declare -p | ack '^declare -- (.*)' --output='$1'
    
    • 1
  5. Praveen Kumar BS
    2022-03-31T20:40:54+08:002022-03-31T20:40:54+08:00
    declare -p |awk -F "--" '/declare --/{print $NF}'
    

    resultado

     MAX_CONNECTIONS_PER_SERVER="6"
     OPTERR="1"
     OSTYPE="linux-gnu"
    
    • -2

relate perguntas

  • Comando ls | grep mostrando apenas diretórios (quando também deveria mostrar arquivos)

  • quando o grep conta e quando não

  • Como posso melhorar este script de conversão de personagens?

  • grep --line-buffered até X linhas?

  • Como remover uma única linha entre duas linhas

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