프로그램 출력에서 .csv 파일을 만들고 싶습니다. 원본 출력 중 일부를 이 수준으로 사용자 정의했습니다.
36.343074719185125 -1.488697037254009 0.517768286726280 -1.488697037254009 48.906350248447872 -0.255764113311881 0.51776828
6726280 -0.255764113311881 31.687963239227631
지금까지 나는 이러한 명령을 사용해 왔습니다.
tail -12 Q.out | head -3 | sed 's/^........//' | tr -d '\n' > q.txt
문제는 그 사이에 특정한 수의 공백이 없다는 것입니다. 4개일 때도 있고 3개일 때도 있습니다. 그것은 모두 프로그램의 출력에 달려 있습니다. 예를 들어 첫 번째 값이 36.343이면 공백 2개, 3.6이면 공백 3개, 360.34이면 공백 1개 앞에 붙습니다. 좀 더 깔끔하게 만드는 방법은 없을까요?
원시 데이터
0 1 2
0 36.343074719185125 -1.488697037254009 0.517768286726280
1 -1.488697037254009 48.906350248447872 -0.255764113311881
2 0.517768286726280 -0.255764113311881 31.687963239227631
alpha_(0.000) = 38.979129402287 a.u.
FCHKWriter: !WARNING! method 'CCSD'' renamed to label 'CC'.
FCHKWriter: Writing 0100-A_ccsd.fchk with label ' CC Density'.
Psi4 stopped on: Tuesday, 12 October 2021 04:09PM
Psi4 wall time for execution: 0:17:43.19
*** Psi4 exiting successfully. Buy a developer a beer!
예상되는 결과
36.343074719185125,-1.488697037254009,0.517768286726280,-1.488697037254009,48.906350248447872,-0.255764113311881,0.51776828
6726280,-0.255764113311881,31.687963239227631
우분투 20.04를 사용하고 있습니다
답변1
우리가 관심 있는 데이터의 세 행이 원본 데이터의 행 4, 5, 6이고 이 모든 숫자를 쉼표로 구분된 목록으로 한 줄에 입력한다고 가정해 보겠습니다.
다음 표현식은 sed
원하는 행 범위 내에 있지 않은 모든 행을 삭제한 다음 숫자만 포함하는 첫 번째 열을 삭제합니다. 이 tr
명령은 이 데이터를 읽고 데이터를 한 줄에 하나씩 숫자 목록으로 변환합니다. 줄 바꿈으로 구분된 숫자는 쉼표로 구분된 목록으로 변환됩니다 paste
.
sed -e '4,6 !d' \
-e 's/[[:blank:]]*[[:digit:]]*[[:blank:]]*//' file |
tr -s ' ' '\n' |
paste -s -d, -
tr
sed
반복되는 공백을 단일 쉼표로 바꾸면 이를 제거 할 수 있습니다 .
sed -e '4,6 !d' \
-e 's/[[:blank:]]*[[:digit:]]*[[:blank:]]*//' \
-e 's/[[:blank:]]\{1,\}/,/g' file |
paste -s -d, -
답변2
앗공백으로 구분된 데이터를 처리하는 데 매우 능숙합니다. @they처럼 관심 있는 3개 행이 4,5,6행이라고 가정해 보겠습니다.
awk -v OFS=, '4 <= NR && NR <= 6 {print $2,$3,$4}' Q.out
산출
36.343074719185125,-1.488697037254009,0.517768286726280
-1.488697037254009,48.906350248447872,-0.255764113311881
0.517768286726280,-0.255764113311881,31.687963239227631
그런 다음 이를 행으로 연결하려면 다음을 사용하십시오.paste
awk -v OFS=, '4 <= NR && NR <= 6 {print $2,$3,$4}' Q.out | paste -sd,
데이터는 다음 위치에 있습니다.마지막파일의 10~12번째 줄. 우리를 보자취소파일을 만들고, 알려진 행 범위에서 데이터를 추출하고, 데이터를 다시 반전하고, 조인합니다.
tac Q.out \
| awk -v OFS=, '10 <= NR && NR <= 12 {print $2,$3,$4}' \
| tac \
| paste -sd,
36.343074719185125,-1.488697037254009,0.517768286726280,-1.488697037254009,48.906350248447872,-0.255764113311881,0.517768286726280,-0.255764113311881,31.687963239227631
답변3
이를 사용하여 awk
데이터 파일을 처리할 수 있습니다. 귀하의 예를 보면 각 행에 4개의 필드가 있고 첫 번째 필드는 음수가 아닌 정수라고 가정할 수 있는 것 같습니다.
awk 'NF == 4 && $1 ~ /^[0-9]+$/ {printf "%s,%s,%s\n", $2, $3, $4}' Q.out
결과
36.343074719185125,-1.488697037254009,0.517768286726280
-1.488697037254009,48.906350248447872,-0.255764113311881
0.517768286726280,-0.255764113311881,31.687963239227631
모든 데이터를 한 줄에 표시하고 싶다는 다양한 의견을 보았습니다. 이것은귀하의 질문에 설명이 없습니다, 혼란스러운 두 줄의 불규칙한 출력이 있지만 어쨌든 수정하겠습니다.
여기에서 원래 awk
문을 수정하여 후속 데이터 행을 초기 행에 추가한 다음 후행 개행 문자를 추가할 수 있습니다.
awk '
NF == 4 && $1 ~ /^[0-9]+$/ {printf "%s%s,%s,%s", s, $2, $3, $4; s=","}
END {print ""}
' Q.out
답변4
모든 Unix 시스템의 모든 쉘에서 awk를 사용하고 CSV로 변환하려는 입력에 다음과 같이 길이가 불확실한 블록이 여러 개 있다고 가정합니다.
$ cat tst.awk
BEGIN { OFS="," }
(NF==4) && sub(/^ +[0-9]+ +/,"") {
$1 = $1
rec = (rec == "" ? "" : rec OFS) $0
}
!NF && (rec != "") {
print rec
rec = ""
}
$ awk -f tst.awk file
36.343074719185125,-1.488697037254009,0.517768286726280,-1.488697037254009,48.906350248447872,-0.255764113311881,0.517768286726280,-0.255764113311881,31.687963239227631