web-dev-qa-db-ja.com

区切り文字を使用して行を抽出し、ファイルの列として追加します

次のようなデータを含むファイルがあります。

a 1
b 2
c,d,e 3,4,5
f 6
g,h 7,8 

...そして私は次のような出力が必要です:

a 1
b 2
c 3
d 4
e 5
f 6
g 7
h 8

Pythonを使用してこれを行うことはできますが、シェルスクリプトを使用してこれを試したいと思います。最初に区切り文字「」を含む行を分離してから、先に進むことを考えていました。これまで、これを使用して行を分離しました。

Perl -F, -ane 'print if $#F >=1' filename

...しかし、私は次のステップで立ち往生しています。

2
Anurag

Perlを使用

$ Perl -lane '@v=split/,/,$F[1]; $i=0;
              print "$_ $v[$i++]" for split/,/,$F[0]' ip.txt
a 1
b 2
c 3
d 4
e 5
f 6
g 7
h 8

1番目と2番目の列のいずれかを分割し、インデックスカウンターを初期化してから、他の列の分割を繰り返してペアを出力します。

-aオプションは、入力行を空白で自動分割し、結果を@F配列

3
Sundeep

Sedエディターを使用してそれを行う1つの方法は次のとおりです。

sed -e '
   s/,/\n/
   s/\(\n.*[[:blank:]]\)\([^,]*\),/ \2\1/
   P;D
' input.file

作業中:

  • 2番目のフィールドから先頭のコンマ区切り要素をクリップします。
  • 次に、この要素を最初のフィールドの先頭のコンマ区切り要素に追加します。
  • 1番目のフィールドの先頭要素を印刷し、その後削除します。
  • パターンスペースが空になるまで、パターンスペースに残っているものでこの手順を繰り返します。

Perlを使用する別の方法は次のとおりです。

Perl -lane '
   my($kref, $vref, %h) = map { [split /,/] } @F[0,1];
   @h{@$kref} = @$vref;
   print "$_  $h{$_}" for @$kref;
' input.file

別の方法をここに示します。

Perl -lpe 'print "$1 $3" while s/^([^,]*),(.*\h)([^,]*),/$2/' input.file

作業中:

  • このように正規表現を見てください:(Perlはファイルから一度に1行ずつ読み取ります)次に:
    • ^([^、] *)は、現在の行の最初のフィールドの先頭のコンマ区切り要素を選択します。これは$ 1変数に格納されます。
    • (。*\h) whileループの次の反復のために、最初のフィールドの2番目のコンマ区切り要素から2番目のコンマ区切り要素の先頭までの中間コンテンツを保持します。 2番目のフィールドの。これは$ 2変数に格納されます。
    • ([^、] *)は、現在の行の2番目のフィールドから先頭のコンマ区切り要素を選択します。これは$ 3変数に格納されます。
    • これで、「$ 1 $ 3」がSTDOUTに出力され、行が$ 2に縮小されます。 whileループは、この編集された行(前の行の$ 2)で操作を繰り返し実行します。これは、s ///が成功するまで繰り返されます。カンマがなくなると失敗します。その時点で、行に残っているもの「c 5」は、-pモードでのPerlのデフォルトの動作によってSTDOUTに出力されます。
  • 最初のフィールドと2番目のフィールドから先頭のコンマ区切り要素を取り出します。
  • それらの要素を印刷し、削除して現在のレコードを縮小します。
  • 2つのコンマがある間に、現在のレコードをループします。
  • 最後のペアは、Perlの-pオプションにより自動印刷されます。

Perl -lane '
   my($kref, $vref) = map { [split /,/] } @F;
   print shift @$kref, " ", shift @$vref while @$kref && @$vref;
' input.file

作業中:

  • キーは配列@ $ krefに格納され、対応する値は@ $ vrefに格納されます。ここにはハッシュが含まれていないことに注意してください。
  • 配列の上部を同時に印刷してから、上部を削除します...すすぎ、両方の配列が空でない間に繰り返します。

出力:

a 1
b 2
c 3
d 4
e 5
f 6
g 7
h 8
2
Rakesh Sharma

Awkソリューション(その数の「キー」(最初のフィールドに含まれていると仮定)$1)は常に「値」の数に対応します(2番目のフィールドに含まれます$2)):

awk '$1 ~ /,/{
         len = split($1, keys, ",");
         split($2, vals, ",");
         for (i = 1; i <= len; i++) print keys[i], vals[i];
         next
    }1' file

出力:

a 1
b 2
c 3
d 4
e 5
f 6
g 7
h 8
2
RomanPerekhrest

「シェルスクリプトを使用する」-これはbashです:

while read -r key value; do
    IFS=, read -ra keys <<<"$key"
    IFS=, read -ra vals <<<"$value"
    for ((i=0; i < ${#keys[@]}; i++)); do
        echo "${keys[i]} ${vals[i]}"
    done
done <<END
a 1
b 2
c,d,e 3,4,5
f 6
g,h 7,8 
END
1
glenn jackman

Awkの使用:

awk '{gsub(","," "); for(i=0;i<NF/2;i++) print $(i+1),$(i+1+NF/2)}' file     

各行からコンマを削除した後、スクリプトは各行のパラメーターの半分をループして、最初のフィールドを行の後半のフィールドと一緒に出力します。

1
oliv

難読化してすみません...

Perl -pe '1 while s/(.*),(.*\h)(.*),/$1 $3\n$2/' infile

各置換は最後のペアを抽出します:

a,b,c 1,2,3  →  a,b 1,2  →  a 1
                c 3         b 2
                            c 3
0
JJoao

ここにはたくさんの解決策がありますprogrammed Perl、awk、sed、bashなどですが、script-solutionはありません。

# /bin/bash
f="yourFile"
paste -d' ' <(cut -d' ' -f1 "$f" | tr , '\n') <(cut -d' ' -f2 "$f" | tr , '\n')

または別の書き方

# /bin/bash
f() { cut -d' ' -f"$1" yourFile | tr , '\n'; }
paste -d' ' <(f 1) <(f 2)
0
Socowi

Gnu sed

sed -E ':A;s/([^,]*),([^ ]*) ([^,]*),(.*)/\1 \3\n\2 \4/;tA' infile
0
ctac_