bash字符串操作 - 正则表达式与分隔符匹配

时间:2018-03-13 10:51:41

标签: bash awk sed

我有一个这样的字符串:

data-target=".navbar0001, .collapse.show" 

分隔符zone=INTERNET|status=good|routed=special|location=001|resp=user|switch=not set|stack=no|dswres=no|CIDR=10.10.10.0/24|allowDuplicateHost=disable|inheritAllowDuplicateHost=true|pingBeforeAssign=enable|inheritPingBeforeAssign=true|locationInherited=true|gateway=10.10.10.100|inheritDefaultDomains=true|inheritDefaultView=true|inheritDNSRestrictions=true|name=SCB-INET-A 内的顺序可以是随机的 - 这意味着键值对可以在字符串中随机排序。

我想要一个如下输出字符串:

|

输出中的所有值都是来自

上面的键值字符串的值

有没有人知道如何用 awk sed 来解决这个问题?

6 个答案:

答案 0 :(得分:1)

鉴于您的输入是变量var:

var="zone=INTERNET|status=good|routed=special|location=001|resp=user|switch=not set|stack=no|dswres=no|CIDR=10.10.10.0/24|allowDuplicateHost=disable|inheritAllowDuplicateHost=true|pingBeforeAssign=enable|inheritPingBeforeAssign=true|locationInherited=true|gateway=10.10.10.100|inheritDefaultDomains=true|inheritDefaultView=true|inheritDNSRestrictions=true|name=SCB-INET-A"

echo "$var" | tr "|" "\n" | sed -n -r "s/(zone|name|gateway)=(.*)/\"\2\"/p" 
"INTERNET"
"10.10.10.100"
"SCB-INET-A"

使用另外两个管道插入逗号并删除换行符:

SOFAR | tr "\n" "," | sed 's/,$//'

"INTERNET","10.10.10.100","SCB-INET-A"

答案 1 :(得分:1)

每当你有名字时 - >输入中的值对最好的方法是创建这些映射的数组(下面是f[]),然后按名称访问这些值:

$ cat tst.awk
BEGIN { RS="|"; FS="[=\n]"; OFS="," }
{ f[$1] = "\"" $2 "\"" }
END { print f["zone"], f["CIDR"], f["name"] }

$ awk -f tst.awk file
"INTERNET","10.10.10.0/24","SCB-INET-A"

以上将有效地工作(即比shell循环快几个数量级)并且在任何UNIX机器上的任何shell中都可以使用任何awk,这与目前为止所有依赖于非POSIX功能的其他答案不同。它执行完整的字符串匹配而不是部分正则表达式匹配,就像其他一些答案一样,因此它非常强大,并且在给定部分匹配时不会导致输出错误。它也不会解释任何输入字符(例如转义序列和/或globbing字符),就像你的其他一些答案那样,而只是在输出中按原样强健地再现它们。

如果您需要增强它以打印任何额外的字段值,只需将它们作为, f["<field name>"]添加到print语句中,如果您需要更改输出格式或执行任何其他操作,那么它们也绝对是微不足道的

答案 2 :(得分:0)

使用awk

var="zone=INTERNET|status=good|routed=special|location=001|resp=user|switch=not set|stack=no|dswres=no|CIDR=10.10.10.0/24|allowDuplicateHost=disable|inheritAllowDuplicateHost=true|pingBeforeAssign=enable|inheritPingBeforeAssign=true|locationInherited=true|gateway=10.10.10.100|inheritDefaultDomains=true|inheritDefaultView=true|name=SCB-INET-A|inheritDNSRestrictions=true" 

awk -v RS='|' -v ORS=',' -F= '$1~/zone|gateway|name/{print "\"" $2 "\""}' <<<"$var" | sed 's/,$//'
"INTERNET","10.10.10.100","SCB-INET-A"

输入记录分隔符RS设置为|

输入字段分隔符FS设置为=

输出记录分隔符ORS设置为,

$1~/zone|gateway|name/正在过滤要提取的参数。 print语句被添加到参数值的双引号中。

sed语句是删除烦人的上一个,print语句正在添加)。

答案 3 :(得分:0)

不使用sedawk,而是使用Bash Arrays功能。

line="zone=INTERNET|sta=good|CIDR=10.10.10.0/24|a=1 1|...=...|name=SCB-INET-A"

echo "$line" | tr '|' '\n' | {
declare -A vars 
while read -r item ; do 
  if [ -n "$item" ] ; then 
     vars["${item%%=*}"]="${item##*=}"
  fi  
done
echo "\"${vars[zone]}\",\"${vars[CIDR]}\",\"${vars[name]}\"" ; }

此方法的一个优点是,您始终可以按顺序获取字段,而与输入行中的字段顺序无关。

答案 4 :(得分:0)

使用Bash的另一个解决方案。不是最短的,但我希望它是最好的可读性,所以最好的可维护性。

#!/bin/bash

# Function split_key_val()
#   selects values from a string with key-value pairs
# IN: string_with_key_value_pairs wanted_key_1 [wanted_key_2] ...
# OUT: result
function split_key_val {
  local KEY_VAL_STRING="$1"
  local RESULT
  # read the string with key-value pairs into array
  IFS=\| read -r -a ARRAY <<< "$KEY_VAL_STRING"
  #
  shift
  # while there are wanted-keys ...
  while [[ -n $1 ]]
  do
    WANTED_KEY="$1"
    # Search the array for the wanted-key
    for KEY_VALUE in "${ARRAY[@]}"
    do
      # the key is the part before "="
      KEY=$(echo "$KEY_VALUE" |cut --delimiter="=" --fields=1)
      # the value is the part after "="
      VALUE=$(echo "$KEY_VALUE" |cut --delimiter="=" --fields=2)
      if [[ $KEY == $WANTED_KEY ]]
      then
        # if result is empty; result= found value...
        if [[ -z $RESULT ]]
        then
          # (quote the damned quotes)
          RESULT="\"${VALUE}\""
        else
          # ... else add a comma as a separator
          RESULT="${RESULT},\"${VALUE}\""
        fi
      fi  # key == wanted-key
    done  # searched whole array
    shift # prepare for next wanted-key
  done
  echo "$RESULT"
  return 0
}

STRING="zone=INTERNET|status=good|routed=special|location=001|resp=user|switch=not set|stack=no|dswres=no|CIDR=10.10.10.0/24|allowDuplicateHost=disable|inheritAllowDuplicateHost=true|pingBeforeAssign=enable|inheritPingBeforeAssign=true|locationInherited=true|gateway=10.10.10.100|inheritDefaultDomains=true|inheritDefaultView=true|inheritDNSRestrictions=true|name=SCB-INET-A"

split_key_val "$STRING" zone CIDR name

结果是:     “互联网”, “10.10.10.0/24”, “SCB-INET-A”

答案 5 :(得分:0)

不使用更复杂的文本编辑工具(作为练习!)

$ tr '|' '\n' <file                          | # make it columnar
  egrep '^(zone|CIDR|name)='                 | # get exact key matches
  cut -d= -f2                                | # get values
  while read line; do echo '"'$line'"'; done | # quote values
  paste -sd,                                   # flatten with comma

将给出

"INTERNET","10.10.10.0/24","SCB-INET-A"

您也可以将while语句替换为xargs printf '"%s"\n'