2014-09-04 336 views
-1

在bash shell中,(solaris 5.8上的bash ver可能是旧的), 使用awk或sed我将如何将行合并为一个“重复”模式之间的行:在shell中如何将两个字符串之间的行整合成一行

[编辑:更好地解释自己)

我的文件中包含这样的一个条目很多:

my-group<--------------------------(main entry) 
<tab>group-code<spcaes>AXZ1<-------(sub-section under main entry) 
<tab>description      
<tab>state<spaces>CA 
<tab>items 
<tab><spaces>item_value_1 
<tab><spaces>item_value_2 
<tab><tab>header_3 <---------------(sub-section under sub-section) (can have upto 5th level) 
<tab><tab>header_3_item_1<spaces>vlaue 

我希望这可以变成: 新行头开始每次该行的列包含字母数字值。当它没有的时候,它应该被附加为: -
所有的TAB被一个“|”替代。以及由“”分隔的参数和值:

my-group|group-code:AXZ1|description:|state:CA|items:something:something2|last-member-name:XYZ 
my-group|group-code:PORTU1|description:|state:CT|items:something:something2|last-member-name:FQRTZ 

我该怎么做?我唯一能想到的方式就是在内存中打开文件并逐行读取并执行操作。是唯一的方法还是可以有一个sed/awk命令?

我在这里把我试图实现这个bash代码。 (还没有工作)

#!/bin/bash 
myFile=$1 

function trim() 
{ 
    local [email protected] 
    var=$(echo $var|sed -e "s/^\s*//" -e "s/\s*$//" -e "s/[ \t]/:/g") 
    echo -n "$var" 
} 

newLine='' 
i=0 
while read line 
do 
    i=$[i + 1] 
    [ -z "$line" ] && continue 
    if [[ $line =~ ^[[:alnum:]] ]] <-----this is not working....matching every line 
     then 
     newLine=$(trim "$line") 
     match="matched ^a-zA-Z0-9" 
    elif [[ $line =~ ^[[:space:]] ]] 
     then 
     line="$(trim "$line")" 
     newLine="${newLine}|${line}" 
     match="matched ^tab/space" 
    fi 
    echo -e "line number=$i match=$match line=$line new-ine value-->"$newLine"<--" 
    echo 
done < $myFile 

ty。

+0

我们应该如何知道“某物”是关键还是值? “描述”显然没有任何价值,但是“物品”的确如此,当两者都是唯一的词。空白有多重要? – 2014-09-05 01:09:30

回答

2

这可以用下面的sed脚本来实现:

:a 
N 
s/\(\n\) \([-a-z][-a-z]*\)/|\2\1/ 
s/\n */:/ 
$!ta 
s/:|/:/g 
P 
d 

在您输入它产生预期的输出:

% sed -f script.sed data 
my-group|group-code:AXZ1|description:|state:CA|items:something:something2|last-member-name:XYZ 
my-group|group-code:PORTU1|description:|state:CT|items:something:something2|last-member-name:FQRTZ 

其中script.sed包含前面的脚本。

演练

:a  Label marking the start of our loop 
N  Read next line of input 
s/…/…/ If the structure matches a key:value declaration, translate it 
$!ta  and return to a, to read the next key (unless we're at end of file) 
s/:|/:/g Otherwise, clean the fields, 
P   print text gathered so far, 
d   and start a new cycle 

注意,我的sed在s命令的替换文本不承认\n,这就是为什么我必须把它保存在一组。

+0

在输出中是否需要'something:2'? – n0741337 2014-09-05 00:39:50

+0

@ n0741337你发现了一个失败,新版本希望按预期工作。 – 2014-09-05 00:50:46

+0

对不起,没有答案的工作。 sunos 5.8 SUNWcsu版本(用于sed)11.8.0,REV = 2000.01.08.18.12或者 - 最新的linux gnu sed版本:$ sed --version GNU sed版本4.2.1 – rajeev 2014-09-05 21:59:45

1

我在GNU awk和-v RS=gensub()附近玩耍,但看起来太像其他答案。

这里是一个awk命令,通过使用-F"[[:space:]]*"使前导空格显著:

awk -F"[[:space:]]*" ' 
    NF==1 {if(b!="") print b; b=$1} 
    NF==2 {b=b (b~/:$/?"":":") $2} 
    NF==3 {b=b "|"$2":"$3} 
    END {print b}' data 

这里的演练:

  • NF==1打印最后b或启动输出线b
  • NF==2捕获无标签字段并将它们附加到b与以前的标签。使用三元运算符来决定何时在前面加上“:”
  • NF==3格式的键/值对,并把它们添加到b
  • END,打印存储在b

在其他线下决赛单词,逐行建立缓冲区,然后在遇到新记录时或在END处输出。


暂时,这里是原来的问题数据的副本:

my-group 
    group-code      AXZ1 
    description 
    state       CA 
    items 
            something 
            something2 
    last-member-name    XYZ 

my-group 
    group-code      PORTU1 
    description 
    state       CT 
    items 
            something 
            something2 
    last-member-name    FQRTZ 
+0

它的功能。我只是复制粘贴命令行,并运行我的数据文件替换数据。 – rajeev 2014-09-05 22:02:12

+0

我并不感到惊讶。您的数据似乎没有反映您最初发布的内容。您已添加更多级别的记录格式而不更新所需的输出。我在awk和linux中用两个不同的GNU旧版awk在mac上试了这个,当'data'是旧的问题数据的复制和粘贴时它就起作用了。请用更复杂的输入(包括一个评论)和输出来更新问题。 – n0741337 2014-09-05 23:24:49

0

我想感谢所有谁回答我最初的问题的人。我会接受你的答案之一。

但是,这是我的工作,它工作正常。

#!/bin/bash 
myfile=$1 

function trim() 
{ 
    local [email protected] 
    var=$(echo "$var"|sed -e "s/^\s*//" -e "s/\s*$//" -e "s/[ \t]\{1,\}/:/") 
    echo -n "$var" 
} 

newLine='' 
i=0 
linesInFile=$(wc -l $myfile|awk '{print $1}') 
while IFS= read line 
do 
    i=$[i + 1] 
    [[ ! $line =~ [[:alnum:]\*] ]] && continue 
    if [[ $line =~ ^[[:alnum:]] ]]; then 
     if [[ $newLine != '' ]]; then 
      echo $newLine 
     fi 
     newLine=$(trim "$line") 
    elif [[ $line =~ ^[[:space:]]{4,} ]]; then 
     newLine="${newLine}:$(trim "$line")" 
    elif [[ $line =~ ^[[:space:]] ]]; then 
     newLine="${newLine}|$(trim "$line")" 
    fi 
    if [[ $linesInFile -eq $i ]]; then 
     echo $newLine 
    fi 
done < $myfile 
IFS=$' \t\n' 
+0

很高兴这为你工作,虽然它给我输出不匹配使用复制/粘贴数据在Mac上使用GNU bash所需的输出。如果原始输入文件是17行的行,那么'$ cfgFile'应该设置为什么?将来,如果答案不适用于您,请详细说明它们如何失败。它会给你更好的机会获得适合你的答案。 – n0741337 2014-09-07 07:42:51

相关问题