쉘 스크립트의 시스템 기능 내에서 기능을 실행하는 방법은 무엇입니까?

쉘 스크립트의 시스템 기능 내에서 기능을 실행하는 방법은 무엇입니까?

meta.txt다음 정보가 포함된 텍스트 파일이 있습니다.

여기에 이미지 설명을 입력하세요.

이라는 또 다른 것이 있습니다.data

output_folder = "data"

conv아래와 같이 함수 내부에 이름이 지정된 함수를 실행하려면 of에서 경로 조회를 사용하여 system파일을 다음으로 변환합니다.fastq.gz2nd columnmeta.txtfastq.gzfastq.txt

tail -n+2 meta.txt | awk -v output_folder=${output_folder} '{ system("convert " $2 $output_folder/"fastq.txt") }' 

하지만 결국 다음과 같은 오류가 발생했습니다.

awk: cmd. line:1: (FILENAME=- FNR=1) fatal: division by zero attempted

답변1

awk는 다른 도구를 순서대로 호출하는 도구가 아니라 텍스트를 조작하기 위한 도구입니다. 이것이 쉘의 목적입니다. 테스트할 텍스트 입력/출력을 제공하지 않았으므로 이는 테스트되지 않은 추측이지만 코드에서 수행하려는 작업은 다음 셸 루프뿐입니다( IFS설정 또는 수정되지 않았다고 가정).

while read -r _ file; do
    convert "$file" "$output_folder/fastq.txt"
done < <(tail -n +2 meta.txt)

어떤 이유로 호출에 awk를 사용하고 싶다면 convert다음과 같이 하세요.

output_folder="$output_folder" \
awk '
    BEGIN { outfold = ENVIRON["output_folder"] }
    NR>1{ system("convert \047" $2 ("\047 \047" outfold "/fastq.txt\047") }
' meta.txt

그러나 이는 system()이 호출될 때마다 하위 쉘을 생성하므로 awk를 사용하여 이점을 얻기에는 너무 느립니다.

바라보다awk 스크립트에서 쉘 변수를 사용하는 방법ENVIRON[]awk 변형 이 ENVIRON[].

0으로 나누기 오류가 발생하는 이유는 내부 코드는 다음과 같습니다 system().

"convert " $2 $output_folder/"fastq.txt"

숫자가 아닌 문자열이 포함될 output_folder수 있으므로 실행하면 gawk --lint다음과 같은 경고 메시지가 표시됩니다.

warning: attempt to field reference from non-numeric value

/그것에 대해, 그러나 리터럴 문자열이 뒤에 오는 나누기 연산자가 있습니다 "fastq.txt". 이 문자열이 숫자로 처리되면(이 예에서와 같이) 0과 동일하므로 "0으로 나누기"입니다.

귀하의 의견과 업데이트된 질문에 관해:

질문에서 이 기능을 변경하세요.

function convert {
    INPUT=$1
    OUTPUT=$2

    INPUT_R=0
    if [ "${INPUT: -3}" == ".gz" ]; then
        INPUT_S=1
    fi
    if [[ $INPUT_R -eq 1 ]]; then
        gunzip -c ${INPUT} > ${OUTPUT}
    else
        cp -v ${INPUT} ${OUTPUT}
    fi
    chmod ug+rw ${OUTPUT}
}

문제를 해결합니다(일부는http://shellcheck.net당신에게 말할 것입니다 - 쉘에 더 익숙해질 때까지 항상 스크립트에서 이것을 실행하십시오):

#!/usr/bin/env bash

convert() {
    local input=$1 output=$2

    if [[ $input = *.gz ]]; then
        gunzip -c -- "$input" > "$output"
    else
        cp -v -- "$input" "$output"
    fi &&
      chmod -- ug+rw "$output"
}

output_folder='/Users/doc'

# now include this:
while read -r _ file; do
    convert "$file" "$output_folder/fastq.txt"
done < <(tail -n +2 meta.txt)

# or this at the end of the same script:
export -f convert # only works if sh is bash in your env since
                  # system() will call sh to run the command
output_folder="$output_folder" \
awk '
    BEGIN { outfold = ENVIRON["output_folder"] }
    NR>1{ system("convert \047" $2 ("\047 \047" outfold "/fastq.txt\047") }
' meta.txt

awk가 서브셸에서 함수를 호출할 수 있도록 하려면 함수를 내보내야 합니다. 사용하려는 출력 파일이 쓰기 가능하지 않은 이유를 스스로 파악해야 하지만 쉬울 것입니다.

~처럼스티븐 차제라스의견에 언급된 바와 같이(현재 구현된 위의 다른 의견에 감사드립니다):

  • 파일 경로에 문자가 포함되지 않는다는 보장이 없으면 '임의 명령 주입 취약점과 동일합니다.
  • 이는 \47ASCII 기반 시스템을 가정합니다(현재로서는 상대적으로 안전한 가정).
  • chmod ug+rw이를 0으로 변경하면 이를 방지할 수 있습니다 umask(그러나 월드 파일을 쓰기 가능으로 만드는 것은 어쨌든 매우 나쁜 생각처럼 들립니다).

답변2

치명적: 0으로 나누려고 시도함

예, 인용 문제가 있으므로 슬래시는 /원하는 방식으로 문자열 작업에 포함되지 않습니다.


이는 말도 안되는 인용을 포함하여 파이프라인 디버깅을 위한 매우 일반적인 전략입니다 awk.

문자열을 생성한 다음 cmd실행 system(cmd)하고 최선을 다하는 대신 다른 접근 방식을 취하십시오. awk 스크립트에 물어보세요산출명령 문자열은 표준 출력으로 전송되고 파이프의 마지막 부분은 sh(또는 bash)입니다.

이것의 장점은

  1. 에서 제안된 명령을 보고 시작할 수 있습니다 awk.그 다음에일단 상황이 좋아 보이 | sh거나 , | sh -x상황이 좋아 보이면
  2. 잠재적으로 유해한 명령(예:)을 디버깅하는 동안 rm먼저바라보다당신 앞에 무엇이 오나요?달리기그 명령.

답변3

다음이 필요한 것 같습니다.

(
  umask 0
  unset -v IFS
  read -r discarded_header &&
    IFS=' ' read -r discarded_first_field file &&
    gzip -dcf < "$file" > data/fasq.txt
) < meta.txt

누구나 읽고 쓸 수 있는 파일 사본(아마도 압축되지 않음)을 만드는 것이 요점이라면 파일 경로는 meta.txt.

관련 정보