소프트웨어 엔지니어가 정기적으로 개발의 여러 측면에서 명령줄을 사용하지만 어레이는 명령줄에서 가장 모호한 기능 중 하나일 가능성이 높습니다(비록 정규식 연산자 =~
만큼 모호합니다. ). 그러나 모호함과 의심스러운 구문을 제외하고 Bash 배열은 매우 강력할 수 있습니다.
잠깐만, 그런데 왜요?
Bash에 대해 작성하는 것은 이상한 구문에 초점을 맞춘 매뉴얼로 기사를 전개하는 것이 매우 쉽기 때문에 어려운 일입니다. 그러나 이 문서의 목적은 RTFM을 피하는 것입니다.
실제(실제로 유용한) 예
이를 위해 실제 시나리오와 Bash가 어떻게 도움이 되는지 살펴보겠습니다. 내부 데이터 파이프라인의 런타임을 평가하고 최적화하기 위해 회사에서 새로운 노력을 주도하고 있습니다. 첫 번째 단계로 매개변수 스윕을 수행하여 파이프라인이 스레드를 얼마나 잘 활용하는지 평가하려고 합니다. 단순함을 위해 파이프라인을 컴파일된 C++ 블랙박스로 취급합니다. 여기서 조정할 수 있는 유일한 매개변수는 데이터 처리를 위해 예약된 스레드 수입니다. ./pipeline --threads 4
.
기본 사항
가장 먼저 할 일은 --threads
값을 포함하는 배열을 정의하는 것입니다. 테스트하려는 매개변수:
allThreads=(1 2 4 8 16 32 64 128)
이 예에서 모든 요소는 숫자이지만 그럴 필요는 없습니다. Bash의 배열에는 숫자와 문자열이 모두 포함될 수 있습니다(예: myArray=(1 2 "three" 4 "five")
유효한 표현입니다. 다른 Bash 변수와 마찬가지로 등호 주위에 공백을 두지 마십시오. 그렇지 않으면 Bash는 변수 이름을 실행할 프로그램으로 취급하고 =
첫 번째 매개변수로!
이제 배열을 초기화했으므로 배열의 몇 가지 요소를 검색해 보겠습니다. echo $allThreads
첫 번째 요소만 출력합니다.
그 이유를 이해하기 위해 한 걸음 물러나서 Bash에서 일반적으로 변수를 출력하는 방법을 다시 살펴보겠습니다. 다음 시나리오를 고려하십시오.
type="article"
echo "Found 42 $type"
변수 $type
는 단수 명사로 주어지며 s
를 추가하려고 합니다. 우리 문장의 끝에. 단순히 s
를 추가할 수는 없습니다. $type
으로 다른 변수로 바뀌므로 $types
. 그리고 echo "Found 42 "$type"s"
와 같은 코드 왜곡을 활용할 수 있지만 , 이 문제를 해결하는 가장 좋은 방법은 중괄호를 사용하는 것입니다. echo "Found 42 ${type}s"
, 변수 이름이 시작하고 끝나는 위치를 Bash에 알릴 수 있습니다(흥미롭게도 이것은 템플릿 리터럴에 변수와 표현식을 삽입하기 위해 JavaScript/ES6에서 사용되는 것과 동일한 구문입니다).
결과적으로 Bash 변수에는 일반적으로 중괄호가 필요하지 않지만 배열에는 필요합니다. 그러면 액세스할 색인을 지정할 수 있습니다(예: echo ${allThreads[1]}
). 배열의 두 번째 요소를 반환합니다. 대괄호를 포함하지 않음(예:echo $allThreads[1]
) , Bash가 [1]
을(를) 처리하도록 합니다. 문자열로 만들어서 그대로 출력하세요.
예, Bash 배열에는 이상한 구문이 있지만 최소한 다른 언어와 달리 인덱스가 0입니다(R
를 보고 있습니다. ).
배열 반복
위의 예에서 배열에 정수 인덱스를 사용했지만 그렇지 않은 두 가지 경우를 고려해 보겠습니다. 첫째, $i
- 배열의 요소, 여기서 $i
관심 인덱스를 포함하는 변수인 경우 다음을 사용하여 해당 요소를 검색할 수 있습니다. echo ${allThreads[$i]}
. 둘째, 배열의 모든 요소를 출력하기 위해 숫자 인덱스를 @
로 바꿉니다. 기호(@
를 생각할 수 있습니다. all
의 약자로 ):echo ${allThreads[@]}
.
배열 요소 반복
이를 염두에 두고 $allThreads
를 반복해 보겠습니다. --threads
의 각 값에 대해 파이프라인을 시작합니다. :
for t in ${allThreads[@]}; do
./pipeline --threads $t
done
배열 인덱스 반복
다음으로 약간 다른 접근 방식을 고려해 보겠습니다. 배열 요소를 반복하는 대신 , 배열 인덱스를 반복할 수 있습니다. :
for i in ${!allThreads[@]}; do
./pipeline --threads ${allThreads[$i]}
done
이를 분해해 보겠습니다. 위에서 보았듯이 ${allThreads[@]}
배열의 모든 요소를 나타냅니다. 느낌표를 추가하여 ${!allThreads[@]}
만들기 모든 배열 인덱스(이 경우 0에서 7까지)의 목록을 반환합니다. 즉, for
루프가 모든 인덱스 $i
를 반복합니다. $i
읽기 - $allThreads
의 요소 --threads
의 값을 설정하려면 매개변수.
이게 훨씬 눈에 거슬리는데 왜 굳이 굳이 먼저 소개를 해야 하는지 의아해 하실 수도 있습니다. 이는 루프 내에서 인덱스와 값을 모두 알아야 하는 경우가 있기 때문입니다. 예를 들어 배열의 첫 번째 요소를 무시하려는 경우 인덱스를 사용하면 루프 내에서 증가하는 추가 변수를 생성하지 않아도 됩니다. .
배열 채우기
지금까지 각 --threads
에 대한 파이프라인을 시작할 수 있었습니다. 관심. 이제 파이프라인에 대한 출력이 몇 초 단위의 런타임이라고 가정해 보겠습니다. 각 반복에서 해당 출력을 캡처하고 마지막에 다양한 조작을 수행할 수 있도록 다른 배열에 저장하고 싶습니다.
유용한 구문
그러나 코드를 살펴보기 전에 구문을 더 소개해야 합니다. 먼저 Bash 명령의 출력을 검색할 수 있어야 합니다. 이렇게 하려면 다음 구문을 사용하십시오. output=$( ./my_script.sh )
, 명령의 출력을 $output
변수에 저장합니다. .
필요한 구문의 두 번째 비트는 방금 검색한 값을 배열에 추가하는 방법입니다. 그렇게 하는 구문은 친숙해 보일 것입니다.
myArray+=( "newElement1" "newElement2" )
매개변수 스윕
모든 것을 종합하면 다음은 매개변수 스윕을 시작하기 위한 스크립트입니다.
allThreads=(1 2 4 8 16 32 64 128)
allRuntimes=()
for t in ${allThreads[@]}; do
runtime=$(./pipeline --threads $t)
allRuntimes+=( $runtime )
done
그리고 짜잔!
또 뭐가 있나요?
이 기사에서는 매개변수 스윕에 배열을 사용하는 시나리오를 다뤘습니다. 하지만 Bash 배열을 사용해야 하는 더 많은 이유가 있음을 약속합니다. 여기에 두 가지 예가 더 있습니다.
로그 알림
이 시나리오에서 앱은 각각 고유한 로그 파일이 있는 모듈로 나뉩니다. 특정 모듈에 문제의 징후가 있을 때 적절한 사람에게 이메일을 보내기 위해 cron 작업 스크립트를 작성할 수 있습니다.
# List of logs and who should be notified of issues
logPaths=("api.log" "auth.log" "jenkins.log" "data.log")
logEmails=("jay@email" "emma@email" "jon@email" "sophia@email")
# Look for signs of trouble in each log
for i in ${!logPaths[@]};
do
log=${logPaths[$i]}
stakeholder=${logEmails[$i]}
numErrors=$( tail -n 100 "$log" | grep "ERROR" | wc -l )
# Warn stakeholders if recently saw > 5 errors
if [[ "$numErrors" -gt 5 ]];
then
emailRecipient="$stakeholder"
emailSubject="WARNING: ${log} showing unusual levels of errors"
emailBody="${numErrors} errors found in log ${log}"
echo "$emailBody" | mailx -s "$emailSubject" "$emailRecipient"
fi
done
API 쿼리
어떤 사용자가 귀하의 Medium 게시물에 가장 많이 댓글을 달았는지에 대한 분석을 생성하고 싶다고 가정해 보겠습니다. 데이터베이스에 직접 액세스할 수 없기 때문에 SQL은 의심할 여지가 없지만 API를 사용할 수 있습니다!
API 인증 및 토큰에 대한 긴 논의를 피하기 위해 대신 공개 API 테스트 서비스인 JSONPlaceholder를 엔드포인트로 사용합니다. 각 게시물을 쿼리하고 댓글을 단 모든 사람의 이메일을 검색하면 해당 이메일을 결과 배열에 추가할 수 있습니다.
endpoint="https://jsonplaceholder.typicode.com/comments"
allEmails=()
# Query first 10 posts
for postId in {1..10};
do
# Make API call to fetch emails of this posts's commenters
response=$(curl "${endpoint}?postId=${postId}")
# Use jq to parse the JSON response into an array
allEmails+=( $( jq '.[].email' <<< "$response" ) )
done
여기에서 jq
를 사용하고 있습니다. 명령줄에서 JSON을 구문 분석하는 도구입니다. jq
구문 이 문서의 범위를 벗어나지만 자세히 살펴보시기 바랍니다.
상상할 수 있듯이 Bash 배열을 사용하는 것이 도움이 될 수 있는 다른 시나리오는 무수히 많으며 이 기사에서 설명하는 예제가 생각할 거리를 제공하기를 바랍니다. 자신의 작업에서 공유할 다른 예가 있으면 아래에 의견을 남겨주세요.
하지만 잠깐만, 더 있습니다!
이 기사에서 꽤 많은 배열 구문을 다루었으므로 다루지 않은 몇 가지 고급 트릭과 함께 다룬 내용을 요약하면 다음과 같습니다.
구문 | 결과 |
---|---|
arr=() | 빈 배열 생성 |
arr=(1 2 3) | 배열 초기화 |
${arr[2]} | 세 번째 요소 검색 |
${arr[@]} | 모든 요소 검색 |
${!arr[@]} | 배열 인덱스 검색 |
${#arr[@]} | 배열 크기 계산 |
arr[0]=3 | 첫 번째 요소 덮어쓰기 |
arr+=(4) | 값 추가 |
str=$(ls) | ls 저장 문자열로 출력 |
arr=( $(ls) ) | ls 저장 파일 배열로 출력 |
${arr[@]:s:n} | starting at index s n개의 요소 검색 |
마지막으로 한 가지 생각
우리가 발견한 바와 같이, Bash 배열은 확실히 이상한 구문을 가지고 있지만, 이 기사가 당신이 그것들이 매우 강력하다는 것을 확신시키기를 바랍니다. 구문에 익숙해지면 Bash 배열을 자주 사용하게 될 것입니다.
Bash 또는 Python?
Python과 같은 다른 스크립팅 언어 대신 Bash 배열을 언제 사용해야 하나요?
나에게 모든 것은 종속성으로 귀결됩니다. 명령줄 도구에 대한 호출만 사용하여 당면한 문제를 해결할 수 있다면 Bash를 사용하는 것이 좋습니다. 그러나 스크립트가 더 큰 Python 프로젝트의 일부인 경우에는 Python을 사용하는 것이 좋습니다.
예를 들어, 우리는 매개변수 스윕을 구현하기 위해 Python으로 전환할 수 있었지만 결국 Bash 주위에 래퍼를 작성하게 되었을 것입니다.
import subprocess
all_threads = [1, 2, 4, 8, 16, 32, 64, 128]
all_runtimes = []
# Launch pipeline on each number of threads
for t in all_threads:
cmd = './pipeline --threads {}'.format(t)
# Use the subprocess module to fetch the return output
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
output = p.communicate()[0]
all_runtimes.append(output)
이 예에서는 명령줄을 우회하지 않으므로 Bash를 직접 사용하는 것이 좋습니다.
뻔뻔한 플러그를 위한 시간
이 기사는 내가 라이브 코딩 워크샵 You Don't Know Bash를 발표한 OSCON에서 했던 강연을 기반으로 합니다. . 슬라이드도, 클릭 도구도 없습니다. 나와 청중만 명령줄에 입력하여 Bash의 놀라운 세계를 탐험할 수 있습니다.