Bash 스크립트의 while 루프는 표준 입력 또는 매개변수를 읽습니다.

Bash 스크립트의 while 루프는 표준 입력 또는 매개변수를 읽습니다.

나는 이 스레드에서 허용되는 답변을 작업 중입니다. 파이프 또는 명령줄 인수에서 파일 이름을 읽는 Bash 스크립트?

아래 스크립트를 사용할 때 efetch(ftp://ftp.ncbi.nlm.nih.gov/entrez/entrezdirect/edirect.zip)는 id(매개변수, 예: 941241313)를 허용하지만 stdin은 허용하지 않습니다.

if [ $# -gt 0 ] ;then
    for name in "$@"; do
        efetch -db nucleotide -id $name -format gpc > $name.xml;
    done
  else
    IFS=$'\n' read -d '' -r -a filenames
    while read name; do
        efetch -db nucleotide -id $name -format gpc > $name.xml;
    done < "${filenames[@]}"
  fi

다음 버전으로 수정하면 efetch가 id 대신 stdin을 허용합니다.

if [ $# -gt 0 ] ;then
    while read name; do
        efetch -db nucleotide -id $name -format gpc > $name.xml;
    done < "$@"
  else
    IFS=$'\n' read -d '' -r -a filenames
    while read name; do
        efetch -db nucleotide -id $name -format gpc > $name.xml;
    done < "${filenames[@]}"
  fi

뭐가 문제 야?

답변1

이 블록은 무엇이 바뀌었나요?

while read name; do
    efetch -db nucleotide -id $name -format gpc > $name.xml;
done < "$@"

이로 인해 efetchin 루프가 인수에 의해 제공된 파일로 리디렉션된 표준 입력으로 실행됩니다. 따라서 efetch사용법이 두 가지 변경됩니다.

  • 표준 입력은 더 이상 기본값(터미널)이 아닙니다.
  • 해당 매개변수 목록은 더 이상 리터럴 스크립트 명령줄 매개변수가 아니며 파일에서 간접적으로 제공됩니다.

입력이 터미널이 아니라는 것을 감지 하면 efetch터미널을 다시 열 가능성이 높습니다(아마도 "efetch가 id 대신 stdin을 허용합니다"라는 뜻일 것입니다). 또는 efetch표준 입력을 읽는 중이라면 예상치 못한 내용을 읽을 수도 있습니다(빠른 테스트에서는 스크립트 자체인 것처럼 보였습니다).

@chepna쉘(이 경우 bash)은 루프에 대한 하위 프로세스를 생성하지 않는다는 점을 지적합니다. 나는 다른 상황을 생각했다. 다음 두 스크립트를 고려하십시오.

#!/bin/bash 
LAST=...
while read name
do
    /bin/echo "** $name"
    LAST="$name"
done < "$@"
echo "...$LAST"

그리고

#!/bin/bash
LAST=...
cat "$@" | while read name
do
    /bin/echo "** $name"
    LAST="$name"
done
echo "...$LAST"

후자(파이프라인)는 끝에 "..."를 에코하는 반면 전자(리디렉션)는 LAST루프 내에 할당된 마지막 변수를 에코합니다. 파이프를 사용하는 양식은 변수 할당이 루프 외부로 전파되지 않는 이유를 설명하기 위해 하위 프로세스가 필요하다고 설명되기도 합니다.

흥미롭게도 후자(파이프라인) 셸에는 사용되는 프로세스 수에 차이가 있습니다. strace -fo시스템 호출 및 프로세스 ID 캡처를 위해 (Debian/testing) bash, dash(/bin/sh), zsh 및 ksh93으로 테스트되었습니다 .

#!/bin/sh
for sh in bash dash zsh ksh93
do
    echo "++ $sh"
    strace -fo $sh.log ./do-$sh ./once
    LC=$(sed -e 's/ .*//' $sh.log |sort -u |wc -l)
    WC=$(wc -l $sh.log)
    echo "-- $LC / $WC"
done

이 스크립트는 셸당 프로세스 수와 시스템 호출 수를 표시합니다. (파일에는 once테스트 경계를 제거하기 위한 "첫 번째"와 "두 번째"라는 두 줄이 포함되어 있습니다.)

zsh와 ksh93은 bash와 dash보다 하나 더 적은 프로세스를 사용한다는 것을 발견했습니다.

$ ./testit
++ bash
** first
** second
......
-- 5 / 401 bash.log
++ dash
** first
** second
......
-- 5 / 222 dash.log
++ zsh
** first
** second
...second
-- 4 / 568 zsh.log
++ ksh93
** first
** second
...second
-- 4 / 336 ksh93.log

이 예에서 파이프라인을 실행하려면 여기에 설명된 것보다 1개 또는 2개 더 많은 프로세스가 필요합니다.

답변2

나에게 나쁜 점은 @chepner가 지적했듯이 이 코드가 전혀 작동하지 않는다는 것입니다. 어떤 이유로 내 스크립트가 손상되었습니다. 이는 GNU 스크린 세션(서버 내)에서 nano를 사용하여 수행됩니다. 이 줄을 주석 처리한 후에도 efetch는 여전히 작동합니다!

IFS=$'\n' read -d '' -r -a filenames
while read name; do
    efetch -db nucleotide -id $name -format gpc > $name.xml;
done < "${filenames[@]}"

아래 스크립트는 보기 흉하지만 작동합니다.

#!/bin/bash
if [ $# -eq 0 ]; then
    echo "Please provide either a GI or a list of GI (one per line) in a file!"
elif [ -r "$@" ]; then
    while read name; do
        efetch -db nucleotide -id $name -format gpc > $name.xml;
    done < "$@"
else
    efetch -db nucleotide -id "$@" -format gpc > "$@".xml;
fi

관련 정보