2014-09-04 108 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

相關問題