AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 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

从 grep 的输出中删除匹配模式?

  • 772

使用 bash,我正在运行:declare -p | grep 'declare -- '打印整行。我想打印那些相同的行,但我想排除匹配本身。即,我可以... | grep pattern | sed 's/pattern//'作为单个命令执行吗?这将与-o选项相反。

我的命令输出这个:

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

但我想输出这个:

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

通常情况下,我只是将它传送到sed,但巧合的是,我今天想这样做两次。我查看了手册页,但没有看到任何可以执行此操作的选项。

在匹配模式之后只返回一行的一部分是一个非常相似的问题。也许它甚至是重复的。有人可能会争辩说我的范围更窄一些:可以保证我正在抓取的模式和我正在删除的模式是相同的。我想删除x. 问题要删除.*x。

grep sed
  • 5 5 个回答
  • 1176 Views

5 个回答

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

    那应该是:

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

    但是这种解析输出的方法declare -p是有缺陷的。

    例如,如果环境中有 a VAR=$'\ndeclare -- reboot #',并且您将该输出提供给 shell 进行解释,该怎么办?

    另请注意,对于已声明但未分配任何值的变量,将bash打印:declare -- VARNAME,因此在 之后declare reboot,上述代码也会输出reboot。

    您可以将其更改为:

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

    限制为分配的变量,但这仍然不能解决包含换行符的变量的问题(即使仅针对$TERMCAP变量也是常见的)。

    相反,您可以使用这种 hack:

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

    我们在哪里评估declare -p我们转义的地方的输出,(并在数组和关联数组表示中使用,在重新定义为一个函数之后,如果第一个参数是 ,则打印它的第二个参数。使用风险自负,过去已知 bash 的输出对于评估是不安全的,添加转义也可能会增加复杂性,特别是对于它们接受的行长度有限制的实现。)[]declare--declare -psedsed


    在 中zsh,你可以这样做:

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

    打印标量变量的定义并且没有任何属性(甚至不是special也不是linked),这似乎是您的意图。

    typesetsilent但是请注意,如果在函数中调用(在这种情况下,它将所有这些变量声明为本地变量)或启用该选项,则它不起作用。

    另一种可以解决这个问题并让您更好地控制如何引用值的方法是:

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

    q+给出与 . 使用的引用风格相似的引用风格typeset。如果您打算在 shell 中使用该输出(可能不是 zsh,或者不是来自同一版本,或者不在相同的语言环境或 OS/libc 中),请使用qq更安全的引用样式。

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

    如果可用,请使用grep -P:

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

    请注意,您的方法通常不会很好地工作,因为变量可能包含您将切断的换行符grep并获得语法错误。

    参见例如:

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

    A.sed和grep

    shell 将某些变量的值显示在多行上,因为它们包含嵌入的换行符。

    grep并被sed设计成在同一行逐行搜索模式(换行符用作硬编码分隔符)。


    B. 使用awk

    awk 可以选择一行上的模式,但也可以有条件地应用规则。

    1.选择匹配的行

    • 显示以 shell 单词开头的行declare --
    /^declare --/
    
    • 显示不以单词开头的行declare
    !/^declare/
    

    前面两条规则允许 1. 显示没有属性的变量和 2. 多行值在后续行中显示的值。

    我们可以使用示例输入来显示模式匹配的概述。

    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/\\~}\""
    

    变量的值HOSTNAME显示成功是因为两条规则连续匹配:一条规则匹配一行,另一条匹配下一行。但是,我们也看到GREETINGS变量值被部分显示。实际上,虽然该行不以 pattern 开头,但由于该行(或“记录”)与第二个 rule 匹配,因此会显示declare --后续行(变量值 cf 的子字符串) 。World!"!/^declare/

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

    由于多行值是后续的,所以只需要显示一些连续的行。

    2.检查shell变量的值

    现在介绍用于更好地理解代码复杂性的算法是合适的。

    首先,你要知道 Awk 每次读取记录时都会检查所有规则(默认为当前行)。但是,您必须考虑之前完成的处理(在之前的记录中)。换句话说,程序在处理下一行时需要知道它的状态:解析的 shell 变量是什么?为此,我们定义了一个名为 的“布尔”变量scan。

    代码无疑是复杂的。它是相互排斥的条件指令的有序序列(如逻辑 XOR)。有关更多信息,请阅读“为什么代码如此复杂?”部分。

    A. 变量没有值

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

    第三个字段如果不包含等号则不是shell变量赋值,它只是一个变量名。

    B. 变量有值

    变量的值可以包含使用双引号转义的特殊字符。

    变量赋值有两种类型:标量变量的声明和数组变量的声明。

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

    无论如何,该值由一对特定字符分隔,要么"和"要么(和)。如果相应的分隔符在同一行,则变量的值不包含换行符。否则下一行必须显示到相应的分隔符。

    开始分隔符位于存储在beg变量中的特定位置,而关闭分隔符必须被搜索(使用end变量)。

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

    注意:match()是一个内置的 Awk 函数,它返回与模式匹配的子字符串的开始和结束位置(使用预定义的变量RSTART和RLENGTH)。这里我们将等号后面beg的字符和记录的最后一个字符保存在end.

    一个。该值不包含嵌入的换行符

    字符beg和end是成对的分隔符。

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

    如果第二次和第三次测试为假,则表示不扫描下一行。确实,Awk 变量赋值scan = 0等价于真值“false”。

    具体而言,这意味着不显示包含与所选模式(想要的变量)无关的子字符串的行。如果我们以“1.选择匹配的行”部分为例,这允许不显示作为( )World!"值的子字符串。GREETINGSdeclare -x

    湾。该值包含嵌入的换行符

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

    此条件指令指示该值尚未正确定界。因此,定界符必然位于另一行(与当前行连续的后续行)。在这种情况下,scan = 1指示扫描下一行,scan在逻辑测试中变为“真”。

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

    严格来说,变量的赋值在scan功能0上没有用,但它提醒读者,默认情况下这个变量设置为0。事实上,我们在对应if部分有相同的“序列”,但在这部分变量赋值scan = 0非常有用(参见“值不包含嵌入的换行符”一节)。

    C. 显示下一个(下...)行

    ;; 待定

    为什么这段代码如此复杂?

    这段代码很复杂,因为它检查了分隔符的各种组合。这并不复杂,但测试是特定的。

    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

    您可以使用 Perl 轻松完成:

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

    如果你使用ack,你可以这样做:

    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}'
    

    输出

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

相关问题

  • 命令 ls | grep 只显示目录(当它也应该显示文件时)

  • grep 什么时候计数,什么时候不计数

  • 如何改进这个字符转换脚本?

  • grep --line-buffered 直到 X 行?

  • 如何删除两行之间的单行

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    模块 i915 可能缺少固件 /lib/firmware/i915/*

    • 3 个回答
  • Marko Smith

    无法获取 jessie backports 存储库

    • 4 个回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    user12345 无法获取 jessie backports 存储库 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl 为什么大多数 systemd 示例都包含 WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST

热门标签

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

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve