List.csv
다음 형식의 CSV 파일이 있습니다 .
Location,IP Address,Host Name,Domain,Domain Name, User Name,Manufacturer,Model,System Type, Serial Number, Operating System,RAM (GB),Processor Type,Processor Frequency
H1,xx.xx.xx.xx,PC1,domain.com,DOMAIN,User1,LENOVO,4089AZ8,X86-based PC,L90RA96,Microsoft Windows 7 Professional ,2,Pentium(R) Dual-Core CPU E5800,3.20GHz
H3,xx.xx.xx.xx,PC2,domain.com,DOMAIN,User2,LENOVO,4089AZ8,X86-based PC,L906W3P,Microsoft Windows 7 Professional ,2,Pentium(R) Dual-Core CPU E5800,3.20GHz
H2,xx.xx.xx.xx,PC3,domain.com,DOMAIN,User3,LENOVO,4089A76,X86-based PC,L929410,Microsoft Windows 7 Professional ,2,Pentium(R) Dual-Core CPU E5400,2.70GHz
H2,xx.xx.xx.xx,PC4,domain.com,DOMAIN,User4,Hewlett-Packard,Z800,x64-based PC,SGH007QT16,Microsoft Windows 7 Professional ,12,Intel(R) Xeon(R) CPU W5590,3.33GHz
컬럼 을 보시면 MODEL
모델명을 설명하지 못하는 값이 일부 포함되어 있는 것을 보실 수 있습니다. model-list.csv
이 값과 해당 모델 이름이 포함된 다른 파일을 만들었습니다 . 그것은 다음과 같습니다:
Manufacturer,Value,Model Name
Lenovo, 4089AZ8, ThinkCentre
Lenovo, 4089A76, ThinkCentre
HP, Z800, HP Z800 Workstation
파일에 있는 List.csv
값 을 model-list.csv
.List.csv
model-list.csv
#!/bin/bash
file1="List.csv"
file2="model-list.csv"
outfile="List_out.csv"
stagingfile="List-staging.csv"
rm -f "$outfile" "$stagingfile"
while read line
do
ModelNo=`echo "$line"|awk -F',' '{print $2}'`
ModelName=`echo "$line"|awk -F',' '{print $3}'`
cat "$file1"|grep ",$ModelNo," > "$stagingfile"
if [ -s "$stagingfile" ]
then
while read line1
do
NewLine=`echo "$line1"|sed "s/,${ModelNo},/,${ModelName},/g"`
echo "$NewLine" >> "$outfile"
done < "$stagingfile"
rm -f "$stagingfile"
fi
done < "$file2"
위 스크립트를 실행할 "$outfile"
때 List.csv
.
스크립트에 문제가 있나요?
답변1
다음을 위해 사용할 수 있습니다 awk
:
awk -F',|, ' 'NR==FNR{a[$2]=$3} NR>FNR{$8=a[$8];print}' OFS=',' "$file2" "$file1"
이는 model-list.csv를 읽고 모든 모델과 해당 설명을 문자열 인덱스 배열(예: a["Z800"] == "HP Z800 Workstation"
)에 저장합니다. 그런 다음 목록 데이터를 읽고 각 모델을 배열의 설명 문자열로 바꿉니다.
설명하다:
-F',|, '
- 정규식 패턴을 사용하여 필드 구분 기호를 설정합니다. 이 경우 필드 구분 기호는 단일 쉼표이거나 단일 쉼표와 단일 공백이 됩니다.NR==FNR{a[$2]=$3}
- NR은 프로그램이 시작된 이후 읽은 총 줄 수를 추적하는 awk 내부 변수입니다. FNR은 비슷하지만 행 수를 기록합니다.현재 파일읽은 것.NR==FNR
"이것이 읽을 첫 번째 파일인 경우"를 의미하는 awk 관용구의 경우에도 마찬가지입니다.a[$2]=$3
필드 3의 값을 배열에 저장a
하고 문자열 인덱스를 필드 2의 값으로 설정합니다.NR>FNR{$8=a[$8];print}'
- 이전과 비슷하지만 이번에는 처음 읽은 파일이 아닌 파일에만 동작합니다. 각 행에 대해 필드 8의 값을 인덱스로 사용하여 배열의 값을 찾은 다음 필드 8을 배열 값에 다시 할당합니다. 마지막으로 전체 행이 인쇄됩니다.OFS=',' "$file2" "$file1"
- 출력 필드 구분 기호를 쉼표(기본값은 공백)로 설정하고 지정된 순서로 2개의 파일을 읽습니다.
답변2
몇 가지 참고사항:
- Bash는 데이터베이스 시뮬레이션에 있어서 끔찍한 언어입니다. 이 작업에 관계형 데이터베이스를 사용할 수 없습니까?
- 피하다쓸모없는 목적
cat
. 넌 할 수있어grep ",$ModelNo," "$file1"
. while IFS=, read -r _ ModelNo ModelName _
대기열을 피할 수 있습니다awk
.- Bash 에서는
my_command <<< "$variable"
.echo "$variable" | my_command
- 가독성을 위해 대신 사용해야 합니다
$(my_command)
.`my_command`
grep -F
리터럴 문자열이 검색됩니다.- 종료 코드를 확인하여
grep
무엇이든 찾을 수 있는지 확인할 수 있습니다. 파일 크기를 확인하는 것보다 빠릅니다.
답변3
Bash에서는 bash 버전이 4보다 크다고 가정하면 다음 명령을 사용하여 쉽게 이 작업을 수행할 수 있습니다.연관 배열:
#!/usr/bin/env bash
## declare models as an associative array
declare -A models
## read the 1st file, load the Value => Model pair
## pairs into the models array. Note that I'm setting bash's
## Input Field Separator ($IFS) to comma (,) and that I first pass
## the file through sed to remove the spaces after the commas.
## For more on why I'm using <() instead of a pipe, see
## http://stackoverflow.com/q/9985076/1081936
while IFS=, read -r man val mod;
do
models["$val"]="$mod"
done < <(sed 's/, /,/g' "$1")
## Read the second file. I am defining 9 variables, 8 for
## the first 8 fields, up to the model and $rest for the rest of
## the fields, up to the end of the line.
while IFS=',' read -r loc ip host dom dnam user manu model rest;
do
printf "%s,%s,%s,%s,%s,%s,%s,%s,%s\n" "$loc" "$ip" "$host" "$dom" \
"$dnam" "$user" "$manu" "${models[$model]}" "$rest";
done < <(sed 's/, /,/g' "$2")
지침:
List.csv
model-list.csv
hasModel Name
whereList.csv
has 이기 때문에 게시한 특정 콘텐츠의 첫 번째 줄에서는 실패합니다Model
. 이는${models[$model]}
첫 번째 행에 일치하는 항목이 없음을 의미합니다. 파일 중 하나의 헤더를 편집하여 필드 이름을 동일하게 만들거나 다음 버전을 사용하여 이 문제를 해결할 수 있습니다.#!/usr/bin/env bash declare -A models while IFS=, read -r man val mod; do models["$val"]="$mod" done < <(sed 's/, /,/g' "$1") ## Set up a counter to hold the line numbers c=0; while IFS=',' read -r loc ip host dom dnam user manu model rest; do ## Increment the line number (( c++ )); ## If this is the 1st line, print if [ "$c" -eq "1" ]; then printf "%s,%s,%s,%s,%s,%s,%s,%s,%s\n" "$loc" "$ip" "$host" "$dom" \ "$dnam" "$user" "$manu" "$model" "$rest"; else printf "%s,%s,%s,%s,%s,%s,%s,%s,%s\n" "$loc" "$ip" "$host" "$dom" \ "$dnam" "$user" "$manu" "${models[$model]}" "$rest"; fi done < <(sed 's/, /,/g' "$2")
이는 파일이 표시된 것처럼 간단하다고 가정합니다.모두필드는 쉼표로 정의되며 어떤 필드에도 쉼표가 포함될 수 없습니다.
물론 Perl에서는 이 작업을 더 간단하게 수행할 수 있습니다.
perl -F',\s*' -lane '$k{$F[1]}=$F[2]; next if $#F < 4; s/$F[7]/$k{$F[7]}/; print' model-list.csv List.csv
설명하다
-F
각 입력 행을 배열 로 자동 분할하는 데 사용되는 필드 구분 기호(여기서는,
0개 이상의 공백 문자가 뒤에 옴)를 설정합니다.-a
@F
-l
\n
각 줄 끝의 자동 제거를 켜고\n
각 명령문에print
암시적 내용을 추가합니다 .-n
입력 파일을 한 줄씩 읽고-e
여기에 전달된 스크립트를 적용하는 것을 의미합니다.$k{$F[1]}=$F[2]
%k
: 각 행의 두 번째 필드가 키이고 값이 세 번째 필드인 곳이 채워집니다 . 이는 관련이 있을model-list.csv
뿐 아니라 running 에도 적용됩니다List.csv
.List.csv
두 번째 필드로도 나타나는 8번째 필드가 포함되지 않는 한 이 필드는 무시해도 됩니다.model-list.csv
next if $#F < 4
: 이 행에 필드가 4개 미만인 경우 다음 행을 읽어보세요. 이는print
다음 줄이 인쇄되지 않기 때문입니다.model-list.csv
s/$F[7]/$k{$F[7]}/; print
: 현재 줄의 8번째 필드를 해시에 저장된 내용으로 바꾸고%k
해당 줄을 인쇄합니다.