![열에 여러 값이 있는 경우 각 값이 포함된 행을 별도로 복사하세요.](https://linux55.com/image/205262/%EC%97%B4%EC%97%90%20%EC%97%AC%EB%9F%AC%20%EA%B0%92%EC%9D%B4%20%EC%9E%88%EB%8A%94%20%EA%B2%BD%EC%9A%B0%20%EA%B0%81%20%EA%B0%92%EC%9D%B4%20%ED%8F%AC%ED%95%A8%EB%90%9C%20%ED%96%89%EC%9D%84%20%EB%B3%84%EB%8F%84%EB%A1%9C%20%EB%B3%B5%EC%82%AC%ED%95%98%EC%84%B8%EC%9A%94..png)
각 열이 탭으로 구분된 다음 형식의 파일이 있습니다.
C1 C2 C3
a b,c d
e f,g,h i
j k l
...
이제 두 번째 열의 쉼표로 구분된 값 수를 기준으로 행 수를 가져와야 합니다(이 경우). 행에는 이러한 값 중 하나가 있어야 하며 다른 값은 없어야 합니다. 결과는 다음과 같습니다.
C1 C2 C3
a b d
a c d
e f i
e g i
e h i
j k l
...
...
급하게 작업을 해야 하기 때문에 그냥 만들어 봤습니다.집에서는 이러지 마세요while
기술이 부족하거나 awk
다른 도구를 사용하여 다른 가능한 솔루션을 탐색하지 않기 때문에 스크립트는 한 줄씩 읽기를 사용합니다. 스크립트는 다음과 같습니다.
동시에 대본을 수정하고 있어요
# 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
쉼표가 있는 줄은 예약된 공간에 복사되고, 한 복사본은 쉼표 앞의 내용을 인쇄하고, 다른 복사본은 다음 루프에 들어가는 쉼표 뒤의 부분을 인쇄합니다. 상세히:
- 여러 쉼표와의 혼동을 피하기 위해 하나의 쉼표를 개행 문자로 바꿉니다.
s//\n/
h
줄을 엉망으로 만들기 전에 복사본을 이전 공간에 저장하세요.s/[^\t]*\n//
첫 번째 쉼표 앞 부분을 삭제하세요.- 그런 다음
x
버퍼를 변경합니다. s/\n[^\t]*//p
쉼표로 시작하는 부분을 제거하고 인쇄하세요.G
예약된 공간을 패턴 공간에 추가합니다. 여기에는 추가 쉼표가 포함될 수 있으므로D
첫 번째 줄(인쇄된)을 삭제하고 나머지 줄부터 다시 시작하세요.
답변3
awk
(또는 perl
in awk
모드)가 아마도 가장 좋은 표준 솔루션일 것입니다.할 수 있는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