Computer >> 컴퓨터 >  >> 프로그램 작성 >> BASH 프로그래밍

당신은 Bash를 모른다:Bash 배열 소개

소프트웨어 엔지니어가 정기적으로 개발의 여러 측면에서 명령줄을 사용하지만 어레이는 명령줄에서 가장 모호한 기능 중 하나일 가능성이 높습니다(비록 정규식 연산자 =~만큼 모호합니다. ). 그러나 모호함과 의심스러운 구문을 제외하고 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의 놀라운 세계를 탐험할 수 있습니다.