일부 기후 데이터가 포함된 7개의 CSV 파일이 있습니다. 파일 이름은 다음 SMVV50065-2015-01.csv
과 같습니다 *2015-02.csv
. 2015-03.csv
csv 파일을 열면 다음 구문이 표시됩니다.
" SMVV, 2015-01-01 00:00,50065,780,7,1000,-2,18, , ,1000"
온도, 압력, 습도 등의 측정값을 나타냅니다. ","는 누락된 데이터를 나타냅니다. sed 명령을 사용하여 누락된 값을 gaps에서 NA로 변경했습니다. 좀 더 구체적으로 적어봤습니다
sed 's/ ,/NA/g' SMVV50065-2015-01.csv > newfile01.csv
모든 공백을 NA로 변경했습니다. 문제는 foreach 명령을 사용하여 나머지 파일에 대해 동일한 작업을 수행하고 변경 후 이름 등을 사용하여 새 파일에 저장하고 싶다는 것입니다. 이 명령의 정확한 구문이 무엇인지 아십니까 newfile01.csv
?newfile02.csv
답변1
귀하의 CSV 파일에는 쉼표가 있는 따옴표가 없고 개행이 있는 필드가 엄격하게 포함되어 있지 않다고 가정합니다.
그러면 빈 필드나 공백만 포함된 필드가 다음으로 변경됩니다 NA
.
awk 'BEGIN { FS=OFS="," } { for (i=1;i<=NF;++i) if ($i ~ /^ *$/) $i = "NA"; print }'
각 입력 줄의 쉼표로 구분된 각 필드에 대해 정규 표현식과 일치하는지 테스트합니다 ^ *$
. 그렇다면 필드는 string 으로 대체됩니다 NA
. 블록의 및 변수는 FS
각각 입력 및 출력 필드 구분 기호입니다. 는 현재 입력 라인에서 감지된 필드 수입니다. 정수인 경우 1부터 계산하여 해당 정수에 해당하는 필드가 됩니다.OFS
BEGIN
NF
awk
i
$i
귀하의 예제 라인,
SMVV, 2015-01-01 00:00,50065,780,7,1000,-2,18, , ,1000
될 것입니다
SMVV, 2015-01-01 00:00,50065,780,7,1000,-2,18,NA,NA,1000
이제 모든 파일에 대해 이 명령을 실행하려면 해당 파일이 모두 라는 디렉터리에 있고 dir
파일 이름이 패턴과 일치한다고 가정합니다 SMVV50065*.csv
.
이 파일을 루핑할 때의 문제점은 다음과 같습니다.
for name in dir/SMVV50065*.csv; do
test -f "$name" || continue
# construct new name and call awk here
done
test -f
실제로 일반 파일 인지 테스트하고 $name
, 그렇지 않은 경우 나머지 반복을 건너뜁니다. 그럴 것이다아니요패턴이 디렉터리 이름과 일치하거나 패턴이 일치하지 않는 경우아무것(이 경우 확장되지 않은 상태로 유지됩니다).
제안된 패턴에 따라 새 파일 이름을 구성하려면 한 번부터 시작하여 각 반복마다 증가하는 카운터 변수를 유지하고 printf
이 변수 파일 이름을 사용하여 출력을 제공하는 형식 문자열로 호출할 수 있습니다.
i=1
for name in dir/SMVV50065*.csv; do
test -f "$name" || continue
newname=$( printf 'newfile%02d.csv' "$i" )
i=$(( i + 1 ))
# call awk here
done
%02d
형식은 printf
에서 0으로 채워진 2자리 정수를 제공합니다 $i
.
이제 awk
이전 파일 이름을 호출하고 결과를 새 파일에 쓰십시오. 원본 파일과 별도로 유지하기 위해 결과를 result
디렉터리의 파일에 기록합니다.
#!/bin/sh
mkdir -p result
i=1
for name in dir/SMVV50065*.csv; do
test -f "$name" || continue
newname=$( printf 'newfile%02d.csv' "$i" )
i=$(( i + 1 ))
awk 'BEGIN { FS=OFS="," } { for (i=1;i<=NF;++i) if ($i ~ /^ *$/) $i = "NA"; print }' "$name" >result/"$newname"
done
여기서 내가 한 유일한 일은 result
시작할 때 디렉토리가 실제로 존재했는지 확인하는 것이었습니다. mkdir -p result
또한 #!
이것이 스크립트임을 나타내기 위해 상단에 한 줄을 추가했습니다 sh
.
다시 몇 가지 진단 및 매개변수화를 추가합니다.
#!/bin/sh
indir=dir
outdir=result
mkdir -p "$outdir"
i=1
for name in "$indir"/SMVV50065*.csv; do
if [ ! -f "$name" ]; then
printf 'Not a regular file: "%s"\n' "$name" >&2
continue
fi
newname=$( printf '%s/newfile%02d.csv' "$outdir" "$i" )
i=$(( i + 1 ))
printf 'Processing "%s" into "%s"...\n' "$name" "$newname" >&2
awk 'BEGIN { FS=OFS="," } { for (i=1;i<=NF;++i) if ($i ~ /^ *$/) $i = "NA"; print }' "$name" >"$newname"
done
원한다면 sed
내 명령 대신 여기에 명령을 입력할 수도 있습니다.awk
댓글에 있는 질문:
위의 작업은 어려워 보이는데 왜 할 수 없나요?
foreach file (ls SMVV50065-2015-0[1-7].csv)
sed 's/ ,/NA/g' > newfile0[1-7].csv
end
회신하다:
먼저 올바른 구문을 사용하여 시작해야 합니다. 이는 쉘 구문과 다소 비슷해 보이지만 csh
질문에는 특정 쉘이 언급되지 않고 sh
유사한 쉘이 더 일반적으로 사용되므로그리고csh
나는 and 에 대한 개인적인 경험이 거의 없기 때문에 tcsh
이를 구문으로 변환하겠습니다 sh
.
sh
쉘의 루프는 for
while 이며 대괄호 대신 및를 foreach
사용합니다 . 또한 for 루프 사용을 제안했지만 엄밀히 말하면 대화형 명령을 사용하면 그 결과는 다음과 같습니다.in
do
ls
ls
보기 전용(바라보다"왜 `ls`를 구문 분석하지 *않나요*?"). 파일 이름 글로빙 패턴을 사용하면 반복할 파일 이름 목록을 생성하는 데 충분합니다.
이제 올바른 구문으로 루프를 사용해 보겠습니다.
for file in SMVV50065-2015-0[1-7].csv; do
sed 's/ ,/NA/g' > newfile0[1-7].csv
done
여기서 루프의 다음 문제는 그것이 $file
유용한 값인지 단순히 알 수 없다는 것입니다. 패턴이 SMVV50065-2015-0[1-7].csv
디렉터리 이름과 일치하거나 전혀 일치하지 않으면 패턴을 사용해서는 안 되므로 $file
테스트해 보겠습니다.
for file in SMVV50065-2015-0[1-7].csv; do
test -f "$file" || continue
sed 's/ ,/NA/g' > newfile0[1-7].csv
done
이제 sed
호출하세요. 몇 가지 작업을 처리할 수 있도록 파일 이름 $file
을 전달해야 합니다.sed
for file in SMVV50065-2015-0[1-7].csv; do
test -f "$file" || continue
sed 's/ ,/NA/g' "$file" > newfile0[1-7].csv
done
다음 문제는 실제로 출력을 sed
파일 이름 globbing 패턴으로 리디렉션할 수 없다는 것입니다 newfile0[1-7].csv
. globbing 패턴은 셸에 의해 패턴과 일치하는 모든 이름으로 확장되거나 일치하지 않는 경우 확장되지 않은 상태로 유지됩니다. 상태.
현재 디렉토리에는 패턴 newfile0[1-7].csv
과 일치하는 파일이 없다고 가정합니다. 그런 다음 루프는 이라는 파일을 생성 newfile0[1-7].csv
하고 루프가 반복될 때마다 채우기를 덮어씁니다.
그래서 i
각 반복마다 새 파일 이름을 구성할 수 있도록 변수를 도입했습니다.
i=1
for file in SMVV50065-2015-0[1-7].csv; do
test -f "$file" || continue
sed 's/ ,/NA/g' "$file" >"newfile0$i.csv"
i=$(( i + 1 ))
done
아마도 처리할 파일이 7개보다 훨씬 많다고 가정합니다. 그래서 printf
0으로 채워진 숫자가 포함된 파일 이름을 얻었는지 확인하기 위해 출력 파일 이름 생성을 사용하여 몇 가지 추가 문제를 겪었습니다.
위의 루프가 도움이 될 수 있지만 약간 다시 작성하면(새 파일 이름을 변수에 할당하고 와 함께 사용 sed
):
i=1
for file in SMVV50065-2015-0[1-7].csv; do
test -f "$file" || continue
newname="newfile0$i.csv"
i=$(( i + 1 ))
sed 's/ ,/NA/g' "$file" >"$newfile"
done
바라보다? 우리는 거의 내 솔루션으로 돌아왔습니다(마지막 변형의 추가 기능 없이). 유일한 근본적인 차이점은 여기에서는 모든 파일이 현재 디렉터리에서 사용 가능하고 출력 파일이 원본 파일과 함께 생성되어야 한다고 가정한다는 것입니다.
답변2
아래는 내가 시도한 것입니다.
filnames.txt==> 모든 파일 이름을 포함합니다.
for j in `cat filenames.txt`; do sed "s/ ,/NA/g" $j >newfiles_$i;i=$(($i + 1)); done