소스코드를 읽을 때FFread
Bash 프로그래밍에 대해 자세히 알아보기 위해 배열로 전달된 시간 초과 옵션을 보았습니다 .여기:
read "${read_flags[@]}" -srn 1 && key "$REPLY"
값이 read_flags
설정되었습니다.이와 같이:
read_flags=(-t 0.05)
(그래서 최종 read
호출은 read -t 0.05 -srn 1
)입니다.
왜 문자열을 사용할 수 없는지 잘 모르겠습니다. 예:
read_flags="-t 0.05"
read "$read_flags" -srn 1 && key "$REPLY"
이 문자열 기반 접근 방식은 "잘못된 시간 초과 사양"을 초래합니다.
몇 가지 조사 끝에 테스트 스크립트를 생각해 냈습니다 parmtest
.
show() {
for i in "$@"; do printf '[%s]' "$i"; done
printf '\n'
}
opt_string="-t 1"
opt_array=(-t 1)
echo 'Using string-based option...'
show string "$opt_string" x y z
read "$opt_string"
echo
echo 'Using array-based option...'
show array "${opt_array[@]}" x y z
read "${opt_array[@]}"
bash parmtest
( $BASH_VERSION
is 5.1.4(1)-release) 와 함께 이 명령을 실행하면 다음이 제공됩니다.
Using string-based option...
[string][-t 1][x][y][z]
parmtest: line 11: read: 1: invalid timeout specification
Using array-based option...
[array][-t][1][x][y][z]
(1 second delay...)
디버그 출력을 보면 1
배열 기반 방식의 값이 개별적이고 공백이 없다는 것을 알 수 있습니다. 오류 메시지에서도 알 수 있습니다 1
. 앞에 추가 공간이 있습니다 read: 1: invalid timeout specification
. 내 의심은 바로 그 부분에 있다.
이상하게도 예를 들어 다른 명령과 함께 이 접근 방식을 사용하면 date
문제가 존재하지 않습니다.
show() {
for i in "$@"; do printf '[%s]' "$i"; done
printf '\n'
}
opt_string="-d 1"
opt_array=(-d 1)
echo 'Using string-based option...'
show string "$opt_string" x y z
date "$opt_string"
echo
echo 'Using array-based option...'
show array "${opt_array[@]}" x y z
date "${opt_array[@]}"
(유일한 차이점 opt_string
은 이제 not을 opt_array
지정 하고 모든 경우에 not을 호출한다는 것입니다 .)-d
-t
date
read
실행하면 bash parmtest
다음이 생성됩니다.
Using string-based option...
[string][-d 1][x][y][z]
Wed Sep 1 01:00:00 UTC 2021
Using array-based option...
[array][-d][1][x][y][z]
Wed Sep 1 01:00:00 UTC 2021
오류가 없습니다.
나는 이 질문에 대한 답을 찾고 있었지만 헛된 일이었습니다. 게다가 저자는 이 문단도 썼다.한숨에 어레이를 직접 사용, 궁금해집니다.
미리 감사드립니다.
9월 3일 업데이트됨: 다음은 제가 지금까지 읽으면서 배운 내용을 기록한 블로그 게시물입니다. fff
또한 이 질문과 그 안에 있는 훌륭한 답변을 인용했습니다.fff 파트 1 살펴보기 - 메인.
답변1
그 이유는 read
내장 함수와 date
명령이 명령줄 인수를 다르게 해석하기 때문입니다.
하지만 가장 먼저 해야 할 일이 있습니다. 두 예제 모두 "${read_flags[@]}"
배열의 경우든 "$read_flags"
스칼라의 경우든 쉘 변수의 역참조 주위에 따옴표를 배치하여 권장 사항을 따랐습니다 . 쉘 변수를 항상 인용하는 것이 권장되는 주된 이유는 다음과 같습니다.필요하지 않음분사. 다음을 고려하세요
- 공백이 포함된 파일이 있고
My favorite songs.txt
이를 디렉토리로 이동하려고 합니다playlists/
. - 파일 이름을 변수에 저장
$fname
하고 호출하면
이mv $fname playlists/
mv
명령은4개매개변수:My
,favorite
및songs.txt
및playlists/
존재하지 않는 파일 3개를 디렉토리로My
이동favorite
하려고 시도합니다 . 분명히 당신이 원하는 것은 아닙니다.songs.txt
playlists/
- 대신에
$fname
다음과 같이 참조를 큰따옴표로 묶으 면
쉘이 공백을 포함하여 전체 문자열을 전달하도록 보장합니다.하나mv "$fname" playlists/
mv
(이름에 공백이 있지만) 이동해야 하는 파일임을 인식하도록 단어를 입력합니다 .
이제 상황을 저장하고 싶습니다.옵션쉘 변수의 매개변수. 때로는 길고 때로는 짧으며 때로는 값이 필요하기 때문에 까다롭습니다. 매개변수를 사용하여 옵션을 지정하는 방법에는 여러 가지가 있습니다. 일반적으로구문 분석 방법은 전적으로 프로그래머에게 달려 있습니다.(바라보다이 Q&A)토론을 해보세요). 따라서 read
Bash의 내장 함수와 명령이 다르게 반응하는 이유는 date
두 함수가 명령줄 인수를 구문 분석하는 방식의 내부 작동 때문일 수 있습니다. 그러나 우리는 조금 추측할 수 있습니다.
- 스칼라 셸 변수에 저장되고
-t 0.05
string 으로 전달되면"$opt_string"
수신자는 이를 공백이 포함된 문자열로 처리합니다(위 참조). - 배열 변수에
-t
및 를 저장 하고 이를 수신자로 전달하면 두 개의 개별 항목, 즉 및 로 처리됩니다 . (1) (2)0.05
"${opt_array[@]}"
-t
0.05
- 많은 프로그램은
getopt()
POSIX 가이드에서 권장하는 대로 GNU C 라이브러리의 함수를 사용하여 명령줄 인수를 구문 분석합니다. - 예를 들어 명령의 경우
getopt()
"짧은" 옵션 형식과 "긴" 옵션 형식을 구분합니다. 모드 옵션date -u
date --utc
date
가치옵션(예:-o
/ )은 일반적으로--option
짧은 옵션 및/또는 긴 옵션의 경우 is로 해석됩니다.getopt
-ovalue
-o value
--option=value
--option value
-t 0.05
다음 과 같이 전달될 때둘사용된 도구의 경우 옵션 이름으로getopt()
뒤의 첫 번째 문자를 사용하고 옵션 값(구문)으로 다음 단어를 사용합니다 .-
따라서 옵션 이름과 옵션 값으로.-o value
read
t
0.05
-t 0.05
다음 과 같이 전달될 때하나단어 다음의 첫 번째 문자는 (역시) 옵션 이름이 되고 나머지 문자열은 옵션 값이 되므로 값은 다음과 같이 해석됩니다.-ovalue
getopt()
-
0.05
선행 공백이 있음.- 이
read
명령은 앞에 공백이 있는 시간 초과 사양을 허용하지 않는 것 같습니다. 사실 전화하면
여기서 값은 명시적으로 선행 공백이 있는 문자열입니다.read -t " 0.05" -srn 1
read
반품이에 대해 불평했습니다.
결론으로date
, 옵션 값과 관련하여 명령은 분명히 더 편안한 방식으로 작성되었으며 -d
값 문자열이 공백으로 시작하는지 여부는 상관하지 않습니다. (분명히) 숫자여야 하는 시간 제한 사양의 경우와는 달리 날짜 사양이 취할 수 있는 값이 매우 다양하기 때문에 이는 놀라운 일이 아닐 것입니다.
(1) @
(대신 *
)을 사용한다는 점에 유의하세요 .큰 차이가 있다여기서 배열 참조를 인용하면 모든 배열 요소가 개별적으로 참조된 것처럼 나타나므로 더 이상 쪼개지지 않고 공백 자체를 포함할 수 있습니다..
(2) 원칙적으로 세 번째 옵션이 있습니다. 즉, -t 0.05
스칼라 변수에 저장하되 $opt_string
다음과 같이 전달하는 것입니다.$opt_string
아니요인용 부호. 이 경우 공백에서 단어 분할을 수행한 다음 다시 수행합니다.둘항목 -t
과 0.05
는 각각 프로그램에 전달됩니다. 그러나 매개변수 값에 보존해야 할 명시적인 공백이 있는 경우가 있기 때문에 이는 권장되는 접근 방식이 아닙니다.
답변2
read_flags="-t 0.05"
read "$read_flags" -srn 1
여기서는 "$read_flags"
큰따옴표로 묶여 있으므로 그렇지 않습니다.분사. 보시다시피 결과는 실행과 동일합니다.
read "-t 0.05" -srn 1
이는 지정된 시간 초과에 선행 공백이 있음을 의미합니다. 이제 숫자를 구문 분석할 때 Bash가 수행하는 모든 작업은 이것이 마음에 들지 않습니다.
추가 공간의 기능은 전적으로 프로그램에 따라 다릅니다. 숫자를 구문 분석할 때 선행 공백을 무시하는 것이 쉬워야 합니다. 표준 strtod()
함수가 이를 수행합니다. 의 경우 date -d
더 복잡한 문자열을 구문 분석해야 하므로 공백에 엄격하지 않다는 것은 놀라운 일이 아닙니다. (단순한 숫자가 아니라 아마도 그런 것일 겁니다 12:00 Jun 4 2019 UTC + 5 days
.) 여기에서 Bash가 왜 그렇게 까다로운지 말하기는 어렵습니다.
이제 파일 이름을 전달하면 선행 공백이 있는 문자열은 선행 공백이 없는 문자열과 다른 파일 이름이 되며 모든 프로그램이 이를 무시하는지 알기가 어렵습니다.
이러한 간단한 값을 사용하십시오(전역 문자가 없고 분할하려는 위치).각실행 공간, 기본값 가정 IFS
) 실제로 배열 대신 문자열을 사용할 수 있습니다.아니요두 개의 서로 다른 매개변수로 분할되도록 인용하세요. 그래서, read $read_flags ...
. 아니면 그냥 설정 timeoutflag=-t0.05
하세요 read "$timeoutflag" ...
. 그러나 read "$timeoutflag"
변수가 비어 있으면 다른 빈 매개변수로 전달되어 오류가 발생하므로 이는 최선의 옵션이 아닙니다.
일반적으로 배열은 임의의 매개변수 목록을 문제 없이 저장하고 사용하는 올바른 방법입니다.
다소 관련됨:변수에 저장된 명령을 어떻게 실행할 수 있나요?