Bash에서 가변 개수의 변수를 읽는 방법

Bash에서 가변 개수의 변수를 읽는 방법

명령줄에서 여러 문자열을 읽고 화장실의 모든 문자열을 읽고 싶습니다. 루프에서 추가로 처리(인쇄 등)하는 방법입니다. 죄송합니다. 초보자입니다. 초보자 질문에 대해 양해해 주시기 바랍니다.

#!/usr/bin/bash


echo "Enter the number of names of students"
read classcount

for ((i=1;i<=classcount;i++)); do
    read names[${i}]
done

for ((i=1;i<=classcount;i++)); do
    echo $names[${i}]
done

답변1

일반적으로 스크립트나 명령줄에서 사용되는 유틸리티에서 사용자 상호 작용을 피하는 것이 가장 좋습니다. 자동화를 방해할 수 있기 때문입니다.

예를 들어, 을 호출하면 삭제하려는 파일 수를 묻고 각 파일에 대해 개별적으로 메시지를 표시할 것이라고 기대 rm file1 file2 'file 3'하지 마십시오 .rm

다시 말하지만 여기에 나열하는 것이 좋습니다학생명령줄 매개변수에서:

#! /usr/bin/bash -
classcount=$#
names=("$@")

if (( classcount > 0 )); then
  echo "Student${name[1]+s}:"
  printf ' - %s\n' "${name[@]}"
fi

다음과 같이 호출됩니다.

that-script 'John Doe' 'Alice Smith' ...

또는 텍스트 파일에서 한 줄에 하나씩 학생 목록을 가져옵니다(GNU 가정 xargs).

xargs -d '\n' -a student.list that-script

표준 입력에서 각 줄을 읽을 수도 있지만, 사용자에게 먼저 개수를 묻기보다는 상호 작용 없이 상호 작용하고 입력이 끝날 때까지 목록을 읽는 것이 더 좋습니다.

#! /usr/bin/bash -

readarray -t names
classcount="${#names[@]}"

if (( classcount > 0 )); then
  echo "Student${name[1]+s}:"
  printf ' - %s\n' "${name[@]}"
fi

stdin이 터미널인 경우 사용자는 입력의 끝을 나타내는 데 사용됩니다 Ctrl+D.

파일에서 목록을 얻으려면:

that-script < student.list

readstdin에서 한 줄을 읽는 데 사용할 수 있지만 구문은 IFS= read -r lvalue, is 가 아닙니다 read lvalue.

또한 이는 [...]와일드카드이므로 목록 컨텍스트에 나타나는 모든 매개변수 확장처럼 인용되어야 합니다.

또한 배열 인덱스(Bourne 배열 제외 "$@")는 bashin 과 마찬가지로 ksh(그러나 대부분의 다른 쉘과는 달리) 1이 아닌 0에서 시작합니다.

따라서 귀하의 방법에 대한 구문은 다음과 같아야 합니다.

#! /usr/bin/bash -
PROGNAME=$0
die() {
  (( $# == 0 )) || printf >&2 '%s: ERROR: %s\n' "$PROGNAME" "$1"
  exit 1
}

IFS= read -rp "Enter the number of names of students: " classcount || die

# validate to avoid command injection vulnerability
shopt -s extglob # only needed in older versions of bash
[[ $classcount = @(0|[123456789]*([0123456789])) ]] ||
  die "Not a valid decimal integer number: $classcount"

for (( i = 1; i <= classcount; i++ )); do
  IFS= read -rp "Student $i: " 'names[i-1]' ||
    die "$(( classcount - i + 1 )) students missing"
done

for (( i=1; i <= classcount; i++)); do
  printf 'Student %s: %s\n' "$i" "${names[i-1]}"
done

$name[$i](t)csh(1970년대 후반에 배열이 처음 도입된 셸)에서 작동하지만, 복사본 의 배열 디자인 $name[$i]:q인 zsh에서 공백이나 와일드카드가 특별히 처리되지 않도록 해야 합니다.bashksh

이와 관련하여 ksh는 Bourne 쉘과 역호환되도록 노력하므로 Bourne 쉘에서와 같이 echo $name[$i]패턴과 일치하는 파일 목록이 출력 <contents-of-$name>[<contents-of-$i>]( $name및 분할)됩니다.$i

ksh/bash에는 구문이 필요하며 ${name[i]}( ${name[$i]}또는 그것도 작동함) 모든 매개변수 확장과 마찬가지로 분할+glob을 방지하려면 ${name[${i}]}따옴표로 묶어야 합니다 ."${name[i]}"

어떤 경우 에라도,echo임의의 데이터를 출력하는 데 사용할 수 없습니다..

답변2

다른 답변에 표시된 데이터 입력을 개선하는 다양한 방법 외에도 작은 변경으로 스크립트가 예상대로 작동하게 됩니다.

전문가

echo $names[${i}]

케이스가 팽창하여 다음 $names과 같은 ${i}문제가 발생할 수 있습니다.

[1]
[2]

잠깐, names정의가 없다고 가정하겠습니다. 그렇지 않으면 names여는 괄호 앞에 값이 추가 됩니다 .
(에서 언급했듯이스티븐 차제라스' 주석, 그러한 파일이 존재하는 경우 결과 [1]등은 따옴표 누락으로 인해 일치하는 파일 이름 등으로 대체될 수 있습니다. )1

대신에

echo "${names[${i}]}"

편집하다:

에서 언급했듯이이르카초댓글, 질문의 스크립트에 사용하기에 충분합니다.

echo "${names[i]}"

$인덱스는 산술 확장이므로 변수를 참조할 때 필요하지 않습니다.

답변3

목록을 대화형으로 요청하려는 경우에도 사용자가 미리 개수를 계산하고 올바른 개수를 입력하도록 요구하는 것보다 스크립트가 일부 종결자를 통해 목록의 끝을 감지하도록 하는 것이 더 사용자 친화적일 수 있습니다.

(결국 컴퓨터는 일반 사람보다 행을 더 정확하게 계산할 수 있어야 합니다. 이는 수동 메모리 관리 기능이 있는 언어를 사용하고 더 많은 입력을 받은 후 배열의 크기를 조정하고 싶지 않은 경우에만 가능합니다. 메모리가 제한된 임베디드 환경에서는 가능할 수도 있지만 "전체" 컴퓨터에서는 그렇지 않으며 쉘에서는 그렇지 않습니다.

대신, 빈 줄(다른 줄도 가능)을 입력하여 목록을 종료하고 비어 있지 않은 줄이 표시되는 한 수신된 이름을 배열에 추가하도록 사용자에게 요청할 수 있습니다. 또한 데이터를 처리할 때 실제로 인덱스가 필요하지 않은 경우 배열 항목 목록을 가져온 "${names[@]}"다음 이를 반복할 수 있습니다.

#!/bin/bash
names=()
echo "Please enter the names of students (end with an empty line)"
while IFS= read -r name && [ "$name" ]; do
    names+=("$name")
done

echo "You entered ${#names[@]} name(s): "
for n in "${names[@]}"; do
    echo "$n"
done

[ "$name" ]입력한 이름이 비어 있는지 테스트합니다. 파일의 끝이 보이면 루프도 종료됩니다.

관련 정보