bash 스크립트를 사용하여 csv 파일의 두 필드(하나 대신)를 출력으로 인쇄하려면 어떻게 해야 합니까?

bash 스크립트를 사용하여 csv 파일의 두 필드(하나 대신)를 출력으로 인쇄하려면 어떻게 해야 합니까?

과거에는 bash 스크립트를 작성하기 위해 bash를 많이 사용하지 않았으며 현재는 읽기용으로 bash 스크립트를 사용하고 있습니다. 파일에는 csv 형식으로 저장된 많은 필드가 포함되어 있습니다. 아래의 첫 번째 스크립트는 파일의 모든 IP를 수집하지만 수집하는 데에도 어려움을 겪고 있습니다.지적 재산권또 다른 필드가 호출됩니다.회로망.. 내가 이것을 달성할 수 있는지 아는 사람이 있나요?

files=`ls | grep data_batch_`
for file in ${files[@]}
do
  cat ${file} | cut -d , -f2 | grep -v "IP" > data_${file}
done

나는 성공하지 못한 채 부울 연산자를 추가해 보았습니다. 또한 더 많은 파이프를 시도했습니다. 저는 bash를 자주 사용하지 않기 때문에 일부 구문이 누락되었거나 이것이 허용되지 않는 이유를 이해하지 못할 수도 있습니다.

    files=`ls | grep data_batch`
for file in ${files[@]}
do
  cat ${file} | cut -d , -f2 | cut -d, -f3 | grep -v "IP" && "Network" > data_${file}
done

어떤 이유에서인지 이 작업을 수행하면 덮어쓰는 것 같습니다.지적 재산권가치를 부여하다회로망값을 동시에 저장하는 대신. 본질적으로 내가 원하는 것은 하나의 필드가 아닌 두 개의 필드를 파일에 인쇄하는 것이지만 그의 솔루션을 구현하는 방법을 잘 모르겠습니다. 어떤 팁이라도 도움이 될 것입니다.

내가 원하는 출력은 파일에 저장된 IP 주소 값과 네트워크 값입니다. 현재 내가 얻는 것은 IP뿐입니다. 아래는 원하는 출력입니다.

1.1.1.1
Network5

답변1

스크립트에 많은 문제가 있습니다.

files=`ls | grep data_batch_`
for file in ${files[@]}
do
  cat ${file} | cut -d , -f2 | grep -v "IP" > data_${file}
done
  1. ls를 구문 분석하지 마세요

  2. 백틱을 사용하지 마세요. 대신 사용하십시오 $(). 동일한 작업을 수행하지만 참조를 중단하지 않으며 중첩될 수 있습니다.

  3. filesfor배열인 것처럼 루프에서 사용 하지만 배열은 아닙니다. 이를 스칼라 문자열(의 출력 ls | grep ...)로 정의합니다. 배열을 정의하려면 다음과 같이 괄호를 사용해야 합니다.

    files이는 문자열로 정의됩니다 .

    $ files=$(echo 1 2 3)
    $ declare -p files
    declare -- files="1 2 3"
    

    비록 이것이 배열로 정의되어 있지만:

    $ files=( $(echo 1 2 3) )
    $ declare -p files
    declare -a files=([0]="1" [1]="2" [2]="3")
    

    mapfile또는 (일명 )을 사용할 수 있습니다 readarray.

     $ mapfile -t files < <(printf "%s\n" 1 2 3)
     $ declare -p files
     declare -a files=([0]="1" [1]="2" [2]="3")
    
  4. 변수 확장을 큰따옴표로 묶으십시오. 중괄호를 사용하는 것은아니요인용된 대안. 바라보다공백이나 기타 특수 문자 때문에 쉘 스크립트가 멈추는 이유는 무엇입니까?그리고$VAR 대 ${VAR} 및 인용 여부이유가 있습니다.

  5. 두 번째 스크립트에서는 출력 cut -d, -f2cut -d, -f3. 그건 작동하지 않습니다.

    첫 번째는 cut하나의 필드(필드 2)만 출력합니다. 두 번째 항목은 cut입력에 필드가 하나만 있고(또는 쉼표가 없으므로 필드가 없음) 존재하지 않는 필드를 출력하도록 지시했기 때문에 정확히 동일하게 출력됩니다. 3. 실행한 echo 1,2,3 | cut -d, -f2다음 실행하면 다음과 같은 결과가 echo 1,2,3 | cut -d, -f2 | cut -d, -f3표시됩니다. 두 명령 모두 출력은 동일합니다. 2.

    두 개의 출력 필드를 사용하려면 cut -f쉼표로 구분하여 나열하세요. 예를 들어:

    cut -d, -f2,3
    

    그런데 를 사용하여 필드 범위를 지정할 수도 있습니다 -. 예를 들어 필드 2~5를 출력하려면 다음을 사용할 수 있습니다 cut -d, -f2-5. 바라보다 man cut.

  6. 이것이 문제인지는 모르겠지만, 알아두셔야 할 부분입니다. 스크립트는 입력 파일과 이름이 같지만 접두사가 붙은 출력 파일로 stdout을 리디렉션합니다 data_. 따라서 입력 파일 data_batch_1.csvdata_data_batch_1.csv.

    이것이 바로 당신이 원하는 것일 수 있으며, 이 경우에는 문제가 되지 않습니다. 그러나 이는 스크립트를 다시 실행하면 파일 glob이 원래 입력 파일과 일치한다는 것을 의미합니다.그리고첫 번째 실행에서는 출력 파일이 생성됩니다. 결과적으로 data_data_data_batch_1.csv.


그럼에도 불구하고 이것이 문제입니다. 다음은 몇 가지 해결 방법입니다. 다음과 같은 방법을 더 시도해 보세요.

for file in *data_batch_*; do
  cut -d, -f2,3 "$file" | grep -v IP > "data_$file"
done

파일 이름 배열을 실제로 사용하려면 예를 들어 mapfile및 를 사용할 수 있습니다.find-print0

mapfile -t -d '' files < <(find . -maxdepth 1 -type f -name '*data_batch_*' -print0)
for file in "${files[@]}"; do
   cut -d, -f2,3 "$file" | grep -v IP > "data_$file"
done

awk또는 다음을 대신 사용할 수 있습니다 cut.

awk -F, -v OFS=, '$2$3 !~ /IP/ { print $2, $3 > "data_" FILENAME }' *data_batch_*

$2"IP" 나 "IP" 가 모두 포함되지 않은 경우 현재 파일 이름(awk의 변수)과 동일한 이름을 가진 파일로 리디렉션되고 "data_"라는 문자열이 앞에 붙은 $3stdout을 사용하여 인쇄됩니다 .FILENAME

cut이는 처리하는 각 파일에 대해 한 번씩 여러 번 분기 하고 grep수행할 필요가 없기 때문에 훨씬 더 빠릅니다 .


마지막으로, CSV 파일에는 큰따옴표로 묶인 문자열 필드가 포함될 수 있으며 종종 포함됩니다. 이러한 인용된 필드에는 쉼표가 포함될 수 있습니다. 따옴표가 없고 쉼표가 포함된 필드가 없는 간단한 쉼표로 구분된 파일은 를 사용하여 안정적으로 처리할 수 있습니다 cut. 모든 선택적 추가 기능이 포함된 실제 CSV에는 CSV 파서가 필요합니다. 가장 좋은 방법은 다음을 사용하는 것입니다.

  1. 모든 기능을 갖춘 CSV 파서가 이미 있는 언어(예 perl:텍스트::CSV모듈 python을 포함하며데이터 세트도서관.

  2. 이런 도구밀러또는csvkit

답변2

awk를 사용할 수 있는 경우:

$ cat /tmp/abc
name1,0.0.0.0,NetworkName1
name2,0.4.2.3,NetworkName2
name3,0.1.43.5,NetworkName3

$ awk 'BEGIN { FS = "," } ;{printf $2","$3"\n"}' /tmp/abc
0.0.0.0,NetworkName1
0.4.2.3,NetworkName2
0.1.43.5,NetworkName3

따라서 이 경우에는

for i in $(ls | grep -E ^test.*[.]csv$)
do
    cat $i | cut -d , -f2,3 >> testing.txt
done

될 수 있다

$ awk 'BEGIN { FS = "," } ;{printf $2","$3"\n"}' test*.csv > testing.txt

구조화된 텍스트 처리를 많이 한다면 awk를 배우는 데 시간을 투자하는 것이 도움이 될 것입니다.

답변3

나는 다음과 같은 행운을 누렸습니다.

디렉토리 내용:

$ ls
test.csv  test1.csv  test3csv test5.txt

각 파일에는 다음과 같은 줄이 포함되어 있습니다.

name1,0.0.0.0,NetworkName1
name2,0.4.2.3,NetworkName2
name3,0.1.43.5,NetworkName3

스크립트:


for i in $(ls | grep -E ^test.*[.]csv$)
do
    cat $i | cut -d , -f2,3 >> testing.txt
done

이렇게 하면 test로 시작하고 로 끝나는 모든 파일을 가져와서 .csv필드 2와 3을 제거하고 파일에 추가합니다 testing.txt.

그 이후의 출력 파일은 다음과 같습니다.

0.0.0.0,NetworkName1
0.4.2.3,NetworkName2
0.1.43.5,NetworkName3

각 IP 주소와 각 네트워크 이름을 별도의 줄에 나열합니다.

스크립트에서 출력 파일의 내용을 덮어쓰는 이유는 현재 >파일의 모든 내용을 덮어쓰는 연산자를 사용하고 있기 때문입니다. 반면에 원하는 것은 >>파일 끝에 텍스트를 추가하는 연산자일 것입니다. 파일의.

관련 정보