簡體   English   中英

在 linux 中使用 awk 或 sed 解析簡單字符串

[英]Parsing simple string with awk or sed in linux

原始字符串:
A/trunk/apple/B/trunk/apple/Z/trunk/orange/citrus/Q/trunk/melon/juice/venti/straw/

目錄的深度會有所不同,但 /trunk 部分將始終保持不變。 /trunk 前面的單個字符是該行的指示符。

所需的 output:

A /trunk/apple
B /trunk/apple
Z /trunk/orange
Q /trunk/melon/juice/venti/straw

*** 編輯
對不起,我犯了一個錯誤,在原始字符串的每個路徑的末尾添加了一個斜杠,這使得 output 令人困惑。 原始字符串在大寫字母前沒有斜線,但我會保留它。

要處理復雜的樣本輸入,例如在單行中可能有 N 個/和值的位置,請嘗試以下操作。

awk '
{
  gsub(/[^/]*\/trunk/,OFS"&")
  sub(/^ /,"")
  sub(/\//,OFS"&")
  gsub(/ +[^/]*\/trunk\/[^[:space:]]+/,"\n&")
  sub(/\n/,OFS)
  gsub(/\n /,ORS)
  gsub(/\/trunk/,OFS"&")
  sub(/[[:space:]]+/,OFS)
}
1
'  Input_file


對於您顯示的示例,請嘗試遵循awk代碼。

awk '{gsub(/\/trunk/,OFS "&");gsub(/trunk\/[^/]*\//,"&\n")} 1' Input_file

對於多字符 RS 和 RT,使用 GNU awk:

$ awk -v RS='([^/]+/){2}[^/\n]+' 'RT{sub("/",OFS,RT); print RT}' file
A trunk/apple
B trunk/apple
Z trunk/orange

我將RS設置為描述您要匹配的每個字符串的正則表達式,即 2 次重復非/ s,然后是/ ,然后是非/ s 的最終字符串(以及輸入行上最后一個字符串的非換行符)。 RT自動設置為每個匹配的字符串,所以我只需將第一個/更改為空白並打印結果。

如果每條路徑並不總是 3 層深,但總是以something/trunk/開頭,例如:

$ cat file
A/trunk/apple/banana/B/trunk/apple/Z/trunk/orange

然后:

$ awk -v RS='[^/]+/trunk/' 'RT{if (NR>1) print pfx $0; pfx=gensub("/"," ",1,RT)} END{printf "%s%s", pfx, $0}' file
A trunk/apple/banana/
B trunk/apple/
Z trunk/orange

awk ,您可以嘗試此解決方案。 它處理下一個字符為大寫時刪除正斜杠的特殊要求。 不會贏得設計獎,但工作。

$ echo "A/trunk/apple/B/trunk/apple/Z/trunk/orange" | 
    awk -F '' '{ x=""; for(i=1;i<=NF;i++){ 
      if($(i+1)~/[A-Z]/&&$i=="/"){$i=""}; 
      if($i~/[A-Z]/){ printf x""$i" "}
      else{ x="\n"; printf $i } }; print "" }'
A /trunk/apple
B /trunk/apple
Z /trunk/orange

也適用於 n 個單詞。 實際上適用於任何遵循給定模式的東西。

$ echo "A/fruits/apple/mango/B/anything/apple/pear/banana/Z/ball/orange/anything" | 
    awk -F '' '{ x=""; for(i=1;i<=NF;i++){
      if($(i+1)~/[A-Z]/&&$i=="/"){$i=""};
      if($i~/[A-Z]/){ printf x""$i" "}
      else{ x="\n"; printf $i } }; print "" }'
A /fruits/apple/mango
B /anything/apple/pear/banana
Z /ball/orange/anything

這可能對您有用(GNU sed):

sed 's/[^/]*/& /;s/\//\n/3;P;D' file

用空格將第一個單詞與第一個/分開。

將第三個/替換為換行符。

打印/刪除第一行並重復。


如果第一個單詞的屬性只有一個字符:

sed 's/./& /;s#/\(./\)#\n\1#;P;D' file

或者,如果第一個單詞具有以大寫字符開頭的屬性:

sed 's/[[:upper:]][^/]*/& /;s#/\([[:upper:][^/]*/\)#\n\1#;P;D' file

或者,如果第一個單詞具有后跟/trunk/的屬性:

sed -E 's#([^/]*)(/trunk/)#\n\1 \2#g;s/.//' file

使用gnu awk您可以使用 FPAT 使用模式設置每個字段的內容。

循環字段時,將第一個/替換為/

str1="A/trunk/apple/B/trunk/apple/Z/trunk/orange"

echo $str1 | awk -v FPAT='[^/]+/trunk/[^/]+' '{    
for(i=1;i<=NF;i++) {
    sub("/", " /", $i)
    print $i
    }
}'

模式匹配

  • [^/]+匹配除/以外的任何字符
  • /trunk/[^/]+匹配/trunk/和除/之外的任何字符

Output

A  /trunk/apple
B  /trunk/apple
Z  /trunk/orange

使用 GNU sed:

$ str="A/trunk/apple/B/trunk/apple/Z/trunk/orange/citrus/Q/trunk/melon/juice/venti/straw/"
$ sed -E 's|/?(.)(/trunk/)|\n\1 \2|g;s|/$||' <<< "$str"

A /trunk/apple
B /trunk/apple
Z /trunk/orange/citrus
Q /trunk/melon/juice/venti/straw

注意第一個空的 output 行。 如果不希望我們可以分開處理第一行 output 行:

$ sed -E 's|(.)|\1 |;s|/(.)(/trunk/)|\n\1 \2|g;s|/$||' <<< "$str"
A /trunk/apple
B /trunk/apple
Z /trunk/orange/citrus
Q /trunk/melon/juice/venti/straw

awk使用gsub()sub()函數:

awk '
{
gsub(/[[:upper:]]{1}/,"& ")
sub(/[[:upper:]]{1}$/,"\n&",$2)
sub(/[[:upper:]]{1}$/,"\n&",$3)
$1=$1
gsub(/[/]\n/,"\n")
} 1' file
A /trunk/apple
B /trunk/apple
Z /trunk/orange
  • 第一個gsub()默認應用於$0
  • 然后我們在sub()中對$2$3字段使用相同的正則表達式。
  • 重建: $1=$1
  • 最后,我們刪除最后的/

假設您的數據將始終采用作為單個字符串提供的格式,您可以試試這個sed

$ sed 's/$/\//;s|\([A-Z]\)\([a-z/]*\)/\([a-z]*\?\)|\1 \2\3\n|g' input_file
$ echo "A/trunk/apple/pine/skunk/B/trunk/runk/bunk/apple/Z/trunk/orange/T/fruits/apple/mango/P/anything/apple/pear/banana/L/ball/orange/anything/S/fruits/apple/mango/B/rupert/cream/travel/scout/H/tall/mountains/pottery/barnes" | sed 's/$/\//;s|\([A-Z]\)\([a-z/]*\)/\([a-z]*\?\)|\1 \2\3\n|g'
A /trunk/apple/pine/skunk
B /trunk/runk/bunk/apple
Z /trunk/orange
T /fruits/apple/mango
P /anything/apple/pear/banana
L /ball/orange/anything
S /fruits/apple/mango
B /rupert/cream/travel/scout
H /tall/mountains/pottery/barnes

perl 帶來一些樂趣,您可以使用非消耗正則表達式自動拆分為@F數組,然后隨意打印。

perl -lanF'/(?=.{1,2}trunk)/' -e 'print "$F[2*$_] $F[2*$_+1]" for 0..$#F/2'

第 1 步:拆分

  • perl -lanF/(?=.{1,2}trunk)/'
  • 這將采用輸入 stream,並在遇到模式.{1,2}trunk時分割每一行
  • 因為我們想要保留trunk和前面的 1 或 2 個字符,所以我們將拆分模式包裝在(?=)中,以實現不消耗的前瞻
  • 這將事情分解成這樣:
$ echo A/trunk/apple/B/trunk/apple/Z/trunk/orange/citrus/Q/trunk/melon/juice/venti/straw/ | perl -lanF'/(?=.{1,2}trunk)/' -e 'print join " ", @F'
A /trunk/apple/ B /trunk/apple/ Z /trunk/orange/citrus/ Q /trunk/melon/juice/venti/straw/

第二步:格式化output:

  • @F數組包含我們要按順序打印的對,因此我們將迭代一半的數組索引,並一次打印 2 個:
  • print "$F[2*$_] $F[2*$_+1]" for 0..$#F/2 --> 將迭代器加倍,並打印對
  • 使用perl -l意味着每個print在末尾都有一個隱含的\n
  • 結果:
$ echo A/trunk/apple/B/trunk/apple/Z/trunk/orange/citrus/Q/trunk/melon/juice/venti/straw/ | perl -lanF'/(?=.{1,2}trunk)/' -e 'print "$F[2*$_] $F[2*$_+1]" for 0..$#F/2'
A /trunk/apple/
B /trunk/apple/
Z /trunk/orange/citrus/
Q /trunk/melon/juice/venti/straw/

尾注:Perl 混淆無效。

  • perl 中的任何數組都可以轉換為格式為 (key,val,key,val....) 的 hash
  • 所以%F=@F; print "$_ $F{$_}" for keys %F %F=@F; print "$_ $F{$_}" for keys %F看起來真的很漂亮
  • 但是你失去了秩序:
$ echo A/trunk/apple/B/trunk/apple/Z/trunk/orange/citrus/Q/trunk/melon/juice/venti/straw/ | perl -lanF'/(?=.{1,2}trunk)/' -e '%F=@F; print "$_ $F{$_}" for keys %F'
Z /trunk/orange/citrus/
A /trunk/apple/
Q /trunk/melon/juice/venti/straw/
B /trunk/apple/

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM