명령줄에서 여러 문자열을 읽고 화장실의 모든 문자열을 읽고 싶습니다. 루프에서 추가로 처리(인쇄 등)하는 방법입니다. 죄송합니다. 초보자입니다. 초보자 질문에 대해 양해해 주시기 바랍니다.
#!/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
read
stdin에서 한 줄을 읽는 데 사용할 수 있지만 구문은 IFS= read -r lvalue
, is 가 아닙니다 read lvalue
.
또한 이는 [...]
와일드카드이므로 목록 컨텍스트에 나타나는 모든 매개변수 확장처럼 인용되어야 합니다.
또한 배열 인덱스(Bourne 배열 제외 "$@"
)는 bash
in 과 마찬가지로 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에서 공백이나 와일드카드가 특별히 처리되지 않도록 해야 합니다.bash
ksh
이와 관련하여 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" ]
입력한 이름이 비어 있는지 테스트합니다. 파일의 끝이 보이면 루프도 종료됩니다.