各列はタブで区切られた次の形式のファイルがあります。
C1 C2 C3
a b,c d
e f,g,h i
j k l
...
次に、2番目の列のカンマ区切り値の数に基づいて行数を取得する必要があります(この場合)。行にはこれらの値のいずれかが必要であり、他の値はあってはなりません。結果は次のとおりです。
C1 C2 C3
a b d
a c d
e f i
e g i
e h i
j k l
...
...
急いで作業をしなければならないので、ただ作ってみました。家ではこれをしないでください。while
技術が不足したり、awk
他のツールを使用して他の可能なソリューションを探索したりしないため、スクリプトは1行ずつ読み取りを使用します。スクリプトは次のとおりです。
同時にスクリプトを修正しています。
# DON'T DO THIS AT HOME SCRIPT
> duplicados.txt
while IFS= read -r line; do
# get the value of the column of interest
cues="$(echo "$line" | awk -F'\t' '{ print $18 }')"
# if the column has commas then it has multiple values
if [[ "$cues" =~ , ]]; then
# count the commas
c=$(printf "%s" "$cues" | sed 's/[^,]*//g' | wc -c)
# loop according to the number of commas
for i in $(seq $(($c + 1))); do
# get each value of the column of interest according to the position
cue="$(echo "$cues" | awk -F',' -v c=$i '{ print $c; ++c }')"
# save the line to a file substituting the whole column for the value
echo "$line" | sed "s;$cues;$cue;" >> duplicados.txt
done
continue
fi
# save the single value lines
echo "$line" >> duplicados.txt
done < inmuebles.txt
これはあなたが望む結果を得ることができます(私が知っている限り)。想像できるように、このスクリプトは遅くて非効率的です。awk
他のツールを使用してこれをどのように実行できますか?
実際のデータサンプルを下に示し、関心のある列は数字18です。
1409233 UNION VIAMONTE Estatal Provincial DGEP 3321 VIAMONTE -33.7447365;-63.0997115 Rural Aglomerado 140273900 140273900-ESCUELA NICOLAS AVELLANEDA
1402961 UNION SAN MARCOS SUD Estatal Provincial DGEA, DGEI, DGEP 3029, 3311, Z11 SAN MARCOS SUD -32.629557;-62.483976 / -32.6302699949582;-62.4824499999125 / -32.632417;-62.484932 Urbano 140049404, 140164000, 140170100, 140173100 140049404-C.E.N.M.A. N° 201 ANEXO SEDE SAN MARCOS SUD, 140164000-C.E.N.P.A. N° 13 CASA DE LA CULTURA(DOC:BERSANO), 140170100-ESCUELA HIPOLITO BUCHARDO, 140173100-J.DE INF. HIPOLITO BUCHARDO
1402960 UNION SAN ANTONIO DE LITIN Estatal Provincial DGEA, DGEI, DGETyFP 3029, TZONAXI, Z11 SAN ANTONIO DE LITIN 3601300101020009 360102097366 0250347 SI / SI -32.212126;-62.635999 / -32.2122558;-62.6360432 / -32.2131931096409;-62.6291815804363 Rural Aglomerado 140049401, 140313000, 140313300, 140483400, 140499800 140049401-C.E.N.M.A. N° 201 ANEXO SAN ANTONIO DE LITIN, 140313000-I.P.E.A. Nº 214. MANUEL BELGRANO, 140313300-J.DE INF. PABLO A. PIZZURNO, 140483400-C.E.N.P.A. DE SAN ANTONIO DE LITIN, 140499800-C.E.N.P.A. B DE SAN ANTONIO DE LITIN
答え1
awk
,
複合列を分割して結果を繰り返すことでこれを行うことができます。
awk -F'\t' 'BEGIN{OFS=FS} {n=split($2,a,/,/); for(i=1;i<=n;i++){$2 = a[i]; print}}' file
たぶんもっときれいにすることができますミラー- 特に、使用ネストされた動詞:
$ cat file
C1 C2 C3
a b,c d
e f,g,h i
j k l
$ mlr --tsv nest --explode --values --across-records --nested-fs ',' -f C2 file
C1 C2 C3
a b d
a c d
e f i
e g i
e h i
j k l
よりコンパクトなものと--explode --values --across-records --nested-fs ','
交換できます。--evar ','
答え2
私は質問にタグを追加したので、ソリューションを追加する必要があるとsed
感じましたsed
。
sed -e '/,/{s//\n/;h;s/[^\t]*\n//;x;s/\n[^\t]*//p;G;D;}'
(注:GNUと同様に、読みやすくするために改行\n
文字とタブを使用しています。移植可能なソリューションの場合は、実際のタブの代わりに実際の改行文字でバックスラッシュを使用し、次のように入力します。)\t
sed
\n
\t
ctrlVtab
コンマ付きの行は予約済みスペースにコピーされ、一方のコピーはコンマの前の内容を印刷し、もう一方のコピーは次のループに入るコンマの後の部分を印刷します。詳細:
- 複数のカンマとの混同を避けるために、1 つのカンマを改行文字に置き換えます。
s//\n/
h
行を混乱させる前に、コピーを古いスペースに保存してください。s/[^\t]*\n//
最初のコンマの前の部分を削除してください。- その後、
x
バッファを変更します。 s/\n[^\t]*//p
カンマで始まる部分を削除して印刷します。G
予約済みスペースをパターンスペースに追加します。これには追加のカンマを含めることができます。D
最初の行(印刷)を削除し、残りの行から再開してください。
答え3
awk
(またはperl
inawk
モード)はおそらく最高の標準ソリューションです。できるksh
bash
ほとんどのシェル、特に配列(、、、)を持つシェルでは、zsh
これを大幅に効率的に実行できます。
set -f # split but don't glob unquoted substitutions
#bash
while IFS=$'\t' read -ra ary; do
#ksh
while read -r line; do IFS=$'\t'; ary=($line)
#zsh I haven't worked out
IFS=,; for v in ${ary[17]}; do
ary[17]=$v; IFS=$'\t'; printf '%s\n' "${ary[*]}"
done
# bash,ksh arrays are 0-origin versus 1-origin fields in awk
# we don't need to special-case no-comma, it splits to a single value
done <input >output
配列のない前/制限されたシェルの場合は、次の位置引数を使用します(さまざまな場合があります)。
set -f
while read -r line; do IFS=$'\t'; set -- $line
IFS=,; for v in ${18}; do
# can't alter $num so yucky
for i in $(seq $#); do
case $i in (1);; (*) printf '\t';; esac
case $i in (18) printf %s "$v";; (*) eval printf %s \"\${$i}\";; esac
done
# or maybe i=1; while [ $i -le $# ]; do ... i=$((i+1)); done
# where [/test is likely shell builtin and seq is unlikely
done
done <input >output
答え4
while read line
do
fic=$(echo $line | awk '{print $1}')
laco=$(echo $line | awk '{print $NF}')
secon_colu=$(echo $line| awk '$2 ~ /,/{print $2}')
if [[ "$secon_colu" =~ "," ]]
then
for ko in $(echo $line | awk '$2 ~ /,/{print $2}'| sed 's/,/ /g')
do
echo "$fic $ko $laco"
done
else
echo $line
fi
done<file.txt
出力
C1 C2 C3
a b d
a c d
e f i
e g i
e h i
j k l