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

Bash로 프로그래밍하는 방법:루프

Bash는 명령줄과 셸 스크립트에서 사용하도록 완벽하게 설계된 강력한 프로그래밍 언어입니다. 3권으로 구성된 Linux 독학 과정을 기반으로 하는 이 3부작 시리즈에서는 Bash를 CLI(명령줄 인터페이스)에서 프로그래밍 언어로 사용하는 방법을 살펴봅니다.

이 시리즈의 첫 번째 기사에서는 변수 및 제어 연산자 사용을 포함하여 Bash를 사용한 몇 가지 간단한 명령줄 프로그래밍을 살펴보았습니다. 두 번째 기사에서는 Bash에서 실행 흐름 제어 논리와 다양한 유형의 셸 확장을 제공하는 파일, 문자열, 숫자 및 기타 논리 연산자 유형을 살펴보았습니다. 이 세 번째(그리고 마지막) 기사에서는 다양한 유형의 반복 작업을 수행하기 위해 루프를 사용하고 이러한 루프를 제어하는 ​​방법을 살펴봅니다.

루프

내가 사용한 모든 프로그래밍 언어에는 반복 작업을 수행할 수 있는 다양한 기능을 제공하는 최소한 몇 가지 유형의 루프 구조가 있습니다. for 루프를 자주 사용하지만 while 및 until 루프도 유용합니다.

for 루프

Bash의 for 구현 내 생각에 명령은 숫자가 아닌 값을 처리할 수 있기 때문에 대부분의 것보다 조금 더 유연합니다. 대조적으로, 예를 들어 표준 C 언어는 for 루프는 숫자 값만 처리할 수 있습니다.

for Bash 버전의 기본 구조 명령은 간단합니다:

for Var in list1 ; do list2 ; done

이는 다음과 같이 번역됩니다. "list1의 각 값에 대해 $Var 해당 값으로 변경한 다음 해당 값을 사용하여 list2의 프로그램 명령문을 수행합니다. list1의 모든 값이 사용되면 완료되므로 루프를 종료하십시오." list1의 값은 단순하고 명시적인 값 문자열이거나 명령 대체의 결과일 수 있습니다(두 번째 연재 기사). 나는 이 구문을 자주 사용합니다.

시도하려면 ~/testdir 여전히 현재 작업 디렉토리(PWD)입니다. 디렉토리를 정리한 다음 for 값의 명시적 목록으로 시작하는 루프. 이 목록은 영숫자 값을 혼합한 것입니다. 그러나 모든 변수가 문자열이며 그렇게 취급될 수 있다는 점을 잊지 마십시오.

[student@studentvm1 testdir]$ rm *
[student@studentvm1 testdir]$ for I in a b c d 1 2 3 4 ; do echo $I ; done
a
b
c
d
1
2
3
4

다음은 더 의미 있는 변수 이름을 가진 좀 더 유용한 버전입니다:

[student@studentvm1 testdir]$ for Dept in "Human Resources" Sales Finance "Information Technology" Engineering Administration Research ; do echo "Department $Dept" ; done 
Department Human Resources
Department Sales
Department Finance
Department Information Technology
Department Engineering
Department Administration
Department Research

디렉토리를 만드십시오(그리고 그렇게 하는 동안 진행 정보를 표시하십시오):

[student@studentvm1 testdir]$ for Dept in "Human Resources" Sales Finance "Information Technology" Engineering Administration Research ; do echo "Working on Department $Dept" ; mkdir "$Dept"  ; done 
Working on Department Human Resources
Working on Department Sales
Working on Department Finance
Working on Department Information Technology
Working on Department Engineering
Working on Department Administration
Working on Department Research
[student@studentvm1 testdir]$ ll
total 28
drwxrwxr-x 2 student student 4096 Apr  8 15:45  Administration
drwxrwxr-x 2 student student 4096 Apr  8 15:45  Engineering
drwxrwxr-x 2 student student 4096 Apr  8 15:45  Finance
drwxrwxr-x 2 student student 4096 Apr  8 15:45 'Human Resources'
drwxrwxr-x 2 student student 4096 Apr  8 15:45 'Information Technology'
drwxrwxr-x 2 student student 4096 Apr  8 15:45  Research
drwxrwxr-x 2 student student 4096 Apr  8 15:45  Sales

$Dept 변수는 mkdir에서 따옴표로 묶어야 합니다. 성명; 그렇지 않으면 두 부분으로 된 부서 이름(예:"정보 기술")이 두 개의 별도 부서로 처리됩니다. 이는 내가 따르고 싶은 모범 사례를 강조합니다. 모든 파일 및 디렉토리 이름은 한 단어여야 합니다. 대부분의 최신 운영 체제는 이름의 공백을 처리할 수 있지만 스크립트 및 CLI 프로그램에서 이러한 특수 사례가 고려되도록 하려면 시스템 관리자가 추가 작업을 수행해야 합니다. (어떤 파일을 갖게 될지 알 수 없기 때문에 성가시더라도 거의 확실히 고려해야 합니다.)

따라서 ~/testdir의 모든 항목을 삭제합니다. —다시—그리고 한 번 더 수행:

[student@studentvm1 testdir]$ rm -rf * ; ll
total 0
[student@studentvm1 testdir]$ for Dept in Human-Resources Sales Finance Information-Technology Engineering Administration Research ; do echo "Working on Department $Dept" ; mkdir "$Dept"  ; done
Working on Department Human-Resources
Working on Department Sales
Working on Department Finance
Working on Department Information-Technology
Working on Department Engineering
Working on Department Administration
Working on Department Research
[student@studentvm1 testdir]$ ll
total 28
drwxrwxr-x 2 student student 4096 Apr  8 15:52 Administration
drwxrwxr-x 2 student student 4096 Apr  8 15:52 Engineering
drwxrwxr-x 2 student student 4096 Apr  8 15:52 Finance
drwxrwxr-x 2 student student 4096 Apr  8 15:52 Human-Resources
drwxrwxr-x 2 student student 4096 Apr  8 15:52 Information-Technology
drwxrwxr-x 2 student student 4096 Apr  8 15:52 Research
drwxrwxr-x 2 student student 4096 Apr  8 15:52 Sales

누군가 특정 Linux 컴퓨터의 모든 RPM 목록과 각 RPM에 대한 간단한 설명을 요청한다고 가정해 보겠습니다. 이것은 내가 노스 캐롤라이나 주에서 일할 때 일어났습니다. 오픈 소스는 그 당시 국가 기관에서 사용하도록 "승인"되지 않았고 저는 데스크탑 컴퓨터에서만 Linux를 사용했기 때문에 PHB(pointy-haired bosses)는 제 컴퓨터에 설치된 각 소프트웨어의 목록을 필요로 했습니다. 예외를 "승인"할 수 있습니다.

어떻게 접근하시겠습니까? 다음은 rpm –qa 명령은 PHB가 원하는 두 가지 항목인 소프트웨어 이름과 간략한 요약을 포함하여 RPM에 대한 완전한 설명을 제공합니다.

한 번에 한 단계씩 최종 결과에 도달합니다. 먼저 모든 RPM을 나열합니다.

[student@studentvm1 testdir]$ rpm -qa 
perl-HTTP-Message-6.18-3.fc29.noarch
perl-IO-1.39-427.fc29.x86_64
perl-Math-Complex-1.59-429.fc29.noarch
lua-5.3.5-2.fc29.x86_64
java-11-openjdk-headless-11.0.ea.28-2.fc29.x86_64
util-linux-2.32.1-1.fc29.x86_64
libreport-fedora-2.9.7-1.fc29.x86_64
rpcbind-1.2.5-0.fc29.x86_64
libsss_sudo-2.0.0-5.fc29.x86_64
libfontenc-1.1.3-9.fc29.x86_64
<snip>

정렬 추가 및 고유 목록을 정렬하고 고유한 것을 인쇄하는 명령(동일한 이름을 가진 일부 RPM이 설치될 수 있기 때문에):

[student@studentvm1 testdir]$ rpm -qa | sort | uniq
a2ps-4.14-39.fc29.x86_64
aajohan-comfortaa-fonts-3.001-3.fc29.noarch
abattis-cantarell-fonts-0.111-1.fc29.noarch
abiword-3.0.2-13.fc29.x86_64
abrt-2.11.0-1.fc29.x86_64
abrt-addon-ccpp-2.11.0-1.fc29.x86_64
abrt-addon-coredump-helper-2.11.0-1.fc29.x86_64
abrt-addon-kerneloops-2.11.0-1.fc29.x86_64
abrt-addon-pstoreoops-2.11.0-1.fc29.x86_64
abrt-addon-vmcore-2.11.0-1.fc29.x86_64
<snip>

이것은 보고 싶은 RPM의 정확한 목록을 제공하므로 이것을 각 RPM의 모든 세부 정보를 인쇄할 루프에 대한 입력 목록으로 사용할 수 있습니다.

[student@studentvm1 testdir]$ for RPM in `rpm -qa | sort | uniq` ; do rpm -qi $RPM ; done

이 코드는 원하는 것보다 훨씬 더 많은 데이터를 생성합니다. 루프가 완료되었음을 유의하십시오. 다음 단계는 PHB가 요청한 정보만 추출하는 것입니다. 따라서 egrep를 추가합니다. ^이름을 선택하는 데 사용되는 명령 또는 ^요약 . 캐럿(^ ) 행의 시작을 지정합니다. 따라서 줄 시작 부분에 이름 또는 요약이 있는 모든 줄이 표시됩니다.

[student@studentvm1 testdir]$ for RPM in `rpm -qa | sort | uniq` ; do rpm -qi $RPM ; done | egrep -i "^Name|^Summary"
Name        : a2ps
Summary     : Converts text and other types of files to PostScript
Name        : aajohan-comfortaa-fonts
Summary     : Modern style true type font
Name        : abattis-cantarell-fonts
Summary     : Humanist sans serif font
Name        : abiword
Summary     : Word processing program
Name        : abrt
Summary     : Automatic bug detection and reporting tool
<snip>

grep을(를) 시도할 수 있습니다. egrep 대신 위의 명령에 있지만 작동하지 않습니다. less를 통해 이 명령의 출력을 파이프할 수도 있습니다. 필터를 사용하여 결과를 탐색합니다. 최종 명령 시퀀스는 다음과 같습니다.

[student@studentvm1 testdir]$ for RPM in `rpm -qa | sort | uniq` ; do rpm -qi $RPM ; done | egrep -i "^Name|^Summary" > RPM-summary.txt

이 명령줄 프로그램은 파이프라인, 리디렉션 및 for 루프 - 모두 한 줄에 있습니다. 작은 CLI 프로그램의 출력을 이메일에서 사용하거나 다른 목적을 위한 입력으로 사용할 수 있는 파일로 리디렉션합니다.

프로그램을 한 번에 한 단계씩 구축하는 이 프로세스를 통해 각 단계의 결과를 보고 예상대로 작동하고 원하는 결과를 제공하는지 확인할 수 있습니다.

이 연습에서 PHB는 1,900개 이상의 개별 RPM 패키지 목록을 받았습니다. 나는 누군가가 그 목록을 읽었는지 심각하게 의심합니다. 하지만 나는 그들이 요구한 대로 정확히 줬고 그에 대해 다른 말을 듣지 못했습니다.

기타 루프

Bash에는 두 가지 유형의 루프 구조가 더 있습니다. while까지 구문과 기능 모두에서 서로 매우 유사한 구조입니다. 이러한 루프 구조의 기본 구문은 간단합니다.

while [ expression ] ; do list ; done

그리고

until [ expression ] ; do list ; done

첫 번째 논리는 다음과 같습니다. "식이 true로 평가되는 동안 프로그램 명령문 목록을 실행합니다. 표현식이 false로 평가되면 루프를 종료합니다." 그리고 두 번째:"표현식이 참으로 평가될 때까지 프로그램 명령문 목록을 실행합니다. 표현식이 참으로 평가되면 루프를 종료합니다."

중 루프

동안 루프는 논리 표현식이 참으로 평가되는 동안 일련의 프로그램 명령문을 실행하는 데 사용됩니다. PWD는 여전히 ~/testdir이어야 합니다. .

while의 가장 단순한 형태 루프는 영원히 실행되는 루프입니다. 다음 형식은 true 문을 사용하여 항상 "true" 반환 코드를 생성합니다. 간단한 "1"을 사용할 수도 있으며 이는 동일하게 작동하지만 다음은 true 명령문의 사용을 보여줍니다.

[student@studentvm1 testdir]$ X=0 ; while [ true ] ; do echo $X ; X=$((X+1)) ; done | head
0
1
2
3
4
5
6
7
8
9
[student@studentvm1 testdir]$

이 CLI 프로그램은 해당 부분을 공부했으므로 이제 더 이해가 될 것입니다. 먼저 $X를 설정합니다. 이전 프로그램이나 CLI 명령에서 남은 값이 있는 경우 0으로 설정합니다. 그러면 논리식 [ true ] 이후 do 사이의 프로그램 명령 목록인 true인 항상 1로 평가됩니다. 완료 영원히 실행되거나 Ctrl+C를 누를 때까지 또는 그렇지 않으면 프로그램에 신호 2를 보냅니다. 이러한 명령어는 $X의 현재 값을 인쇄하는 산술 확장입니다. 그런 다음 1씩 증가시킵니다.

시스템 관리자를 위한 Linux 철학의 신조 중 하나 우아함을 추구하는 것이며, 우아함을 달성하는 한 가지 방법은 단순함입니다. 변수 증가 연산자 ++를 사용하여 이 프로그램을 단순화할 수 있습니다. . 첫 번째 인스턴스에서 변수의 현재 값이 인쇄된 다음 변수가 증가합니다. 이것은 ++ 변수 뒤의 연산자:

[student@studentvm1 ~]$ X=0 ; while [ true ] ; do echo $((X++)) ; done | head
0
1
2
3
4
5
6
7
8
9

지금 삭제 | 머리 프로그램 끝에서 다시 실행하십시오.

이 버전에서 변수는 값이 인쇄되기 전에 증가합니다. 이것은 ++ 변수 앞에 연산자. 차이점이 보이시나요?

[student@studentvm1 ~]$ X=0 ; while [ true ] ; do echo $((++X)) ; done | head
1
2
3
4
5
6
7
8
9

두 개의 명령문을 변수 값을 인쇄하고 해당 값을 증가시키는 단일 명령문으로 줄였습니다. 감소 연산자도 있습니다. -- .

특정 번호에서 루프를 중지하는 방법이 필요합니다. 이를 수행하려면 실제 표현식을 실제 숫자 평가 표현식으로 변경하십시오. 프로그램 루프를 5로 만들고 중지합니다. 아래 예제 코드에서 -le "보다 작거나 같음"에 대한 논리 숫자 연산자입니다. 이는 "$X 5보다 작거나 같으면 루프가 계속됩니다. $X일 때 6으로 증가하면 루프가 종료됩니다."

[student@studentvm1 ~]$ X=0 ; while [ $X -le 5 ] ; do echo $((X++)) ; done 
0
1
2
3
4
5
[student@studentvm1 ~]$

루프까지

~까지 명령은 while과 매우 유사합니다. 명령. 차이점은 논리식이 "true"로 평가될 때까지 계속 반복된다는 것입니다. 이 구조의 가장 간단한 형태를 보십시오:

[student@studentvm1 ~]$ X=0 ; until false  ; do echo $((X++)) ; done | head
0
1
2
3
4
5
6
7
8
9
[student@studentvm1 ~]$

논리적 비교를 사용하여 특정 값으로 계산합니다.

[student@studentvm1 ~]$ X=0 ; until [ $X -eq 5 ]  ; do echo $((X++)) ; done
0
1
2
3
4
[student@studentvm1 ~]$ X=0 ; until [ $X -eq 5 ]  ; do echo $((++X)) ; done
1
2
3
4
5
[student@studentvm1 ~]$

요약

이 시리즈에서는 Bash 명령줄 프로그램 및 셸 스크립트를 빌드하기 위한 여러 강력한 도구를 살펴보았습니다. 그러나 Bash로 할 수 있는 많은 흥미로운 일들에 대해 거의 표면을 긁지 않았습니다. 나머지는 당신에게 달려 있습니다.

Bash 프로그래밍을 배우는 가장 좋은 방법은 직접 해보는 것입니다. 여러 Bash 명령이 필요한 간단한 프로젝트를 찾아 CLI 프로그램을 만드십시오. 시스템 관리자는 CLI 프로그래밍에 적합한 많은 작업을 수행하므로 자동화할 작업을 쉽게 찾을 수 있을 것입니다.

수년 전, 다른 쉘 언어와 Perl에 익숙함에도 불구하고 저는 모든 시스템 관리자 자동화 작업에 Bash를 사용하기로 결정했습니다. 가끔 약간의 검색을 통해 Bash를 사용하여 필요한 모든 것을 달성할 수 있다는 것을 발견했습니다.