파일에서 값을 grep하고 FILENAME을 기반으로 다른 파일로 인쇄하는 방법

파일에서 값을 grep하고 FILENAME을 기반으로 다른 파일로 인쇄하는 방법

A_bla.csv(예: 등...) 과 같은 여러 파일이 있습니다 B_bla.csv C_bla.csv(이것은 실제 파일의 정렬 하위 샘플이지만 열 수는 실제입니다).

1,test,test2,55.2,test3
1,test,test2,96.3,test3
1,test,test2,64.2,test3
1,test,test2,97.2,test3

및 기본 파일 main.tsv(필드 구분 기호 = \t):

id  coverage    clade
A   wrongdata   20
B   wrongdata   19
C   wrongdata   19

*_bla.csvcoverage각 파일의 4열, 89행의 필드를 파일 열에 인쇄하고 싶습니다 main.tsv. 이렇게 하려면 *_bla.csv파일의 FILENAME을 사용하고 이를 id파일 열의 패턴 으로 사용해야 합니다 main.tsv.

지금까지 나는 다음을 시도했습니다.

for file in *_bla.csv ; do
r="$(basename -s "_bla.csv" $file)"
awk ... ; 
done

하지만 이제 곧 답변을 시작하겠습니다. Linux 시스템에 기본으로 제공되는 도구(awk, grep, sed, python, perl...)만 사용하여 이를 수행하는 방법을 알고 계십니까? 감사해요

답변1

피할 수 없는 GNU sed라인:

sed '1n;h;s/\([[:alnum:]]\).*/sed -E "89!d;s_([^,]*,){3}__;s_,.*__" \1_bla.csv/e;G;s/\(.*\)\n\(.*\)wrongdata/\2\1/' main.tsv

마법은 무엇입니까? e대체 명령의 xecute 옵션을 사용 하고 다른 명령을 사용하여 s89행의 4번째 필드를 추출합니다. sed상세히:

  • 1n첫 번째 줄은 변경하지 않고 그대로 둡니다.
  • h엉망이 되기 전에 예약된 공간에 라인을 저장하세요
  • \([[:alnum:]]\).*전체 줄을 일치시키고 교체 시 \(\)참조 할 수 있도록 첫 번째 영숫자 필드를 캡처합니다.\1
  • sed -E "89!d;s_([^,]*,){3}__;s_,.*__" \1_bla.csv대체 방법은 다음과 같습니다. 89를 제외한 모든 행이 삭제되고 d처음 세 필드가 삭제되며 마지막으로 새로운 첫 번째 필드 이후의 모든 항목이 삭제됩니다. 따라서 실제로는 89행의 네 번째 필드만 남아 있으므로 e버퍼 실행에 의해 이 필드가 반환됩니다.
  • 이제 저장 공간에 저장 라인을 추가 G하고 추출된 필드로 바꾸면 됩니다 s/\(.*\)\n\(.*\)wrongdata/\2\1/.wrongdata

답변2

awk -F',|\t' '
FILENAME != "main.tsv" && FNR == 89 {
                        sub(/_.*$/, "", FILENAME)
                        A[FILENAME]=$4
                        }
FILENAME != "main.tsv"  {next}
A[$1]                   {$2 = A[$1]}
                        {print}
' *bla.csv OFS='\t' main.tsv

파일에 탭 구분 기호가 없습니다 main.tsv. 그런 다음 패턴을 다음과 같이 수정해야 합니다.-F',|[[:blank:]]+'

답변3

awk 유틸리티를 사용하여 이 작업을 수행할 수 있습니다. csv 파일이 있는 디렉터리에서 실행해야 합니다.

awk -v OFS='\t' '
  FS=="," && FNR==89 {
    split(FILENAME, a, "_")
    h[substr(a[1],3)] = $4
  }
  f=FS=="\t" {
    $2 = FNR>1&&($1 in h) ? h[$1] : $2
  };f
' FS=, ./*_bla.csv FS="\t" ./main.tsv

id  coverage  clade
A   35.8      20
B   65.7      19
C   35.8      19

perl -F, -lane '
  if (@ARGV) {
    89..89 && do{
      $h{($ARGV=~/^..(\w+)_/)[0]} = $F[3];
      close ARGV;
    };
  } else { #last file here
    @F = split "(\t)";
    $F[2] = $h{$F[0]} // $F[2];
    print($.>1?@F:$_)
  }
' ./*_bla.csv ./main.tsv

GNU sed. 하지만 먼저 각 CSV 파일에서 89행, 필드 4를 파악하고 sed s/// 문의 RHS를 안전하게 삽입할 수 있도록 나타나는 데이터를 이스케이프합니다.

hold=$(
  grep -Pzo '^(?:.*\n){88}(?:[^,]*,){3}\K[^,]*' -- *_bla.csv |
  tr '\0' '\n'|
  sed -Ee '
    s/_.*:/\t/
    s:[\&/]:\\&:g
    $!s:$:\\:
  ')

sed -En \
  -e "1{x;s/.*/$hold/;x;}" \
  -e '
    1!G
    s/^(\S+\t)wrongdata(\t.*)\n\1([^\n]*)/\1\3\2/
    P
  ' \
./main.tsv

python3 -c '
import sys, pathlib, itertools
mainf = sys.argv[1]
fs,rs = ofs,ors = "\t","\n"
d,dlm = {},","
for p in pathlib.Path(".").glob("*_bla.csv"):
  if p.is_file():
    id = p.name[:p.name.find("_")]
    with open(p.name) as f:
      d[id] = [l.rstrip(rs).split(dlm)[3] for l in itertools.islice(f,88,89)][0]
with open(mainf) as f:
  for l in f:
    L = l.rstrip(rs).split(fs)
    print(L[0], d.get(L[0],L[1]), *L[2:], sep=ofs)
' ./main.tsv

관련 정보