Jekyll2022-02-14T07:39:23+00:00https://www.bangseongbeom.com/feed.xml방성범 블로그개발자 방성범의 기술 블로그방성범 (Bang Seongbeom)셀레늄 IDE 가이드2020-06-14T00:00:00+00:002020-06-14T00:00:00+00:00https://www.bangseongbeom.com/selenium-ide-guide<p><a href="https://www.selenium.dev/selenium-ide/">셀레늄 IDE</a>(Selenium IDE)는 사용자가 웹 브라우저에서 수행한 동작을 기록하고, 이를 다시 재현합니다.</p>
<p><img src="/assets/2020-06-14-selenium-ide-guide/duckduckgo.gif" alt="" /></p>
<h2 id="설치">설치</h2>
<p>크롬 확장 기능 또는 파이어폭스 플러그인으로 셀레늄 IDE를 사용할 수 있습니다. <a href="https://www.selenium.dev/selenium-ide/">셀레늄 IDE 공식 홈페이지</a>에서 플러그인을 설치하세요.</p>
<div class="note">
<p><span>참고:</span></p>
<p>이 글에서 소개하는 셀레늄 IDE는 버전 3입니다. 버전 4는 현재 개발 중이며, 브라우저 없이 독립 실행 가능하도록 <a href="https://www.electronjs.org/">일렉트론</a>(Electron) 기반으로 구현되고 있습니다<sup id="fnref:electron" role="doc-noteref"><a href="#fn:electron" class="footnote" rel="footnote">1</a></sup>.</p>
</div>
<h2 id="기록">기록</h2>
<p><img src="/assets/2020-06-14-selenium-ide-guide/record-a-new-test-in-a-new-project.png" alt="" /></p>
<p>셀레늄 IDE를 실행한 뒤 곧바로 ‘Record a new test in a new project’를 클릭해 기록을 시작할 수 있습니다.</p>
<p><img src="/assets/2020-06-14-selenium-ide-guide/start-recording.png" alt="" /></p>
<p>또는 ‘Start recording’을 눌러 기록을 시작할 수도 있습니다.</p>
<p>기록을 시작하기 전에 먼저 <strong>프로젝트 이름</strong>과 <strong>베이스 URL</strong>을 설정해야 합니다.</p>
<h3 id="용어-설명-프로젝트">용어 설명: 프로젝트</h3>
<p><img src="/assets/2020-06-14-selenium-ide-guide/project-name-2.png" alt="" /></p>
<p>셀레늄 IDE는 프로젝트 단위로 파일을 저장하거나 불러옵니다. 이때 사용할 프로젝트 이름을 미리 설정해야 합니다.</p>
<p>프로젝트 파일은 <code class="language-plaintext highlighter-rouge">.side</code> 확장자로 저장됩니다. <code class="language-plaintext highlighter-rouge">.side</code>는 내부적으로 <a href="https://www.json.org/">JSON</a> 형태를 가집니다.</p>
<h3 id="용어-설명-베이스-url">용어 설명: 베이스 URL</h3>
<p><img src="/assets/2020-06-14-selenium-ide-guide/base-url-2.png" alt="" /></p>
<p>베이스 URL(base URL)은 기록을 처음 실행할 때 시작할 웹 사이트의 URL입니다.</p>
<p>하나의 프로젝트 당 하나의 베이스 URL을 가질 수 있습니다. 각 테스트 별로 베이스 URL을 지정할 수는 없습니다.</p>
<h3 id="기록을-위한-새-창">기록을 위한 새 창</h3>
<p><img src="/assets/2020-06-14-selenium-ide-guide/selenium-ide-is-recording.png" alt="" /></p>
<p>기록이 시작되면 새로운 웹 브라우저 창이 열립니다. 기록 중이라는 메시지가 브라우저 오른쪽 아래에 나옵니다. 이제 이 브라우저에서 기록하고자 하는 동작을 수행하면 됩니다.</p>
<p>사용자가 행하는 동작은 <strong>명령어</strong>로 기록됩니다.</p>
<h3 id="용어-설명-명령어">용어 설명: 명령어</h3>
<p>명령어(command)는 클릭하기, 타이핑하기, 브라우저 창 크기 조절하기와 같이 브라우저에서 행하는 동작을 의미합니다. 기록되는 동안 웹 브라우저에 대한 조작이 명령어로 만들어집니다.</p>
<h2 id="실행">실행</h2>
<p><img src="/assets/2020-06-14-selenium-ide-guide/run-current-test.gif" alt="" /></p>
<p>‘Run current test’ 버튼을 눌러 기록한 명령어들을 실행합니다. 새로운 웹 브라우저가 열리며 명령어에 따라 순차적으로 동작합니다.</p>
<h2 id="저장">저장</h2>
<p>‘Save project’ 버튼을 눌러 프로젝트를 저장합니다.</p>
<div class="note">
<p><span>참고:</span></p>
<p>웹 브라우저 환경에서는 그 특성 상 특정 파일의 경로에 직접적으로 파일을 쓰거나 읽을 수 없습니다. 반드시 ‘다른 이름으로 저장’이나 ‘열기’를 통해 사용자가 직접 읽거나 쓸 파일을 선택해야 합니다. 이러한 이유로 인해 셀레늄 IDE는 프로젝트를 저장할 때마다 매번 저장할 위치를 지정해야 하는 번거로움이 있습니다<sup id="fnref:why-location-not-remembered" role="doc-noteref"><a href="#fn:why-location-not-remembered" class="footnote" rel="footnote">2</a></sup>.</p>
</div>
<h3 id="저장되지-않음-표시">저장되지 않음 표시</h3>
<p><img src="/assets/2020-06-14-selenium-ide-guide/not-saved.png" alt="" /></p>
<p>프로젝트, 테스트, 테스트 스위트 이름 뒤에 ‘*‘이 붙어있을 때가 있습니다. 이는 변경된 내용이 아직 저장되지 않았음을 의미합니다.</p>
<h2 id="테스트테스트-스위트-목록">테스트/테스트 스위트 목록</h2>
<p><img src="/assets/2020-06-14-selenium-ide-guide/test-area.png" alt="" /></p>
<p>아래 화살표 버튼을 눌러 테스트와 테스트 스위트 목록을 전환할 수 있습니다. (이외에도 ‘Executing’이라는 목록이 있습니다. 이 목록은 현재 실행 중인 테스트를 보여줍니다.)</p>
<p><strong>테스트에 관한 설정은 테스트 목록에서, 테스트 스위트에 관한 설정은 테스트 스위트 목록에서만 가능합니다.</strong> 테스트 스위트 목록인 상태에서는 테스트를 생성할 수 없습니다. 그 반대도 마찬가지입니다.</p>
<h3 id="용어-설명-테스트">용어 설명: 테스트</h3>
<p><img src="/assets/2020-06-14-selenium-ide-guide/tests.png" alt="" /></p>
<p>테스트(test)는 여러 <strong>명령어</strong>들을 순서대로 나열해놓은 것입니다. 하나의 <strong>프로젝트</strong>에 여러 테스트가 존재할 수 있습니다.</p>
<div class="note">
<p><span>참고:</span></p>
<p>이것이 테스트라고 불리는 이유는, 이 용어가 <a href="https://junit.org/junit5/docs/current/user-guide/#writing-tests-classes-and-methods">J유닛의 테스트</a>와 관련이 있기 때문으로 보입니다. 셀레늄 IDE는 작성한 테스트를 J유닛의 테스트로 내보낼 수 있는 기능을 제공하는데요(하단 참조), 이때 셀레늄 IDE의 테스트 하나는 J유닛의 테스트 하나로 변환됩니다.</p>
</div>
<h3 id="용어-설명-테스트-스위트">용어 설명: 테스트 스위트</h3>
<p><img src="/assets/2020-06-14-selenium-ide-guide/test-suites.png" alt="" /></p>
<p>테스트 스위트(test suite)는 여러 테스트의 집합입니다. 테스트 스위트를 이용해 테스트를 목적에 따라 분류할 수 있습니다.</p>
<p>테스트가 새로 만들어지면 기본적으로 ‘Default Suite’에 속합니다.</p>
<p><strong>주의:</strong> 하나의 테스트는 여러 테스트 스위트에 속할 수도 있고, 그 어떤 테스트 스위트에도 속하지 않을 수도 있습니다.</p>
<div class="note">
<p><span>참고:</span></p>
<p>앞서 언급한 테스트의 사례처럼, 이것을 테스트 스위트라 부르는 이유 역시 <a href="https://junit.org/junit5/docs/current/user-guide/#running-tests-junit-platform-runner-test-suite">J유닛의 테스트 스위트</a>에서 따왔습니다. 셀레늄 IDE의 테스트 스위트 하나는 J유닛의 테스트 스위트 하나로 변환됩니다.</p>
</div>
<h3 id="테스트-테스트-스위트-추가">테스트, 테스트 스위트 추가</h3>
<p><img src="/assets/2020-06-14-selenium-ide-guide/add-new-test.png" alt="" /></p>
<p>테스트 목록인 상태에서 ‘+’ 버튼을 눌러 테스트를 추가할 수 있습니다. 테스트 스위트 역시 마찬가지입니다.</p>
<p>주의할 점은 테스트 추가는 테스트 목록에서만, 테스트 스위트 추가는 테스트 스위트 목록에서만 가능하다는 것입니다. <strong>테스트 목록인 상태에서 테스트 스위트를 추가할 수는 없습니다.</strong></p>
<h3 id="검색">검색</h3>
<p><img src="/assets/2020-06-14-selenium-ide-guide/search-tests.png" alt="" /></p>
<p>검색어를 입력해 테스트를 찾을 수 있습니다.</p>
<h3 id="이름-바꾸기-복제-삭제">이름 바꾸기, 복제, 삭제</h3>
<p><img src="/assets/2020-06-14-selenium-ide-guide/test-options-1.png" alt="" /></p>
<p><img src="/assets/2020-06-14-selenium-ide-guide/test-options-2.png" alt="" /></p>
<p>‘⋮’ 버튼을 눌러 이름을 바꾸거나 테스트 또는 테스트 스위트를 복제, 삭제할 수 있습니다.</p>
<h3 id="테스트-스위트에-테스트-포함시키기">테스트 스위트에 테스트 포함시키기</h3>
<p>테스트 스위트 목록인 상태에서 ‘⋮’ 버튼을 클릭한 뒤, ‘Add tests’를 눌러 테스트 스위트에 테스트를 포함시킬 수 있습니다.</p>
<div class="note">
<p><span>참고:</span></p>
<p>이 작업이 테스트를 <strong>생성</strong>하는 것은 아닙니다. 테스트를 생성하기 위해서는 반드시 <strong>테스트 목록인 상태</strong>에서 ‘+’ 버튼을 클릭해 테스트를 생성해야 합니다.</p>
</div>
<h3 id="내보내기">내보내기</h3>
<p><img src="/assets/2020-06-14-selenium-ide-guide/export-1.png" alt="" /></p>
<p><img src="/assets/2020-06-14-selenium-ide-guide/export-2.png" alt="" /></p>
<p><img src="/assets/2020-06-14-selenium-ide-guide/export-3.png" alt="" /></p>
<p>내보내기는 특정 테스트 혹은 테스트 스위트를 프로그래밍 언어로 변환할 수 있는 기능입니다. 자바의 J유닛(JUnit), 파이썬의 파이테스트(pytest)와 같은 테스트 프레임워크 기반의 코드가 생성됩니다.</p>
<p>현재 다음과 같은 언어 및 테스트 프레임워크를 지원합니다<sup id="fnref:supported-exports" role="doc-noteref"><a href="#fn:supported-exports" class="footnote" rel="footnote">3</a></sup>:</p>
<ul>
<li>C# N유닛(NUnit)</li>
<li>C# x유닛(xUnit)</li>
<li>자바 J유닛(JUnit)</li>
<li>자바스크립트 모카(Mocha)</li>
<li>파이썬 파이테스트(pytest)</li>
<li>루비 R스펙(RSpec)</li>
</ul>
<p>새로운 언어나 테스트 프레임워크가 필요하다면 <a href="https://www.selenium.dev/selenium-ide/docs/en/introduction/code-export#how-to-contribute">셀레늄 IDE에 기여</a>해보세요.</p>
<h2 id="실행-및-관련-설정">실행 및 관련 설정</h2>
<p><img src="/assets/2020-06-14-selenium-ide-guide/run-area.png" alt="" /></p>
<div class="note">
<p><span>참고:</span></p>
<p>여기서는 GUI로 실행하는 방법에 대해 다룹니다. CLI로 실행하는 방법은 이 글의 후반부에서 설명합니다.</p>
</div>
<h3 id="전체-실행">전체 실행</h3>
<p><img src="/assets/2020-06-14-selenium-ide-guide/run-all-tests.png" alt="" /></p>
<p>좌측에 테스트 목록을 보이게 한 상태에서 ‘Run all tests’ 버튼을 눌러 모든 테스트를 실행합니다.</p>
<p>좌측에 테스트 스위트 목록을 보이게 한 상태에서는 버튼(‘Run all tests in suite’)의 동작이 테스트 스위트 내의 모든 테스트를 실행하는 것으로 바뀝니다.</p>
<h3 id="현재-테스트-실행">현재 테스트 실행</h3>
<p><img src="/assets/2020-06-14-selenium-ide-guide/run-current-test.png" alt="" /></p>
<p>‘Run current test’ 버튼을 눌러 현재 활성화한 테스트를 실행할 수 있습니다.</p>
<h3 id="한-줄씩-실행">한 줄씩 실행</h3>
<p><img src="/assets/2020-06-14-selenium-ide-guide/step-over-current-command.png" alt="" /></p>
<p>‘Step over current command’ 버튼을 눌러 테스트를 한 줄씩 실행할 수 있습니다.</p>
<h3 id="테스트-실행-속도-조절">테스트 실행 속도 조절</h3>
<p><img src="/assets/2020-06-14-selenium-ide-guide/test-execution-speed.png" alt="" /></p>
<p>‘Test execution speed’ 버튼을 눌러 테스트의 실행 속도를 조절할 수 있습니다.</p>
<h2 id="명령어">명령어</h2>
<p><img src="/assets/2020-06-14-selenium-ide-guide/command-area.png" alt="" /></p>
<p>셀레늄 IDE는 조건문이나 반복문 같이 프로그래밍 언어에서 지원할 법한 명령어도 제공합니다. 이러한 명령어는 웹 브라우저의 사용을 기록하는 방식으로 만들 수는 없고, 대신 직접 명령어를 추가해야 합니다.</p>
<div class="note">
<p><span>참고:</span></p>
<p><a href="https://www.selenium.dev/selenium-ide/docs/en/api/commands">Commands - Selenium IDE</a>에서 셀레늄 IDE가 제공하는 모든 명령어를 확인하세요.</p>
</div>
<h3 id="중단점">중단점</h3>
<p><img src="/assets/2020-06-14-selenium-ide-guide/breakpoints.png" alt="" /></p>
<p>셀레늄 IDE는 테스트 실행 도중 특정 명령어에서 일시정지할 수 있는 기능을 제공하는데, 이를 중단점(breakpoint)이라 합니다.</p>
<p>명령어의 <strong>왼쪽 번호</strong>를 클릭해 중단점을 지정합니다.</p>
<p>테스트 실행 중 중단점을 만나면 테스트가 일시정지됩니다. ‘Run current test’를 눌러 테스트를 계속 진행할 수 있습니다.</p>
<h3 id="중단점-비활성화">중단점 비활성화</h3>
<p><img src="/assets/2020-06-14-selenium-ide-guide/disable-breakpoints.png" alt="" /></p>
<p>‘Disable breakpoints’는 모든 중단점을 비활성화합니다. 중단점을 만나도 테스트가 일시정지되지 않게 됩니다.</p>
<h3 id="예외-발생-시-정지">예외 발생 시 정지</h3>
<p><img src="/assets/2020-06-14-selenium-ide-guide/pause-on-exceptions.png" alt="" /></p>
<p>웹 사이트의 변경으로 인해 클릭해야 할 버튼이 없어진다거나 하는 문제가 생길 수 있습니다. 이를 가리켜 예외라 합니다.</p>
<p>셀레늄 IDE는 일반적으로 예외가 발생하면 테스트를 종료합니다. ‘Pause on exceptions’ 버튼을 클릭하면 예외가 발생했을 때 테스트를 종료하는 것 대신 마치 중단점처럼 테스트를 일시정지합니다.</p>
<h3 id="자바스크립트-실행">자바스크립트 실행</h3>
<p><a href="https://www.selenium.dev/selenium-ide/docs/en/api/commands#execute-script"><code class="language-plaintext highlighter-rouge">execute script</code></a>, <a href="https://www.selenium.dev/selenium-ide/docs/en/api/commands#execute-async-script"><code class="language-plaintext highlighter-rouge">execute async script</code></a> 명령어를 통해 자바스크립트 코드를 실행할 수 있습니다.</p>
<h3 id="검증">검증</h3>
<p><a href="https://www.selenium.dev/selenium-ide/docs/en/api/commands#assert"><code class="language-plaintext highlighter-rouge">assert</code></a>, <a href="https://www.selenium.dev/selenium-ide/docs/en/api/commands#assert-element-present"><code class="language-plaintext highlighter-rouge">assert element present</code></a>, <a href="https://www.selenium.dev/selenium-ide/docs/en/api/commands#assert-checked"><code class="language-plaintext highlighter-rouge">assert checked</code></a> 등의 명령어를 통해 테스트하려 하는 대상이 제대로 존재하는지 검증할 수 있습니다. 검증에 실패하면 테스트를 종료합니다.</p>
<p>검증 실패 시 테스트를 종료하고 싶지 않다면, <a href="https://www.selenium.dev/selenium-ide/docs/en/api/commands#verify"><code class="language-plaintext highlighter-rouge">verify</code></a>, <a href="https://www.selenium.dev/selenium-ide/docs/en/api/commands#verify-element-present"><code class="language-plaintext highlighter-rouge">verify element present</code></a>, <a href="https://www.selenium.dev/selenium-ide/docs/en/api/commands#verify-checked"><code class="language-plaintext highlighter-rouge">verify checked</code></a> 등의 명령어를 사용하세요.</p>
<h3 id="변수">변수</h3>
<p><a href="https://www.selenium.dev/selenium-ide/docs/en/api/commands#store"><code class="language-plaintext highlighter-rouge">store</code></a>, <a href="https://www.selenium.dev/selenium-ide/docs/en/api/commands#store-attribute"><code class="language-plaintext highlighter-rouge">store attribute</code></a> 등의 명령어를 통해 테스트 도중 필요한 값을 변수에 보관할 수 있습니다.</p>
<h3 id="제어문">제어문</h3>
<p>스프링 IDE는 <a href="https://www.selenium.dev/selenium-ide/docs/en/api/commands#if"><code class="language-plaintext highlighter-rouge">if</code></a>, <a href="https://www.selenium.dev/selenium-ide/docs/en/api/commands#while"><code class="language-plaintext highlighter-rouge">while</code></a>, <a href="https://www.selenium.dev/selenium-ide/docs/en/api/commands#for-each"><code class="language-plaintext highlighter-rouge">for each</code></a>와 같이 프로그래밍 언어에서 지원하는 제어문을 제공합니다.</p>
<p>각종 제어문에서는 조건을 검사하기 위해 자바스크립트 표현식을 요구합니다<sup id="fnref:javascript-control-flow" role="doc-noteref"><a href="#fn:javascript-control-flow" class="footnote" rel="footnote">4</a></sup>.</p>
<h3 id="다른-테스트-실행-명령">다른 테스트 실행 명령</h3>
<p><a href="https://www.selenium.dev/selenium-ide/docs/en/api/commands#run"><code class="language-plaintext highlighter-rouge">run</code></a> 명령어를 통해 다른 테스트를 실행할 수 있습니다.</p>
<h3 id="주석">주석</h3>
<p><img src="/assets/2020-06-14-selenium-ide-guide/enable-disable-command.png" alt="" /></p>
<p>‘Enable/Disable this command’ 버튼을 누르거나 명령어 이름 앞에 <code class="language-plaintext highlighter-rouge">//</code>를 붙여 명령어를 비활성화할 수 있습니다. 비활성화한 명령어는 실행되지 않습니다.</p>
<h3 id="타깃">타깃</h3>
<p><img src="/assets/2020-06-14-selenium-ide-guide/targets.png" alt="" /></p>
<p>타깃(target)은 명령어가 동작하는데 필요한 <code class="language-plaintext highlighter-rouge"><button></code>이나 <code class="language-plaintext highlighter-rouge"><input></code>같은 HTML 엘리먼트를 의미합니다.</p>
<h3 id="다중-타깃">다중 타깃</h3>
<p><img src="/assets/2020-06-14-selenium-ide-guide/multiple-targets.png" alt="" /></p>
<p>웹 사이트는 시간이 지남에 따라 내용이 달라지는 경우가 많습니다. <a href="https://news.google.com/">구글 뉴스</a>의 경우, 실시간 정보를 반영하기 위해 짧은 시간을 주기로 계속 그 내용이 바뀝니다. 이외에도 다양한 이유로 내용이 변화합니다.</p>
<p><strong>사람</strong>은 이러한 웹 사이트의 변화 속에서도 버튼의 텍스트, 위치, 색상과 같은 다양한 단서를 종합하여 이전과 동일한 버튼임을 추리할 수 있습니다. 웹 사이트가 완전히 개편되지만 않는다면 말입니다.</p>
<p>반대로 <strong>컴퓨터 프로그램</strong>은 버튼의 텍스트, 위치, 색상과 같은 다양한 단서를 <strong>개별적으로</strong> 사용할 수는 있으나, 사람과 달리 <strong>종합적으로</strong> 추리하도록 하기는 어렵습니다. 추리라는 과정을 컴퓨터가 이해할 수 있는 알고리즘으로 설명하기가 까다롭기 때문입니다.</p>
<p>셀레늄 IDE 역시 마찬가지입니다. 셀레늄 IDE는 주로 <a href="https://developer.mozilla.org/ko/docs/Web/CSS/CSS_Selectors">CSS 선택자</a>나 <a href="https://developer.mozilla.org/en-US/docs/Web/XPath">X패스</a>(XPath)같은 위치 기반 단서를 이용해 같은 버튼임을 파악합니다. 물론 이 방식은 단서를 종합적으로 고려한 것이 아니라 단점이 존재합니다. 위치 기반 단서이기 때문에 웹 사이트의 디자인과 상관 없이 동일한 버튼을 타깃으로 선택할 수 있지만, 버튼의 위치 자체가 바뀌는 경우에는 무척 취약합니다. 이를 보완하기 위해 버튼의 텍스트를 이용해 같은 버튼임을 인지하기도 합니다. 어찌되었든 종합적인 추론 기능은 탑재하고 있지 않습니다.</p>
<p>셀레늄 IDE는 이 문제를 해결하지 않습니다. 대신, 사용자가 웹 브라우저에서 수행한 동작을 기록할 때 요소를 파악할 수 있는 모든 방식을 동원해 <strong>다중 타깃</strong>을 만들어둡니다. 그리고 그 가운데 어떤 방식을 선택할지는 사람에게 맡깁니다.</p>
<h3 id="타깃-선택">타깃 선택</h3>
<p><img src="/assets/2020-06-14-selenium-ide-guide/select-target-in-page.png" alt="" /></p>
<p>‘Select target in page’ 버튼을 눌러 현재 페이지에서 다른 타깃을 선택할 수 있습니다. 이 버튼은 페이지 내의 다른 타깃을 선택하는 기능일 뿐입니다. 앞서 설명한 다중 타깃과는 무관합니다.</p>
<h2 id="로그">로그</h2>
<p><img src="/assets/2020-06-14-selenium-ide-guide/log.png" alt="" /></p>
<p>로그 탭에서는 테스트 중 명령어의 실행 시간이나 테스트의 성공 여부를 확인할 수 있습니다.</p>
<h2 id="레퍼런스">레퍼런스</h2>
<p><img src="/assets/2020-06-14-selenium-ide-guide/reference.png" alt="" /></p>
<p>레퍼런스 탭은 선택한 명령어의 사용 방법을 안내합니다. 모든 명령어의 사용 방법을 확인하려면 <a href="https://www.selenium.dev/selenium-ide/docs/en/api/commands">Commands - Selenium IDE</a>를 참고하세요.</p>
<h2 id="cli-selenium-side-runner">CLI (<code class="language-plaintext highlighter-rouge">selenium-side-runner</code>)</h2>
<p><a href="https://www.seleniumhq.org/selenium-ide/docs/en/introduction/command-line-runner/"><code class="language-plaintext highlighter-rouge">selenium-side-runner</code></a>는 셀레늄 IDE에서 작성한 테스트를 실행하는 CLI 도구입니다.</p>
<p>이 CLI 도구를 이용해 테스트를 <strong>기록</strong>하거나 <strong>편집</strong>하는 것은 불가능합니다.</p>
<p>셀레늄 IDE와 달리 더 많은 브라우저를 지원하며, 병렬 실행 기능도 포함합니다. 자동화된 테스트 환경을 만들 경우 셀레늄 IDE 대신 <a href="https://www.seleniumhq.org/selenium-ide/docs/en/introduction/command-line-runner/"><code class="language-plaintext highlighter-rouge">selenium-side-runner</code></a>를 사용하는 것이 좋습니다.</p>
<p>요약하자면 다음과 같습니다:</p>
<table>
<thead>
<tr>
<th> </th>
<th>GUI</th>
<th>CLI</th>
<th><code class="language-plaintext highlighter-rouge">.side</code> 파일</th>
</tr>
</thead>
<tbody>
<tr>
<td>셀레늄 IDE</td>
<td>✔️ 지원</td>
<td>❌ 미지원</td>
<td>✔️ 지원</td>
</tr>
<tr>
<td><a href="https://www.seleniumhq.org/selenium-ide/docs/en/introduction/command-line-runner/"><code class="language-plaintext highlighter-rouge">selenium-side-runner</code></a></td>
<td>❌ 미지원</td>
<td>✔️ 지원</td>
<td>✔️ 지원</td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th> </th>
<th>사람이 수행하는 동작 기록</th>
<th>테스트 편집</th>
<th>테스트 실행</th>
</tr>
</thead>
<tbody>
<tr>
<td>셀레늄 IDE</td>
<td>✔️ 지원</td>
<td>✔️ 지원</td>
<td>✔️ 지원</td>
</tr>
<tr>
<td><a href="https://www.seleniumhq.org/selenium-ide/docs/en/introduction/command-line-runner/"><code class="language-plaintext highlighter-rouge">selenium-side-runner</code></a></td>
<td>❌ 미지원</td>
<td>❌ 미지원</td>
<td>✔️ 지원</td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th> </th>
<th>지원 웹 브라우저</th>
<th>테스트 병렬 실행</th>
</tr>
</thead>
<tbody>
<tr>
<td>셀레늄 IDE</td>
<td>크롬(Chrome), 파이어폭스(Firefox)</td>
<td>❌ 미지원 (<a href="https://www.seleniumhq.org/selenium-ide/docs/en/introduction/command-line-runner/"><code class="language-plaintext highlighter-rouge">selenium-side-runner</code></a>에서 실행 시 병렬 실행되게끔 하는 설정만 변경 가능)</td>
</tr>
<tr>
<td><a href="https://www.seleniumhq.org/selenium-ide/docs/en/introduction/command-line-runner/"><code class="language-plaintext highlighter-rouge">selenium-side-runner</code></a></td>
<td>크롬(Chrome), 에지(Edge), 파이어폭스(Firefox), 인터넷 익스플로러(Internet Explorer), 사파리(Safari)</td>
<td>✔️ 지원</td>
</tr>
</tbody>
</table>
<hr />
<p><a href="https://www.seleniumhq.org/selenium-ide/docs/en/introduction/command-line-runner/"><code class="language-plaintext highlighter-rouge">selenium-side-runner</code></a>는 버전 8 이상의 노드JS를 기반으로 합니다. 노드JS가 설치되어있지 않다면 <a href="https://nodejs.org/">노드JS 공식 홈페이지</a>에서 설치할 수 있습니다.</p>
<p>다음 명령어는 <a href="https://www.seleniumhq.org/selenium-ide/docs/en/introduction/command-line-runner/"><code class="language-plaintext highlighter-rouge">selenium-side-runner</code></a>를 설치합니다:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm <span class="nb">install</span> <span class="nt">--global</span> selenium-side-runner
</code></pre></div></div>
<p>실제로 사용해보기에 앞서 <a href="https://www.seleniumhq.org/selenium-ide/docs/en/introduction/command-line-runner/"><code class="language-plaintext highlighter-rouge">selenium-side-runner</code></a>를 웹 브라우저와 연결하는 과정이 필요합니다. 이를 위해 각 웹 브라우저에 맞는 웹드라이버를 설치해야 합니다. 웹드라이버의 설치 방법은 수동으로 웹드라이버 파일을 다운로드받는 방법과, <code class="language-plaintext highlighter-rouge">npm install</code>을 통해 노드JS 패키지 형태로 다운로드받는 방법이 있습니다. <a href="https://www.seleniumhq.org/selenium-ide/docs/en/introduction/command-line-runner/#installing-a-browser-driver">Installing a browser driver - Selenium IDE</a>를 참고하세요.</p>
<div class="note">
<p><span>참고:</span></p>
<p><code class="language-plaintext highlighter-rouge">npm install</code>을 통해 노드JS 패키지 형태로 다운로드하는 경우 반드시 다운로드된 웹드라이버 <strong>자체</strong>가 속한 디렉터리를 <code class="language-plaintext highlighter-rouge">PATH</code> 환경 변수에 명시해야 합니다. 대부분의 웹드라이버 패키지는 <code class="language-plaintext highlighter-rouge">chromedriver</code>같이 웹드라이버 명령어를 제공하기도 합니다만, 이것들은 자바스크립트 코드를 통해 간접적으로 접근하는 명령어이기 때문에 <a href="https://www.seleniumhq.org/selenium-ide/docs/en/introduction/command-line-runner/"><code class="language-plaintext highlighter-rouge">selenium-side-runner</code></a>가 인식하지 못합니다.</p>
</div>
<p>준비가 끝났습니다. 셀레늄 IDE를 통해 작성한 <code class="language-plaintext highlighter-rouge">.side</code> 파일을 <a href="https://www.seleniumhq.org/selenium-ide/docs/en/introduction/command-line-runner/"><code class="language-plaintext highlighter-rouge">selenium-side-runner</code></a>로 실행해봅시다.</p>
<p>다음 명령어는 크롬에서 <code class="language-plaintext highlighter-rouge">.side</code> 파일을 실행합니다:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>selenium-side-runner my-project.side
</code></pre></div></div>
<p>크롬 드라이버 대신 다른 드라이버를 사용하길 원할 경우 다음과 같이 해야 합니다:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>selenium-side-runner <span class="nt">--capabilities</span> <span class="s2">"browserName='internet explorer'"</span> my-project.side
selenium-side-runner <span class="nt">--capabilities</span> <span class="s2">"browserName=edge"</span> my-project.side
selenium-side-runner <span class="nt">--capabilities</span> <span class="s2">"browserName=firefox"</span> my-project.side
selenium-side-runner <span class="nt">--capabilities</span> <span class="s2">"browserName=safari"</span> my-project.side
</code></pre></div></div>
<p>이외에도 <a href="https://www.seleniumhq.org/selenium-ide/docs/en/introduction/command-line-runner/"><code class="language-plaintext highlighter-rouge">selenium-side-runner</code></a>는 다음과 같은 기능을 제공합니다:</p>
<ul>
<li><a href="https://www.seleniumhq.org/selenium-ide/docs/en/introduction/command-line-runner/#change-the-base-url">베이스 URL 변경</a></li>
<li><a href="https://www.seleniumhq.org/selenium-ide/docs/en/introduction/command-line-runner/#specify-a-default-configuration">설정 파일 지원 (<code class="language-plaintext highlighter-rouge">.side.yml</code>)</a></li>
<li><a href="https://www.selenium.dev/selenium-ide/docs/en/introduction/command-line-runner#filter-tests">정규 표현식을 통해 특정 테스트만 필터링</a></li>
<li><a href="https://www.seleniumhq.org/selenium-ide/docs/en/introduction/command-line-runner/#test-parallelization-in-a-suite">병렬 컴퓨팅을 위한 셀레늄 그리드(Selenium Grid) 연결</a></li>
</ul>
<h2 id="관련-saas">관련 SaaS</h2>
<ul>
<li><a href="https://testingbot.com/">TestingBot</a>: 클라우드 기반의 셀레늄 IDE</li>
</ul>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:electron" role="doc-endnote">
<p><a href="https://github.com/SeleniumHQ/selenium-ide#electron">Electron - Selenium IDE</a></p>
<blockquote>
<p>The move to Electron is a work in progress. We will post updates as progress is made. If you’re looking for Selenium IDE as a browser extension then check out the v3 branch.</p>
</blockquote>
<p><a href="#fnref:electron" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:why-location-not-remembered" role="doc-endnote">
<p><a href="https://www.selenium.dev/selenium-ide/docs/en/introduction/faq#why-is-the-location-i-saved-my-side-project-to-not-remembered">Why is the location I saved my SIDE project to not remembered? - Selenium IDE</a></p>
<blockquote>
<p>All of these questions are part of the same problem – as a browser extension Selenium IDE does not have access to the file system. The only way to offer “save” functionality is through downloading the file. This issue will be resolved when the IDE moves to a native application. This will give the IDE premier filesystem access, which will enable it to offer a polished “save” experience.</p>
<p>If you want to stay updated, you can follow along with issue 363.</p>
</blockquote>
<p><a href="#fnref:why-location-not-remembered" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:supported-exports" role="doc-endnote">
<p><a href="https://www.selenium.dev/selenium-ide/docs/en/introduction/code-export#supported-exports">Code Export - Selenium IDE</a> <a href="#fnref:supported-exports" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:javascript-control-flow" role="doc-endnote">
<p><a href="https://www.selenium.dev/selenium-ide/docs/en/introduction/control-flow#javascript-expressions">Control Flow - Selenium IDE</a></p>
<blockquote>
<p>Conditions in your application are checked by using JavaScript expressions.</p>
</blockquote>
<p><a href="#fnref:javascript-control-flow" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>방성범 (Bang Seongbeom)셀레늄 IDE(Selenium IDE)는 사용자가 웹 브라우저에서 수행한 동작을 기록하고, 이를 다시 재현합니다.넘파이 vs 사이파이2020-05-17T00:00:00+00:002020-05-17T00:00:00+00:00https://www.bangseongbeom.com/numpy-vs-scipy<p><a href="https://numpy.org/">넘파이</a>(NumPy)와 <a href="https://scipy.org/">사이파이</a>(SciPy)의 차이점에 대해 다룹니다.</p>
<h2 id="주요-역할">주요 역할</h2>
<table>
<thead>
<tr>
<th> </th>
<th>주요 역할</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://numpy.org/">넘파이</a></td>
<td>행렬 및 관련 기초 연산</td>
</tr>
<tr>
<td><a href="https://scipy.org/">사이파이</a></td>
<td>수학, 과학 알고리즘</td>
</tr>
</tbody>
</table>
<p><strong><a href="https://numpy.org/">넘파이</a>:</strong> 행렬 및 관련 기초 연산을 지원합니다. 행렬 그 자체에 대한 지원과 더불어, 정렬이나 형태 변환과 같이 행렬에서 사용하는 기본적인 연산을 지원합니다.</p>
<p><strong><a href="https://scipy.org/">사이파이</a>:</strong> <a href="https://numpy.org/">넘파이</a>에서 제공하는 행렬에 기반한 수학, 과학 알고리즘을 지원합니다.</p>
<h2 id="fft-선형대수-알고리즘">FFT, 선형대수 알고리즘</h2>
<table>
<thead>
<tr>
<th> </th>
<th>FFT, 선형대수 알고리즘</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://numpy.org/">넘파이</a></td>
<td>⚠️ 하위 호환성을 위해 어느 정도의 FFT, 선형대수 알고리즘 지원</td>
</tr>
<tr>
<td><a href="https://scipy.org/">사이파이</a></td>
<td>✔️ 완전한 기능을 가진 FFT, 선형대수 알고리즘 지원</td>
</tr>
</tbody>
</table>
<p><a href="https://numpy.org/">넘파이</a>의 주요 역할은 행렬과 관련된 기초 연산을 제공하는 것이므로, 원칙적으로는 FFT와 선형대수 알고리즘을 지원할 필요가 없습니다. 하지만 <a href="https://numpy.org/">넘파이</a>의 선배격인 라이브러리에서 FFT와 선형대수를 지원해왔기에, <a href="https://numpy.org/">넘파이</a> 역시 호환성을 위해 이러한 기능을 가지게 되었습니다<sup id="fnref:faq-scipy" role="doc-noteref"><a href="#fn:faq-scipy" class="footnote" rel="footnote">1</a></sup><sup id="fnref:numpy-dual-numpy" role="doc-noteref"><a href="#fn:numpy-dual-numpy" class="footnote" rel="footnote">2</a></sup>.</p>
<p>일반적인 경우라면 <a href="https://numpy.org/">넘파이</a> 대신 <a href="https://scipy.org/">사이파이</a>에 있는 FFT, 선형대수 알고리즘을 사용하는 것이 좋습니다. 대부분의 새로운 기능은 <a href="https://scipy.org/">사이파이</a>에서만 제공하기 때문입니다<sup id="fnref:faq-scipy:1" role="doc-noteref"><a href="#fn:faq-scipy" class="footnote" rel="footnote">1</a></sup><sup id="fnref:numpy-dual-numpy:1" role="doc-noteref"><a href="#fn:numpy-dual-numpy" class="footnote" rel="footnote">2</a></sup>.</p>
<h2 id="알고리즘의-포트란-의존성">알고리즘의 포트란 의존성</h2>
<table>
<thead>
<tr>
<th> </th>
<th>알고리즘의 포트란 의존성</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://numpy.org/">넘파이</a></td>
<td>❌ 포트란에 의존하지 않음</td>
</tr>
<tr>
<td><a href="https://scipy.org/">사이파이</a></td>
<td>✔️ FFT, 선형대수뿐만 아니라 많은 알고리즘이 포트란에 의존</td>
</tr>
</tbody>
</table>
<p><a href="https://numpy.org/">넘파이</a>에서 제공하는 FFT와 선형대수 알고리즘의 또다른 특징은 포트란에 의존하지 않는다는 것입니다. 이와 반대로 <a href="https://scipy.org/">사이파이</a>에서 제공하는 많은 알고리즘은 포트란에 강력히 의존하고 있으며, 이는 FFT와 선형대수 역시 마찬가지입니다. <a href="https://scipy.org/">사이파이</a>에서 선형대수 알고리즘을 제공하는 <code class="language-plaintext highlighter-rouge">scipy.linalg</code> 모듈은 포트란으로 짜여진 <a href="http://www.netlib.org/lapack/">LAPACK</a> 라이브러리를 파이썬에서 사용할 수 있게끔 감싸놓은 것입니다<sup id="fnref:wrapping-of-fortran-lapack-scipy" role="doc-noteref"><a href="#fn:wrapping-of-fortran-lapack-scipy" class="footnote" rel="footnote">3</a></sup>.</p>
<h2 id="참고">참고</h2>
<ul>
<li><a href="https://stackoverflow.com/questions/6200910/relationship-between-scipy-and-numpy">Relationship between SciPy and NumPy - Stack Overflow</a>: 관련 스택오버플로 질문</li>
<li><a href="https://stackoverflow.com/questions/6363154/what-is-the-difference-between-numpy-fft-and-scipy-fftpack">What is the difference between numpy.fft and scipy.fftpack? - Stack Overflow</a>: 관련 스택오버플로 질문</li>
</ul>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:faq-scipy" role="doc-endnote">
<p><a href="https://www.scipy.org/scipylib/faq.html#what-is-the-difference-between-numpy-and-scipy">Frequently Asked Questions - SciPy</a></p>
<blockquote>
<p>What is the difference between NumPy and SciPy?</p>
<p>In an ideal world, NumPy would contain nothing but the array data type and the most basic operations: indexing, sorting, reshaping, basic elementwise functions, etc. All numerical code would reside in SciPy. However, one of NumPy’s important goals is compatibility, so NumPy tries to retain all features supported by either of its predecessors. Thus, NumPy contains some linear algebra functions and Fourier transforms, even though these more properly belong in SciPy. In any case, SciPy contains more fully-featured versions of the linear algebra modules, as well as many other numerical algorithms. If you are doing scientific computing with Python, you should probably install both NumPy and SciPy. Most new features belong in SciPy rather than NumPy.</p>
</blockquote>
<p><a href="#fnref:faq-scipy" class="reversefootnote" role="doc-backlink">↩</a> <a href="#fnref:faq-scipy:1" class="reversefootnote" role="doc-backlink">↩<sup>2</sup></a></p>
</li>
<li id="fn:numpy-dual-numpy" role="doc-endnote">
<p><a href="https://numpy.org/doc/stable/reference/routines.dual.html">Optionally Scipy-accelerated routines (numpy.dual) - NumPy</a></p>
<blockquote>
<p>Scipy can be built to use accelerated or otherwise improved libraries for FFTs, linear algebra, and special functions.</p>
</blockquote>
<p><a href="#fnref:numpy-dual-numpy" class="reversefootnote" role="doc-backlink">↩</a> <a href="#fnref:numpy-dual-numpy:1" class="reversefootnote" role="doc-backlink">↩<sup>2</sup></a></p>
</li>
<li id="fn:wrapping-of-fortran-lapack-scipy" role="doc-endnote">
<p><a href="https://www.scipy.org/scipylib/faq.html#why-both-numpy-linalg-and-scipy-linalg-what-s-the-difference">Frequently Asked Questions - SciPy</a></p>
<blockquote>
<p>Why both numpy.linalg and scipy.linalg? What’s the difference?</p>
<p>scipy.linalg is a more complete wrapping of Fortran LAPACK using f2py.</p>
<p>One of the design goals of NumPy was to make it buildable without a Fortran compiler, and if you don’t have LAPACK available, NumPy will use its own implementation. SciPy requires a Fortran compiler to be built, and heavily depends on wrapped Fortran code.</p>
</blockquote>
<p><a href="#fnref:wrapping-of-fortran-lapack-scipy" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>방성범 (Bang Seongbeom)넘파이(NumPy)와 사이파이(SciPy)의 차이점에 대해 다룹니다.기계 학습 라이브러리들의 ‘import as’ 약칭2020-05-10T00:00:00+00:002020-05-10T00:00:00+00:00https://www.bangseongbeom.com/machine-learning-libraries-import-as-abbreviations<p><code class="language-plaintext highlighter-rouge">numpy</code>를 <code class="language-plaintext highlighter-rouge">np</code>로 줄여쓰는 것처럼, 다양한 기계 학습 라이브러리의 약칭을 정리했습니다.</p>
<hr />
<p>수학이나 통계, 기계 학습과 관련된 파이썬 환경에서는, 라이브러리의 모듈 전체를 한꺼번에 <code class="language-plaintext highlighter-rouge">import</code>하는 것을 선호합니다. 가져올 함수는 많은데 이것들을 하나 하나 <code class="language-plaintext highlighter-rouge">import</code>하는 건 여간 귀찮은 일이 아닙니다.</p>
<p>아예 <a href="https://numpy.org/">넘파이</a>(NumPy)는 공식 문서에서 <code class="language-plaintext highlighter-rouge">import as</code>를 어떻게 할 것인지 대해 명시적으로 언급합니다<sup id="fnref:import-conventions-throughout-numpy" role="doc-noteref"><a href="#fn:import-conventions-throughout-numpy" class="footnote" rel="footnote">1</a></sup>. 이와 관련된 논의 또한 오래 전부터 존재해왔습니다<sup id="fnref:digest-vol-19-issue-44" role="doc-noteref"><a href="#fn:digest-vol-19-issue-44" class="footnote" rel="footnote">2</a></sup>.</p>
<p><code class="language-plaintext highlighter-rouge">import as</code>를 어떻게 할 것인지는 전적으로 <strong>코드 작성자의 선택</strong>입니다. 하지만 관습적으로 쓰이는 약칭을 사용한다면, 다른 사람이 나의 코드를 볼 때 내 코드의 <code class="language-plaintext highlighter-rouge">import</code>가 어떻게 되어있는지 굳이 확인해보지 않아도 된다는 장점이 있습니다. 기계 학습 관련 라이브러리 중 하나인 <a href="https://pandas.pydata.org/">판다스</a>(pandas)는 문서의 모든 예시 코드에서 <code class="language-plaintext highlighter-rouge">import pandas as pd</code> 코드가 삽입되어 있다고 가정합니다<sup id="fnref:code-assumed-pandas" role="doc-noteref"><a href="#fn:code-assumed-pandas" class="footnote" rel="footnote">3</a></sup>. 때문에 <code class="language-plaintext highlighter-rouge">pd</code>가 무엇의 약칭인지 일일이 확인할 필요가 없습니다.</p>
<h2 id="넘파이">넘파이</h2>
<p><a href="https://numpy.org/">넘파이</a>의 경우, 공식 문서와 소스 코드에서 <code class="language-plaintext highlighter-rouge">numpy</code> 모듈을 <code class="language-plaintext highlighter-rouge">np</code>로 줄여쓸 것이라 <strong>명시적으로</strong> 언급합니다:</p>
<p><a href="https://numpy.org/doc/stable/docs/howto_document.html#import-conventions">A Guide to NumPy/SciPy Documentation - NumPy</a>:</p>
<blockquote>
<p>The following import conventions are used throughout the NumPy source and documentation:</p>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="n">np</span>
</code></pre></div> </div>
</blockquote>
<h2 id="맷플롯립">맷플롯립</h2>
<p><a href="https://numpy.org/">넘파이</a> 공식 문서와 소스 코드에서는 <a href="https://matplotlib.org/">맷플롯립</a>(Matplotlib)의 <code class="language-plaintext highlighter-rouge">matplotlib</code> 모듈을 <code class="language-plaintext highlighter-rouge">mpl</code>, <code class="language-plaintext highlighter-rouge">matplotlib.pyplot</code> 모듈을 <code class="language-plaintext highlighter-rouge">plt</code>으로 줄여쓸 것이라 <strong>명시적으로</strong> 언급합니다:</p>
<p><a href="https://numpy.org/doc/stable/docs/howto_document.html#import-conventions">A Guide to NumPy/SciPy Documentation - NumPy</a>:</p>
<blockquote>
<p>The following import conventions are used throughout the NumPy source and documentation:</p>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">matplotlib</span> <span class="k">as</span> <span class="n">mpl</span>
<span class="kn">import</span> <span class="nn">matplotlib.pyplot</span> <span class="k">as</span> <span class="n">plt</span>
</code></pre></div> </div>
</blockquote>
<p><a href="https://matplotlib.org/">맷플롯립</a>의 다른 하위 모듈에 대한 약칭에 대해서는 명시적으로 언급하고 있지 않습니다. 공식 문서를 살펴보면 모듈 자체의 이름을 사용하거나, 아니면 모듈에서 사용할 함수나 클래스를 하나씩 <code class="language-plaintext highlighter-rouge">from import</code>로 가져옵니다:</p>
<p><a href="https://matplotlib.org/faq/howto_faq.html">How-to - Matplotlib</a>:</p>
<blockquote>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">matplotlib.mlab</span> <span class="k">as</span> <span class="n">mlab</span>
<span class="kn">import</span> <span class="nn">matplotlib.ticker</span> <span class="k">as</span> <span class="n">ticker</span>
</code></pre></div> </div>
<p>…</p>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">matplotlib.backends.backend_pdf</span> <span class="kn">import</span> <span class="n">PdfPages</span>
</code></pre></div> </div>
<p>…</p>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">matplotlib.figure</span> <span class="kn">import</span> <span class="n">Figure</span>
</code></pre></div> </div>
</blockquote>
<h2 id="사이파이">사이파이</h2>
<p><a href="https://scipy.org/">사이파이</a>(SciPy)의 경우 <code class="language-plaintext highlighter-rouge">scipy</code> 모듈에 대해 약칭을 사용하지 않을 것을 <strong>명시적으로</strong> 권고합니다<sup id="fnref:do-not-abbreviate-scipy" role="doc-noteref"><a href="#fn:do-not-abbreviate-scipy" class="footnote" rel="footnote">4</a></sup>. 하위 모듈에 대해 각각 <code class="language-plaintext highlighter-rouge">from import</code>를 사용합니다:</p>
<p><a href="https://docs.scipy.org/doc/scipy/reference/tutorial/general.html#scipy-organization">Introduction - SciPy</a>:</p>
<blockquote>
<p>SciPy sub-packages need to be imported separately, for example:</p>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">>>></span> <span class="kn">from</span> <span class="nn">scipy</span> <span class="kn">import</span> <span class="n">linalg</span><span class="p">,</span> <span class="n">optimize</span>
</code></pre></div> </div>
</blockquote>
<p>다만 <code class="language-plaintext highlighter-rouge">scipy.io</code> 모듈은 파이썬 내장 패키지인 <code class="language-plaintext highlighter-rouge">io</code>와 충돌하기에, <code class="language-plaintext highlighter-rouge">import scipy.io as spio</code>처럼 약칭을 사용하는 것으로 보입니다:</p>
<p><a href="https://docs.scipy.org/doc/scipy/reference/api.html#guidelines-for-importing-functions-from-scipy">SciPy API - SciPy</a>:</p>
<blockquote>
<p>This form of importing submodules is preferred for all submodules except scipy.io (because io is also the name of a module in the Python stdlib):</p>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">scipy</span> <span class="kn">import</span> <span class="n">interpolate</span>
<span class="kn">from</span> <span class="nn">scipy</span> <span class="kn">import</span> <span class="n">integrate</span>
<span class="kn">import</span> <span class="nn">scipy.io</span> <span class="k">as</span> <span class="n">spio</span>
</code></pre></div> </div>
</blockquote>
<p><code class="language-plaintext highlighter-rouge">scipy.sparse.linalg</code> 모듈같이 한 층 더 깊이 존재하는 모듈이라면 <code class="language-plaintext highlighter-rouge">from scipy.sparse import linalg</code>처럼 합니다:</p>
<p><a href="https://docs.scipy.org/doc/scipy/reference/api.html#guidelines-for-importing-functions-from-scipy">SciPy API - SciPy</a>:</p>
<blockquote>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># second form
</span><span class="kn">from</span> <span class="nn">scipy.stats</span> <span class="kn">import</span> <span class="n">distributions</span>
<span class="n">distributions</span><span class="p">.</span><span class="n">lomax</span><span class="p">(...)</span>
</code></pre></div> </div>
</blockquote>
<div class="note">
<p><span>참고:</span></p>
<p>지금은 더 이상 <code class="language-plaintext highlighter-rouge">scipy</code> 모듈 자체를 <code class="language-plaintext highlighter-rouge">import</code>하는 것을 권장하지 않지만<sup id="fnref:api-scipy" role="doc-noteref"><a href="#fn:api-scipy" class="footnote" rel="footnote">5</a></sup>, 예전에는 <code class="language-plaintext highlighter-rouge">scipy</code>를 직접 <code class="language-plaintext highlighter-rouge">import</code>하고 했습니다. 당시에는 <code class="language-plaintext highlighter-rouge">scipy</code> 모듈 자체에 대해 <code class="language-plaintext highlighter-rouge">sp</code>라는 약칭을 사용하기도 했던 것으로 보입니다<sup id="fnref:top-level-scipy" role="doc-noteref"><a href="#fn:top-level-scipy" class="footnote" rel="footnote">6</a></sup>.</p>
</div>
<h2 id="판다스">판다스</h2>
<p><a href="https://pandas.pydata.org/">판다스</a>(pandas)의 경우 명시적으로 언급하지는 않으나, 공식 문서에서는 <code class="language-plaintext highlighter-rouge">pandas</code> 모듈을 <code class="language-plaintext highlighter-rouge">pd</code>라고 줄여씁니다:</p>
<p><a href="https://pandas.pydata.org/docs/development/contributing_docstring.html#conventions-for-the-examples">pandas docstring guide - pandas</a>:</p>
<blockquote>
<p>Code in examples is assumed to always start with these two lines which are not shown:</p>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="n">np</span>
<span class="kn">import</span> <span class="nn">pandas</span> <span class="k">as</span> <span class="n">pd</span>
</code></pre></div> </div>
</blockquote>
<h2 id="사이킷런">사이킷런</h2>
<p><a href="https://scikit-learn.org/">사이킷런</a>(scikit-learn)의 경우 명시적으로 언급하지는 않으나, 공식 문서에서는 <code class="language-plaintext highlighter-rouge">import as</code> 대신 <code class="language-plaintext highlighter-rouge">from import</code>로 모듈, 함수, 클래스를 적절하게 불러옵니다:</p>
<p><a href="https://scikit-learn.org/stable/auto_examples/bicluster/plot_spectral_coclustering.html">A demo of the Spectral Co-Clustering algorithm - scikit-learn</a>:</p>
<blockquote>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">sklearn.datasets</span> <span class="kn">import</span> <span class="n">make_biclusters</span>
<span class="kn">from</span> <span class="nn">sklearn.cluster</span> <span class="kn">import</span> <span class="n">SpectralCoclustering</span>
<span class="kn">from</span> <span class="nn">sklearn.metrics</span> <span class="kn">import</span> <span class="n">consensus_score</span>
</code></pre></div> </div>
</blockquote>
<p><a href="https://scikit-learn.org/stable/tutorial/basic/tutorial.html">An introduction to machine learning with scikit-learn - scikit-learn</a>:</p>
<blockquote>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">>>></span> <span class="kn">from</span> <span class="nn">sklearn</span> <span class="kn">import</span> <span class="n">datasets</span>
<span class="o">>>></span> <span class="kn">from</span> <span class="nn">sklearn</span> <span class="kn">import</span> <span class="n">svm</span>
</code></pre></div> </div>
</blockquote>
<h2 id="대스크">대스크</h2>
<p><a href="https://dask.org/">대스크</a>(Dask)의 경우 명시적으로 언급하지는 않으나, 공식 문서에서는 <code class="language-plaintext highlighter-rouge">dask.dataframe</code> 모듈을 <code class="language-plaintext highlighter-rouge">dd</code>로, <code class="language-plaintext highlighter-rouge">dask.array</code> 모듈을 <code class="language-plaintext highlighter-rouge">da</code>로, <code class="language-plaintext highlighter-rouge">dask.bag</code> 모듈을 <code class="language-plaintext highlighter-rouge">db</code>로 줄여씁니다:</p>
<p><a href="https://docs.dask.org/en/latest/">Dask - Dask</a>:</p>
<blockquote>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">dask.dataframe</span> <span class="k">as</span> <span class="n">dd</span>
</code></pre></div> </div>
<p>…</p>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">dask.array</span> <span class="k">as</span> <span class="n">da</span>
</code></pre></div> </div>
<p>…</p>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">dask.bag</span> <span class="k">as</span> <span class="n">db</span>
</code></pre></div> </div>
</blockquote>
<p>그 외 모듈의 경우 <code class="language-plaintext highlighter-rouge">from import</code>를 통해 함수나 클래스를 직접 가져오는 것으로 보입니다:</p>
<p><a href="https://docs.dask.org/en/latest/futures.html">Futures - Dask</a>:</p>
<blockquote>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">dask.distributed</span> <span class="kn">import</span> <span class="n">Client</span>
</code></pre></div> </div>
<p>…</p>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">dask.distributed</span> <span class="kn">import</span> <span class="n">wait</span>
</code></pre></div> </div>
</blockquote>
<h2 id="텐서플로">텐서플로</h2>
<p><a href="https://www.tensorflow.org/">탠서플로</a>(TensorFlow)의 경우 명시적으로 언급하지는 않으나, 공식 문서에서는 <code class="language-plaintext highlighter-rouge">tensorflow</code> 모듈을 <code class="language-plaintext highlighter-rouge">tf</code>로 줄여씁니다:</p>
<p><a href="https://www.tensorflow.org/tutorials/quickstart/beginner">TensorFlow 2 quickstart for beginners - TensorFlow</a>:</p>
<blockquote>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">tensorflow</span> <span class="k">as</span> <span class="n">tf</span>
</code></pre></div> </div>
</blockquote>
<p><code class="language-plaintext highlighter-rouge">tensorflow_datasets</code> 모듈의 경우 <code class="language-plaintext highlighter-rouge">tfds</code>로 줄여씁니다:</p>
<p><a href="https://www.tensorflow.org/datasets/overview">TensorFlow Datasets - TensorFlow</a>:</p>
<blockquote>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">tensorflow_datasets</span> <span class="k">as</span> <span class="n">tfds</span>
</code></pre></div> </div>
</blockquote>
<h2 id="파이토치">파이토치</h2>
<p><a href="https://pytorch.org/">파이토치</a>(PyTorch)의 경우 명시적으로 언급하지는 않으나, 공식 문서에서는 <code class="language-plaintext highlighter-rouge">import torch</code>를 주로 사용합니다. 하위 모듈이나 함수, 클래스의 경우 이름이 짧으면 그대로 쓰고 이름이 길면 줄여 쓰는 경향이 있습니다:</p>
<p><a href="https://pytorch.org/get-started/locally/">Start Locally - PyTorch</a>:</p>
<blockquote>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">torch</span>
</code></pre></div> </div>
</blockquote>
<p><a href="https://pytorch.org/docs/stable/notes/ddp.html">Distributed Data Parallel - PyTorch</a>:</p>
<blockquote>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">torch.distributed</span> <span class="k">as</span> <span class="n">dist</span>
<span class="kn">import</span> <span class="nn">torch.multiprocessing</span> <span class="k">as</span> <span class="n">mp</span>
<span class="kn">import</span> <span class="nn">torch.nn</span> <span class="k">as</span> <span class="n">nn</span>
<span class="kn">import</span> <span class="nn">torch.optim</span> <span class="k">as</span> <span class="n">optim</span>
<span class="kn">from</span> <span class="nn">torch.nn.parallel</span> <span class="kn">import</span> <span class="n">DistributedDataParallel</span> <span class="k">as</span> <span class="n">DDP</span>
</code></pre></div> </div>
</blockquote>
<h2 id="케라스">케라스</h2>
<p>[케라스](Keras)의 경우 명시적으로 언급하지는 않으나, 공식 문서에서는 <code class="language-plaintext highlighter-rouge">keras</code>는 <code class="language-plaintext highlighter-rouge">keras</code> 그대로 사용합니다. <code class="language-plaintext highlighter-rouge">keras.layers</code>의 경우 <code class="language-plaintext highlighter-rouge">layers</code>로 줄여쓰거나 <code class="language-plaintext highlighter-rouge">keras.layers</code> 그대로 사용합니다:</p>
<p><a href="https://keras.io/guides/functional_api/">The Functional API - Keras</a>:</p>
<blockquote>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">tensorflow</span> <span class="kn">import</span> <span class="n">keras</span>
<span class="kn">from</span> <span class="nn">tensorflow.keras</span> <span class="kn">import</span> <span class="n">layers</span>
</code></pre></div> </div>
</blockquote>
<p><a href="https://keras.io/guides/serialization_and_saving/">Serialization and saving - Keras</a>:</p>
<blockquote>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">outputs</span> <span class="o">=</span> <span class="n">keras</span><span class="p">.</span><span class="n">layers</span><span class="p">.</span><span class="n">Dense</span><span class="p">(</span><span class="mi">1</span><span class="p">)(</span><span class="n">inputs</span><span class="p">)</span>
</code></pre></div> </div>
</blockquote>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:import-conventions-throughout-numpy" role="doc-endnote">
<p><a href="https://numpy.org/doc/stable/docs/howto_document.html#import-conventions">A Guide to NumPy/SciPy Documentation - NumPy</a>:</p>
<blockquote>
<p>The following import conventions are used throughout the NumPy source and documentation:</p>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="n">np</span>
<span class="kn">import</span> <span class="nn">matplotlib</span> <span class="k">as</span> <span class="n">mpl</span>
<span class="kn">import</span> <span class="nn">matplotlib.pyplot</span> <span class="k">as</span> <span class="n">plt</span>
</code></pre></div> </div>
</blockquote>
<p><a href="#fnref:import-conventions-throughout-numpy" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:digest-vol-19-issue-44" role="doc-endnote">
<p><a href="http://numpy-discussion.10968.n7.nabble.com/Re-Numpy-discussion-Digest-Vol-19-Issue-44-tt10095.html">Re: Numpy-discussion Digest, Vol 19, Issue 44</a>: 2008년 메일링 리스트에서 진행된 <code class="language-plaintext highlighter-rouge">import as</code> 표준화 논의 <a href="#fnref:digest-vol-19-issue-44" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:code-assumed-pandas" role="doc-endnote">
<p><a href="https://pandas.pydata.org/docs/development/contributing_docstring.html#conventions-for-the-examples">pandas docstring guide - pandas</a>:</p>
<blockquote>
<p>Code in examples is assumed to always start with these two lines which are not shown:</p>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="n">np</span>
<span class="kn">import</span> <span class="nn">pandas</span> <span class="k">as</span> <span class="n">pd</span>
</code></pre></div> </div>
</blockquote>
<p><a href="#fnref:code-assumed-pandas" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:do-not-abbreviate-scipy" role="doc-endnote">
<p><a href="https://numpy.org/doc/stable/docs/howto_document.html#import-conventions">A Guide to NumPy/SciPy Documentation - NumPy</a>:</p>
<blockquote>
<p>Do not abbreviate scipy. There is no motivating use case to abbreviate it in the real world, so we avoid it in the documentation to avoid confusion.</p>
</blockquote>
<p><a href="#fnref:do-not-abbreviate-scipy" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:api-scipy" role="doc-endnote">
<p><a href="https://docs.scipy.org/doc/scipy/reference/api.html">SciPy API - SciPy</a>:</p>
<blockquote>
<p>These functions still exist for backwards compatibility, but should be imported from numpy directly.</p>
</blockquote>
<p><a href="#fnref:api-scipy" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:top-level-scipy" role="doc-endnote">
<p><a href="https://docs.scipy.org/doc/scipy-0.9.0/reference/tutorial/basic.html">Basic functions in Numpy (and top-level scipy) - SciPy v0.9</a>:</p>
<blockquote>
<p>To begin with, all of the Numpy functions have been subsumed into the scipy namespace so that all of those functions are available without additionally importing Numpy. In addition, the universal functions (addition, subtraction, division) have been altered to not raise exceptions if floating-point errors are encountered; instead, NaN’s and Inf’s are returned in the arrays. To assist in detection of these events, several functions (sp.isnan, sp.isfinite, sp.isinf) are available.</p>
</blockquote>
<p><a href="#fnref:top-level-scipy" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>방성범 (Bang Seongbeom)numpy를 np로 줄여쓰는 것처럼, 다양한 기계 학습 라이브러리의 약칭을 정리했습니다.발레나에처 소개2020-05-07T00:00:00+00:002020-05-07T00:00:00+00:00https://www.bangseongbeom.com/balena-etcher-intro<p><a href="https://www.balena.io/etcher/">발레나에처</a>(balenaEtcher)는 간단하게 SD 카드나 USB에 운영 체제 이미지를 구울 수 있는 유틸리티 프로그램입니다.</p>
<h2 id="장점">장점</h2>
<ul>
<li>매우 단순한 UI를 가지고 있어 누구나 쉽게 사용할 수 있습니다.</li>
</ul>
<h2 id="단점">단점</h2>
<ul>
<li>용량이 100MB가 넘습니다. 기능에 비해 너무 큽니다. (이는 <a href="https://www.electronjs.org/">일렉트론</a>을 기반으로 하기 때문입니다<sup id="fnref:electron" role="doc-noteref"><a href="#fn:electron" class="footnote" rel="footnote">1</a></sup>. 일렉트론은 내부적으로 <a href="https://www.google.com/chrome/">크롬 브라우저</a>를 포함하고 있습니다)</li>
</ul>
<h2 id="시스템-요구-사항">시스템 요구 사항</h2>
<ul>
<li><strong>언어:</strong> 영어 <strong>(한국어 지원 안 함)</strong></li>
<li><strong>OS:</strong> 윈도우, 맥OS, 리눅스</li>
<li><strong>용량:</strong> 약 142MB</li>
</ul>
<h2 id="사용-방법">사용 방법</h2>
<figure>
<p><img src="/assets/2020-05-07-balena-etcher-intro/balena-etcher-1.png" alt="" /></p>
<figcaption><strong>‘Select image’</strong> 버튼을 누릅니다.</figcaption>
</figure>
<figure>
<p><img src="/assets/2020-05-07-balena-etcher-intro/balena-etcher-2.png" alt="" /></p>
<figcaption>굽는데 사용할 이미지 파일(ISO, IMG, …)을 선택합니다.</figcaption>
</figure>
<figure>
<p><img src="/assets/2020-05-07-balena-etcher-intro/balena-etcher-3.png" alt="" /></p>
<figcaption><strong>‘Select target’</strong> 버튼을 누릅니다.</figcaption>
</figure>
<figure>
<p><img src="/assets/2020-05-07-balena-etcher-intro/balena-etcher-4.png" alt="" /></p>
<figcaption>이미지를 설치할 SD 카드 또는 USB를 선택합니다.</figcaption>
</figure>
<figure>
<p><img src="/assets/2020-05-07-balena-etcher-intro/balena-etcher-5.png" alt="" /></p>
<figcaption><strong>‘Flash!’</strong> 버튼을 눌러 굽기를 시작합니다.</figcaption>
</figure>
<div class="note">
<p><span>참고:</span></p>
<figure>
<p><img src="/assets/2020-05-07-balena-etcher-intro/balena-etcher-6.png" alt="" /></p>
<figcaption>SD 카드 또는 USB의 용량이 너무 클 경우, <a href="https://www.balena.io/etcher/">발레나에처</a>는 사용자가 <strong>자료 백업 용과 같은 중요한 드라이브</strong>에 이미지를 구우려 하는 것이 아닌지 경고합니다. 정말 이미지를 굽기 위한 드라이브가 맞다면 <strong>‘Continue’</strong> 버튼을 눌러 굽기를 시작합시다.</figcaption>
</figure>
</div>
<h2 id="대안">대안</h2>
<ul>
<li><a href="https://rufus.ie/">루퍼스</a>(Rufus): 매우 적은 용량을 차지합니다. 한국어도 지원합니다. 단순한 UI를 가지고 있지만, 미숙련 사용자에게는 <a href="https://www.balena.io/etcher/">발레나에처</a>에 비해 조금 어려울 수 있습니다.</li>
</ul>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:electron" role="doc-endnote">
<p><a href="https://www.balena.io/etcher/">balenaEtcher</a></p>
<blockquote>
<p>Open Source</p>
<p>Made with JS, HTML, node.js and Electron. Dive in and contribute!</p>
</blockquote>
<p><a href="#fnref:electron" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>방성범 (Bang Seongbeom)발레나에처(balenaEtcher)는 간단하게 SD 카드나 USB에 운영 체제 이미지를 구울 수 있는 유틸리티 프로그램입니다.배시 배열2020-05-02T00:00:00+00:002020-05-02T00:00:00+00:00https://www.bangseongbeom.com/bash-arrays<p>배열을 통해 여러 값을 효과적으로 다룰 수 있습니다.</p>
<hr />
<p><a href="https://www.gnu.org/software/bash/manual/html_node/index.html">배시</a>(Bash)는 여느 프로그래밍 언어와 같이 배열 기능을 제공합니다. 배열은 여러 값을 하나의 변수에 보관할 수 있도록 해줍니다. 새로운 값을 추가하거나 이미 존재하는 값을 수정 또는 삭제할 수 있습니다.</p>
<h2 id="배열-생성">배열 생성</h2>
<p><code class="language-plaintext highlighter-rouge">변수이름=(값1 값2 값3)</code> 형태로 배열을 생성합니다<sup id="fnref:way-1" role="doc-noteref"><a href="#fn:way-1" class="footnote" rel="footnote">1</a></sup>. <strong>배열의 값을 띄어쓰기로 구분함에 주의하세요.</strong></p>
<div class="example">
<p><span>예시:</span></p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">ASDF</span><span class="o">=(</span>100 200 Hello<span class="o">)</span>
</code></pre></div> </div>
</div>
<div class="example">
<p><span>예시:</span></p>
<p><strong>공백을 포함한 문자열</strong>은 [배시]의 문법 상 큰따옴표나 작은따옴표로 묶어야 합니다:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">ASDF</span><span class="o">=(</span><span class="s2">"H e l l o"</span> <span class="s1">'w o r l d'</span><span class="o">)</span>
</code></pre></div> </div>
</div>
<div class="example invalid">
<p><span>잘못된 예시:</span></p>
<p>다른 프로그래밍 언어와 달리, 배시에서는 <code class="language-plaintext highlighter-rouge">=</code> 좌우에 띄어쓰기가 있으면 안 됩니다:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ASDF <span class="o">=</span> <span class="o">(</span>100 200 Hello<span class="o">)</span> <span class="c"># 잘못됨!</span>
</code></pre></div> </div>
<p>위 코드를 실행하면 다음과 같이 오류를 출력합니다:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-bash: syntax error near unexpected token `('
</code></pre></div> </div>
</div>
<h2 id="값-얻기-인덱스">값 얻기, 인덱스</h2>
<p>배열로부터 값을 하나 얻기 위해서는 <code class="language-plaintext highlighter-rouge">${변수이름[인덱스]}</code> 형태를 사용합니다.</p>
<p><strong>인덱스</strong>는 값이 배열의 어디에 위치해있는지를 가리키는 수입니다. 배열을 만들 때 나열한 순서대로 인덱스가 지정됩니다. 인덱스가 <strong>0</strong>이라면 <strong>1번째</strong> 값을, 인덱스가 <strong>1</strong>이라면 <strong>2번째</strong> 값을 의미합니다. (인덱스는 1이 아니라 <strong>0부터 시작</strong>합니다<sup id="fnref:starts-at-zero" role="doc-noteref"><a href="#fn:starts-at-zero" class="footnote" rel="footnote">2</a></sup>)</p>
<div class="example">
<p><span>예시:</span></p>
<p>다음은 배열에 <code class="language-plaintext highlighter-rouge">100</code>, <code class="language-plaintext highlighter-rouge">200</code>, <code class="language-plaintext highlighter-rouge">300</code>을 넣은 뒤 순서대로 출력하는 코드입니다:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">ASDF</span><span class="o">=(</span>100 200 300<span class="o">)</span>
<span class="nb">echo</span> <span class="k">${</span><span class="nv">ASDF</span><span class="p">[0]</span><span class="k">}</span>
<span class="nb">echo</span> <span class="k">${</span><span class="nv">ASDF</span><span class="p">[1]</span><span class="k">}</span>
<span class="nb">echo</span> <span class="k">${</span><span class="nv">ASDF</span><span class="p">[2]</span><span class="k">}</span>
</code></pre></div> </div>
<p>출력 결과:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>100
200
300
</code></pre></div> </div>
</div>
<h3 id="특이한-성질-중괄호가-필요한-이유">[특이한 성질] 중괄호가 필요한 이유</h3>
<p>값을 얻기 위해서는 <code class="language-plaintext highlighter-rouge">echo ${ASDF[2]}</code>처럼 중괄호를 <strong>반드시</strong> 붙여야 합니다. <code class="language-plaintext highlighter-rouge">echo $ASDF[2]</code>처럼 중괄호를 붙이지 않으면, 배시는 <code class="language-plaintext highlighter-rouge">ASDF[2]</code>를 통째로 변수로서 인식하지 않고 <code class="language-plaintext highlighter-rouge">ASDF</code>까지만 인식합니다.</p>
<p>그 이유는 대괄호가 특별한 값으로 먼저 취급되기 때문입니다<sup id="fnref:braces-are-required" role="doc-noteref"><a href="#fn:braces-are-required" class="footnote" rel="footnote">3</a></sup>. 대괄호 없이 <code class="language-plaintext highlighter-rouge">$ASDF[2]</code>처럼 한다면 배시는 <code class="language-plaintext highlighter-rouge">$ASDF</code>와 <code class="language-plaintext highlighter-rouge">[2]</code>로 나누어 인식합니다.</p>
<div class="note">
<p><span>참고:</span></p>
<p><code class="language-plaintext highlighter-rouge">echo $ASDF[2]</code>처럼 한다고 해서 오류가 발생하지는 않습니다. 이는 배시에서 <code class="language-plaintext highlighter-rouge">$ASDF</code>같이 <strong>배열 이름 자체에 접근</strong>하는 것을 허용하고 있기 때문인데요, 자세한 내용은 잠시 뒤에 알아보겠습니다.</p>
</div>
<h2 id="전체-값-얻기">전체 값 얻기</h2>
<p>전체 값을 얻는 방법은 다음과 같이 4가지 형태가 있습니다<sup id="fnref:at-or-asterisk" role="doc-noteref"><a href="#fn:at-or-asterisk" class="footnote" rel="footnote">4</a></sup>:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">${변수이름[@]}</code>: 인덱스가 들어갈 자리에 <code class="language-plaintext highlighter-rouge">@</code></li>
<li><code class="language-plaintext highlighter-rouge">${변수이름[*]}</code>: 인덱스가 들어갈 자리에 <code class="language-plaintext highlighter-rouge">*</code></li>
<li><code class="language-plaintext highlighter-rouge">"${변수이름[@]}"</code>: 큰따옴표(<code class="language-plaintext highlighter-rouge">""</code>)로 묶은 뒤 인덱스가 들어갈 자리에 <code class="language-plaintext highlighter-rouge">@</code></li>
<li><code class="language-plaintext highlighter-rouge">"${변수이름[*]}"</code>: 큰따옴표(<code class="language-plaintext highlighter-rouge">""</code>)로 묶은 뒤 인덱스가 들어갈 자리에 <code class="language-plaintext highlighter-rouge">*</code></li>
</ul>
<p>다음과 같은 배열이 존재한다고 했을 때:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ASDF[0]<span class="o">=</span><span class="s2">"A A"</span>
ASDF[1]<span class="o">=</span><span class="s2">"B B"</span>
</code></pre></div></div>
<p>각 형태 별로 다양한 결과를 보입니다:</p>
<table>
<thead>
<tr>
<th>명령어</th>
<th>중간 결과</th>
<th>출력</th>
<th>공백</th>
<th>위험성</th>
<th>특징</th>
</tr>
</thead>
<tbody>
<tr>
<td><code class="language-plaintext highlighter-rouge">echo ${ASDF[@]}</code></td>
<td><code class="language-plaintext highlighter-rouge">echo A A B B</code></td>
<td><code class="language-plaintext highlighter-rouge">A A B B</code></td>
<td>무시됨</td>
<td>⚠️ 존재</td>
<td>하나의 값에서 공백으로 구분된 각 부분을 개별적인 문자열로서 취급</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">echo ${ASDF[*]}</code></td>
<td><code class="language-plaintext highlighter-rouge">echo A A B B</code></td>
<td><code class="language-plaintext highlighter-rouge">A A B B</code></td>
<td>무시됨</td>
<td>⚠️ 존재</td>
<td>하나의 값에서 공백으로 구분된 각 부분을 개별적인 문자열로서 취급</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">echo "${ASDF[@]}"</code></td>
<td><code class="language-plaintext highlighter-rouge">echo "A A" "B B"</code></td>
<td><code class="language-plaintext highlighter-rouge">A A B B</code></td>
<td>보존됨</td>
<td>없음</td>
<td>각 값을 개별적인 문자열로서 취급</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">echo "${ASDF[*]}"</code></td>
<td><code class="language-plaintext highlighter-rouge">echo "A A B B"</code></td>
<td><code class="language-plaintext highlighter-rouge">A A B B</code></td>
<td>보존됨</td>
<td>⚠️ 존재</td>
<td>배열에 존재하는 모든 값을 하나의 문자열로서 취급</td>
</tr>
</tbody>
</table>
<div class="note">
<p><span>참고:</span></p>
<p>배시는 개별적으로 취급하는 문자열과 문자열 사이의 공백을 하나로 합치는 성질이 있습니다. 이 성질에 관해서는…</p>
</div>
<p><strong>결론 1:</strong> 배열의 값에 공백이 들어가지 않음을 보장할 수 있다면 <code class="language-plaintext highlighter-rouge">echo ${ASDF[@]}</code>나 <code class="language-plaintext highlighter-rouge">echo ${ASDF[*]}</code>을 사용해도 무방합니다.</p>
<p><strong>결론 2:</strong> 그렇지 않고 얼마든지 공백이 들어갈 수 있다면 각 값을 개별적인 문자열로서 취급하는 <code class="language-plaintext highlighter-rouge">echo "${ASDF[@]}"</code> 형태를 사용해야 합니다.</p>
<h2 id="값-변경-및-추가">값 변경 및 추가</h2>
<p><code class="language-plaintext highlighter-rouge">변수이름[인덱스]=값</code> 형태로 특정 인덱스에 존재하는 값을 변경합니다. 인덱스에 값이 존재하지 않는다면 새 값을 추가합니다<sup id="fnref:way-2" role="doc-noteref"><a href="#fn:way-2" class="footnote" rel="footnote">5</a></sup>.</p>
<div class="example">
<p><span>예시:</span></p>
<p>다음은 배열에 <code class="language-plaintext highlighter-rouge">100</code>, <code class="language-plaintext highlighter-rouge">200</code>, <code class="language-plaintext highlighter-rouge">300</code>을 넣은 뒤, <code class="language-plaintext highlighter-rouge">100</code>을 <code class="language-plaintext highlighter-rouge">99999</code>로 <strong>변경</strong>하는 코드입니다:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">ASDF</span><span class="o">=(</span>100 200 300<span class="o">)</span>
ASDF[0]<span class="o">=</span>99999
</code></pre></div> </div>
</div>
<div class="example">
<p><span>예시:</span></p>
<p>다음은 <code class="language-plaintext highlighter-rouge">Hello</code>라는 값 하나만 들어간 배열을 만든 뒤, 여기에 <code class="language-plaintext highlighter-rouge">Linus</code>와 <code class="language-plaintext highlighter-rouge">Torvalds</code>를 <strong>추가</strong>하는 코드입니다:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">ASDF</span><span class="o">=(</span>Hello<span class="o">)</span>
ASDF[1]<span class="o">=</span>Linus
ASDF[2]<span class="o">=</span>Torvalds
</code></pre></div> </div>
</div>
<div class="example">
<p><span>예시:</span></p>
<p><strong>공백을 포함한 문자열</strong>은 [배시]의 문법 상 큰따옴표나 작은따옴표로 묶어야 합니다:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ASDF[0]<span class="o">=</span><span class="s2">"Brian Fox"</span>
ASDF[1]<span class="o">=</span><span class="s1">'Stephen Bourne'</span>
</code></pre></div> </div>
</div>
<h2 id="특이한-성질-암시적-배열-생성">[특이한 성질] 암시적 배열 생성</h2>
<p>다른 프로그래밍 언어와 달리 [배시]는 배열이 존재하지 않아도 해당 변수에 값을 추가할 수 있습니다. <strong>변수가 배열이 아니라면 배시는 이를 배열로 만듭니다<sup id="fnref:created-automatically" role="doc-noteref"><a href="#fn:created-automatically" class="footnote" rel="footnote">6</a></sup>.</strong></p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ASDF[0]<span class="o">=</span>100 <span class="c"># 문제 없음</span>
ASDF[1]<span class="o">=</span>200
</code></pre></div></div>
<p>대부분의 프로그래밍 언어에서는 배열을 먼저 만들어두어야 값을 추가할 수 있습니다. 파이썬이라는 프로그래밍 언어의 경우 다음과 같이 배열을 먼저 선언해야 합니다:</p>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">asdf</span> <span class="o">=</span> <span class="p">[]</span> <span class="c1"># 배열 선언
</span><span class="n">asdf</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mi">100</span>
<span class="n">asdf</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="mi">200</span>
<span class="n">asdf</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="s">"Hello"</span>
</code></pre></div></div>
<h2 id="특이한-성질-순서대로-값이-존재하지-않아도-된다">[특이한 성질] 순서대로 값이 존재하지 않아도 된다</h2>
<p>배시는 다른 몇몇 프로그래밍 언어와 달리 배열에 값을 추가할 때 인덱스 0부터 연속적으로 값을 채워나가지 않아도 됩니다<sup id="fnref:nor-contiguously" role="doc-noteref"><a href="#fn:nor-contiguously" class="footnote" rel="footnote">7</a></sup>. 인덱스 0, 인덱스 1, 인덱스 2같이 연속적으로 채우지 않고 인덱스 0, 인덱스 5, 인덱스 123같이 불연속적으로 채울 수도 있습니다.</p>
<div class="example">
<p><span>예시:</span></p>
<p>다음은 인덱스 12345에 <code class="language-plaintext highlighter-rouge">99999</code>를 추가하는 코드입니다:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">ASDF</span><span class="o">=(</span>100 200 300<span class="o">)</span>
ASDF[12345]<span class="o">=</span>99999
</code></pre></div> </div>
</div>
<h2 id="특이한-성질-인덱스-명시-배열-생성">[특이한 성질] 인덱스 명시 배열 생성</h2>
<p>배열을 생성할 때 <code class="language-plaintext highlighter-rouge">배열이름=([인덱스]=값 [인덱스]=값)</code> 형태로 인덱스를 지정하여 생성하는 것도 가능합니다<sup id="fnref:subscript-is-supplied" role="doc-noteref"><a href="#fn:subscript-is-supplied" class="footnote" rel="footnote">8</a></sup>.</p>
<div class="example">
<p><span>예시:</span></p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">ASDF</span><span class="o">=([</span>10]<span class="o">=</span>100 <span class="o">[</span>0]<span class="o">=</span>200 <span class="o">[</span>2]<span class="o">=</span>300<span class="o">)</span>
</code></pre></div> </div>
</div>
<div class="note">
<p><span>참고:</span></p>
<p><code class="language-plaintext highlighter-rouge">(값1 [인덱스]=값2 값3)</code>처럼 인덱스를 지정하는 것과 지정하지 않는 것을 혼용할 수도 있습니다.</p>
<p>인덱스를 명시하지 않을 경우의 규칙은 언제나 <strong>왼쪽보다 1 더 큰 인덱스</strong>입니다. 다음 코드를 보세요:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># [3]=Global [4]=World</span>
<span class="nv">ASDF</span><span class="o">=([</span>10]<span class="o">=</span>Welcome <span class="o">[</span>2]<span class="o">=</span>Hello Global World <span class="o">[</span>50]<span class="o">=</span>Bye<span class="o">)</span>
<span class="nb">echo</span> <span class="k">${</span><span class="nv">ASDF</span><span class="p">[10]</span><span class="k">}</span>
<span class="nb">echo</span> <span class="k">${</span><span class="nv">ASDF</span><span class="p">[2]</span><span class="k">}</span>
<span class="nb">echo</span> <span class="k">${</span><span class="nv">ASDF</span><span class="p">[3]</span><span class="k">}</span>
<span class="nb">echo</span> <span class="k">${</span><span class="nv">ASDF</span><span class="p">[4]</span><span class="k">}</span>
<span class="nb">echo</span> <span class="k">${</span><span class="nv">ASDF</span><span class="p">[50]</span><span class="k">}</span>
</code></pre></div> </div>
<p><code class="language-plaintext highlighter-rouge">Global</code>은 <code class="language-plaintext highlighter-rouge">[2]=Hello</code>의 왼쪽에 있으므로 인덱스 3에 할당됩니다. <code class="language-plaintext highlighter-rouge">World</code>는 <code class="language-plaintext highlighter-rouge">Global</code>의 인덱스보다 1 더 큰 인덱스 4에 할당됩니다.</p>
<p>실행 결과:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Welcome
Hello
Global
World
Bye
</code></pre></div> </div>
</div>
<h2 id="배열-끝-값-추가">배열 끝 값 추가</h2>
<p><code class="language-plaintext highlighter-rouge">배열이름+=(값1 값2 값3)</code> 형태로 배열에 값을 추가합니다.</p>
<div class="example">
<p><span>예시:</span></p>
<p>다음은 배열을 생성한 뒤 배열 끝에 값을 추가하는 코드입니다:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">ASDF</span><span class="o">=(</span>Alpha Beta Theta Gamma<span class="o">)</span>
ASDF+<span class="o">=(</span>123<span class="o">)</span>
ASDF+<span class="o">=(</span>456 789<span class="o">)</span>
</code></pre></div> </div>
<p><code class="language-plaintext highlighter-rouge">123</code>과 <code class="language-plaintext highlighter-rouge">456</code>, <code class="language-plaintext highlighter-rouge">789</code>를 추가했습니다.</p>
</div>
<div class="example invalid">
<p><span>잘못된 예시:</span></p>
<p><strong>단 하나의 값</strong>을 추가한다고 해도 반드시 괄호를 붙여야 합니다. 다음 코드를 보세요:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">ASDF</span><span class="o">=(</span>Alpha Beta Theta Gamma<span class="o">)</span>
ASDF+<span class="o">=</span>123
<span class="nb">echo</span> <span class="k">${</span><span class="nv">ASDF</span><span class="p">[0]</span><span class="k">}</span>
</code></pre></div> </div>
<p>배시의 인덱스 없이 배열 이름만으로 배열에 접근하려 하면 배열의 첫 번째 값처럼 해석하는 성질로 인해 <code class="language-plaintext highlighter-rouge">ASDF+=123</code>은 <code class="language-plaintext highlighter-rouge">ASDF[0]+=123</code>같이 해석됩니다. <code class="language-plaintext highlighter-rouge">+=</code>을 배열이 아닌 변수에 사용하면 문자열을 붙이는 방식으로 동작합니다.</p>
<p>실행 결과:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Alpha123
</code></pre></div> </div>
<p><code class="language-plaintext highlighter-rouge">ASDF[0]</code>에 존재하는 <code class="language-plaintext highlighter-rouge">Alpha</code>와 <code class="language-plaintext highlighter-rouge">123</code>이 붙어 <code class="language-plaintext highlighter-rouge">Alpha123</code>이라는 결과가 나왔습니다.</p>
</div>
<h2 id="배열-합치기">배열 합치기</h2>
<p><code class="language-plaintext highlighter-rouge">${ASDF[@]}</code>를 응용해 두 개의 배열을 하나로 합치는(결합하는) 데 사용할 수도 있습니다.</p>
<div class="example">
<p><span>예시:</span></p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">AAA</span><span class="o">=(</span>123 456 789<span class="o">)</span>
<span class="nv">BBB</span><span class="o">=(</span>1 4 7<span class="o">)</span>
<span class="nv">CCC</span><span class="o">=(</span><span class="k">${</span><span class="nv">AAA</span><span class="p">[@]</span><span class="k">}</span> <span class="k">${</span><span class="nv">BBB</span><span class="p">[@]</span><span class="k">}</span><span class="o">)</span>
<span class="nb">echo</span> <span class="k">${</span><span class="nv">CCC</span><span class="p">[@]</span><span class="k">}</span>
</code></pre></div> </div>
<p>출력 결과:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>123 456 789 1 4 7
</code></pre></div> </div>
</div>
<p>앞서 말씀드렸듯 <strong>배열의 값이 공백을 포함할 수도 있다면</strong> <code class="language-plaintext highlighter-rouge">${변수이름[@]}</code> 대신 큰따옴표가 들어간 <code class="language-plaintext highlighter-rouge">"${변수이름[@]}"</code>를 사용해야 합니다. 그렇지 않을 경우 여러 공백을 하나로 묶는 배시의 성질로 인해 공백이 모두 무시되며 여러 가지 예기치 않은 결과가 일어날 수 있습니다.</p>
<div class="note">
<p><span>참고:</span></p>
<p>배시는 개별적으로 취급하는 문자열과 문자열 사이의 공백을 하나로 합치는 성질이 있습니다. 이 성질에 관해서는…</p>
</div>
<div class="example invalid">
<p><span>잘못된 예시:</span></p>
<p>다음은 배열의 값이 공백을 포함함에도 불구하고 <code class="language-plaintext highlighter-rouge">${변수이름[@]}</code>을 사용한 잘못된 코드입니다:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">AAA</span><span class="o">=(</span><span class="s2">"1 1"</span> <span class="s2">"2 2"</span><span class="o">)</span>
<span class="nv">BBB</span><span class="o">=(</span><span class="s2">"b b"</span> <span class="s2">"B B"</span><span class="o">)</span>
<span class="nv">CCC</span><span class="o">=(</span><span class="k">${</span><span class="nv">AAA</span><span class="p">[@]</span><span class="k">}</span> <span class="k">${</span><span class="nv">BBB</span><span class="p">[@]</span><span class="k">}</span><span class="o">)</span>
<span class="nb">echo</span> <span class="k">${</span><span class="nv">CCC</span><span class="p">[@]</span><span class="k">}</span>
</code></pre></div> </div>
<p>출력 결과:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1 1 2 2 b b B B
</code></pre></div> </div>
<p>여러 공백을 하나로 묶는 배시의 성질로 인해 공백이 모두 무시된 것을 확인할 수 있습니다.</p>
</div>
<div class="example">
<p><span>예시:</span></p>
<p>다음은 공백을 포함하는 값을 가진 배열에 대해, 제대로 <code class="language-plaintext highlighter-rouge">"${변수이름[@]}"</code>을 사용한 코드입니다:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">AAA</span><span class="o">=(</span><span class="s2">"1 1"</span> <span class="s2">"2 2"</span><span class="o">)</span>
<span class="nv">BBB</span><span class="o">=(</span><span class="s2">"b b"</span> <span class="s2">"B B"</span><span class="o">)</span>
<span class="nv">CCC</span><span class="o">=(</span><span class="s2">"</span><span class="k">${</span><span class="nv">AAA</span><span class="p">[@]</span><span class="k">}</span><span class="s2">"</span> <span class="s2">"</span><span class="k">${</span><span class="nv">BBB</span><span class="p">[@]</span><span class="k">}</span><span class="s2">"</span><span class="o">)</span>
<span class="nb">echo</span> <span class="s2">"</span><span class="k">${</span><span class="nv">CCC</span><span class="p">[@]</span><span class="k">}</span><span class="s2">"</span>
</code></pre></div> </div>
<p>출력 결과:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1 1 2 2 b b B B
</code></pre></div> </div>
<p>예상 대로 공백이 출력됨을 확인할 수 있습니다.</p>
</div>
<h2 id="배열-크기-얻기">배열 크기 얻기</h2>
<p><code class="language-plaintext highlighter-rouge">${#변수이름[@]}</code> (<code class="language-plaintext highlighter-rouge">변수이름</code> 앞에 <code class="language-plaintext highlighter-rouge">#</code>이 있습니다) 형태로 배열에 들어 있는 값의 개수를 구합니다<sup id="fnref:length-of-array" role="doc-noteref"><a href="#fn:length-of-array" class="footnote" rel="footnote">9</a></sup>.</p>
<div class="example">
<p><span>예시:</span></p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">ASDF</span><span class="o">=(</span>123 456 789<span class="o">)</span>
<span class="nv">ZXCV</span><span class="o">=(</span>A B C D E F G<span class="o">)</span>
<span class="nb">echo</span> <span class="k">${#</span><span class="nv">ASDF</span><span class="p">[@]</span><span class="k">}</span>
<span class="nb">echo</span> <span class="k">${#</span><span class="nv">ZXCV</span><span class="p">[@]</span><span class="k">}</span>
</code></pre></div> </div>
<p>실행 결과:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>3
7
</code></pre></div> </div>
</div>
<p>배열의 크기는 시작 인덱스부터 마지막 인덱스까지의 길이를 구하는 것이 <strong>아닙니다.</strong> 순전히 배열에 들어 있는 값의 개수를 구합니다.</p>
<div class="example">
<p><span>예시:</span></p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">ASDF</span><span class="o">=(</span>123 456 <span class="o">[</span>100]<span class="o">=</span>789<span class="o">)</span>
<span class="nb">echo</span> <span class="k">${#</span><span class="nv">ASDF</span><span class="p">[@]</span><span class="k">}</span>
</code></pre></div> </div>
<p>실행 결과:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>3
</code></pre></div> </div>
<p>시작 인덱스부터 마지막 인덱스까지의 길이를 구하는 것이었다면 <code class="language-plaintext highlighter-rouge">100</code>을 출력해야 합니다. <code class="language-plaintext highlighter-rouge">3</code>을 출력하는 것으로 보아 배열에 들어있는 값의 개수를 구한다는 것을 알 수 있습니다.</p>
</div>
<div class="note">
<p><span>참고:</span></p>
<p><code class="language-plaintext highlighter-rouge">${#변수이름[1]}</code>과 같이 길이를 구할 때 <code class="language-plaintext highlighter-rouge">@</code> 대신 인덱스를 넣을 경우, 배열에 존재하는 값의 개수를 구하는 게 아니라 해당 값의 문자열 길이를 얻습니다:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">ASDF</span><span class="o">=(</span>Short LongLongLong <span class="s2">" "</span><span class="o">)</span>
<span class="nb">echo</span> <span class="k">${#</span><span class="nv">ASDF</span><span class="p">[0]</span><span class="k">}</span>
<span class="nb">echo</span> <span class="k">${#</span><span class="nv">ASDF</span><span class="p">[1]</span><span class="k">}</span>
<span class="nb">echo</span> <span class="k">${#</span><span class="nv">ASDF</span><span class="p">[2]</span><span class="k">}</span>
</code></pre></div> </div>
<p>실행 결과:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>5
12
2
</code></pre></div> </div>
<p><code class="language-plaintext highlighter-rouge">Short</code>는 5글자, <code class="language-plaintext highlighter-rouge">LongLongLong</code>은 12글자, <code class="language-plaintext highlighter-rouge">" "</code>은 2글자이므로 각각 <code class="language-plaintext highlighter-rouge">5</code>, <code class="language-plaintext highlighter-rouge">12</code>, <code class="language-plaintext highlighter-rouge">2</code>가 출력되었습니다.</p>
</div>
<h2 id="배열-전체-인덱스-얻기">배열 전체 인덱스 얻기</h2>
<p><code class="language-plaintext highlighter-rouge">${!변수이름[@]}</code> (<code class="language-plaintext highlighter-rouge">변수이름</code> 앞에 <code class="language-plaintext highlighter-rouge">!</code>가 있습니다) 형태로 배열이 가진 전체 인덱스 목록을 얻을 수 있습니다<sup id="fnref:obtain-the-keys" role="doc-noteref"><a href="#fn:obtain-the-keys" class="footnote" rel="footnote">10</a></sup>.</p>
<div class="example">
<p><span>예시:</span></p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">ASDF</span><span class="o">=(</span>123 456 789 <span class="o">[</span>777]<span class="o">=</span>TripleSeven<span class="o">)</span>
<span class="nb">echo</span> <span class="k">${</span><span class="p">!ASDF[@]</span><span class="k">}</span>
</code></pre></div> </div>
<p>출력 결과:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0 1 2 777
</code></pre></div> </div>
</div>
<h2 id="배열-순회">배열 순회</h2>
<p><code class="language-plaintext highlighter-rouge">for</code> 명령어를 통해 배열의 값 하나 하나에 동일한 연산을 수행할 수 있습니다. 값을 대량으로 변경하거나 모든 값을 출력할 때 사용합니다.</p>
<div class="example">
<p><span>예시:</span></p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">ASDF</span><span class="o">=(</span>111 222 333<span class="o">)</span>
<span class="k">for </span>V <span class="k">in</span> <span class="s2">"</span><span class="k">${</span><span class="nv">ASDF</span><span class="p">[@]</span><span class="k">}</span><span class="s2">"</span>
<span class="k">do
</span><span class="nb">echo</span> <span class="nv">$V</span>
<span class="k">done</span>
</code></pre></div> </div>
<p>출력 결과:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>111
222
333
</code></pre></div> </div>
</div>
<div class="note">
<p><span>참고:</span></p>
<p><code class="language-plaintext highlighter-rouge">for</code> 명령어에 관한 자세한 내용은…</p>
</div>
<h2 id="값-제거">값 제거</h2>
<p><code class="language-plaintext highlighter-rouge">unset</code> 명령어를 통해 배열의 값을 제거할 수 있습니다. <code class="language-plaintext highlighter-rouge">unset 변수이름[0]</code>, <code class="language-plaintext highlighter-rouge">unset 변수이름[1]</code> 형태입니다. <code class="language-plaintext highlighter-rouge">unset 변수이름</code> 형태로 배열 전체를 제거할 수도 있습니다<sup id="fnref:unset" role="doc-noteref"><a href="#fn:unset" class="footnote" rel="footnote">11</a></sup>.</p>
<div class="example">
<p><span>예시:</span></p>
<p>다음 코드는 배열의 값을 하나 제거합니다:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>A[5]<span class="o">=</span>123
<span class="nb">unset </span>A[5]
</code></pre></div> </div>
</div>
<div class="example">
<p><span>예시:</span></p>
<p>다음 코드는 배열 전체를 제거합니다:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">A</span><span class="o">=(</span>123 456 789<span class="o">)</span>
<span class="nb">unset </span>A
</code></pre></div> </div>
</div>
<h3 id="값-제거-vs-빈-문자열-대입">값 제거 vs 빈 문자열 대입</h3>
<p>배시는 배열에 빈 문자열(길이가 0인 문자열)이 들어가는 것을 정상적인 값의 설정으로 취급합니다<sup id="fnref:null-string" role="doc-noteref"><a href="#fn:null-string" class="footnote" rel="footnote">12</a></sup>.</p>
<div class="example">
<p><span>예시:</span></p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">ASDF</span><span class="o">=(</span>123 456 789<span class="o">)</span>
ASDF[2]<span class="o">=</span> <span class="c"># 빈 문자열 대입</span>
</code></pre></div> </div>
</div>
<p>그러므로 빈 문자열을 대입하는 것은 배열에 있는 값을 <strong>제거</strong>하는 게 아니라 그저 빈 문자열로 덮어쓸 뿐입니다.</p>
<div class="example">
<p><span>예시:</span></p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">ASDF</span><span class="o">=(</span>123 456 789<span class="o">)</span>
<span class="nb">echo</span> <span class="s2">"Count:"</span> <span class="k">${#</span><span class="nv">ASDF</span><span class="p">[@]</span><span class="k">}</span>
<span class="nb">echo</span> <span class="s2">"Get all:"</span> <span class="k">${</span><span class="nv">ASDF</span><span class="p">[@]</span><span class="k">}</span>
ASDF[1]<span class="o">=</span>
<span class="nb">echo</span> <span class="s2">"Count:"</span> <span class="k">${#</span><span class="nv">ASDF</span><span class="p">[@]</span><span class="k">}</span>
<span class="nb">echo</span> <span class="s2">"Get all:"</span> <span class="k">${</span><span class="nv">ASDF</span><span class="p">[@]</span><span class="k">}</span>
</code></pre></div> </div>
<p>출력 결과:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Count: 3
Get all: 123 456 789
Count: 3
Get all: 123 789
</code></pre></div> </div>
<p>값이 제거되지 않고 그대로 남아있는 것을 확인할 수 있습니다.</p>
</div>
<h2 id="음수-인덱스">음수 인덱스</h2>
<p><code class="language-plaintext highlighter-rouge">ASDF[-1]</code>과 같이 인덱스 자리에 음수를 사용하면 배열이 가진 마지막 인덱스에서부터 거꾸로 접근합니다. <code class="language-plaintext highlighter-rouge">ASDF[-1]</code>이 마지막 인덱스입니다<sup id="fnref:negative-number" role="doc-noteref"><a href="#fn:negative-number" class="footnote" rel="footnote">13</a></sup>.</p>
<div class="example">
<p><span>예시:</span></p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">ASDF</span><span class="o">=(</span>100 200 300<span class="o">)</span>
<span class="nb">echo</span> <span class="k">${</span><span class="nv">ASDF</span><span class="p">[-1]</span><span class="k">}</span> <span class="c"># ASDF[2]와 같음</span>
<span class="nb">echo</span> <span class="k">${</span><span class="nv">ASDF</span><span class="p">[-2]</span><span class="k">}</span> <span class="c"># ASDF[1]과 같음</span>
<span class="nb">echo</span> <span class="k">${</span><span class="nv">ASDF</span><span class="p">[-3]</span><span class="k">}</span> <span class="c"># ASDF[0]과 같음</span>
</code></pre></div> </div>
<p>출력 결과:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>300
200
100
</code></pre></div> </div>
</div>
<div class="example">
<p><span>예시:</span></p>
<p>음수 인덱스는 가장 마지막 인덱스부터 거꾸로 접근하기 때문에, 값이 들어있지 않은 인덱스는 아무런 값도 출력하지 않습니다.</p>
<p>다음은 인덱스 9부터 거꾸로 값들을 출력하는 코드입니다:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">ASDF</span><span class="o">=(</span>100 200 300<span class="o">)</span>
ASDF[9]<span class="o">=</span>1000
<span class="nb">echo</span> <span class="k">${</span><span class="nv">ASDF</span><span class="p">[-1]</span><span class="k">}</span> <span class="c"># ASDF[9]와 같음</span>
<span class="nb">echo</span> <span class="k">${</span><span class="nv">ASDF</span><span class="p">[-2]</span><span class="k">}</span> <span class="c"># ASDF[8]과 같음</span>
<span class="nb">echo</span> <span class="k">${</span><span class="nv">ASDF</span><span class="p">[-3]</span><span class="k">}</span> <span class="c"># ASDF[7]과 같음</span>
</code></pre></div> </div>
<p>출력 결과:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1000
</code></pre></div> </div>
<p>보시는 바와 같이 <code class="language-plaintext highlighter-rouge">${ASDF[-2]}</code>, <code class="language-plaintext highlighter-rouge">${ASDF[-3]}</code>은 아무 것도 출력되지 않은 것을 확인할 수 있습니다.</p>
</div>
<h2 id="특이한-성질-배열-이름-자체로-접근">[특이한 성질] 배열 이름 자체로 접근</h2>
<p>배시에서는 인덱스 없이 배열 이름만으로 배열에 접근하려 하면 <strong>배열의 첫 번째 값처럼 취급합니다<sup id="fnref:without-a-subscript" role="doc-noteref"><a href="#fn:without-a-subscript" class="footnote" rel="footnote">14</a></sup>.</strong> <code class="language-plaintext highlighter-rouge">ASDF=100</code>을 <code class="language-plaintext highlighter-rouge">ASDF[0]=100</code>처럼 처리한다는 소리죠.</p>
<table>
<thead>
<tr>
<th>입력 코드</th>
<th>처리 코드</th>
</tr>
</thead>
<tbody>
<tr>
<td><code class="language-plaintext highlighter-rouge">ASDF=100</code></td>
<td><code class="language-plaintext highlighter-rouge">ASDF[0]=100</code></td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">echo ${ASDF}</code></td>
<td><code class="language-plaintext highlighter-rouge">echo ${ASDF[0]}</code></td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">ASDF=100</code><br /><code class="language-plaintext highlighter-rouge">ASDF[1]=200</code></td>
<td><code class="language-plaintext highlighter-rouge">ASDF[0]=100</code><br /><code class="language-plaintext highlighter-rouge">ASDF[1]=200</code></td>
</tr>
</tbody>
</table>
<p>배열의 값을 수정할 때, 배열의 값을 출력할 때 모두 적용됩니다. 심지어는 배열이 아직 생성되지 않았을 때의 값 또한 배열의 첫 번째 값처럼 취급합니다.</p>
<h2 id="특이한-성질-명시적-선언">[특이한 성질] 명시적 선언</h2>
<p><code class="language-plaintext highlighter-rouge">declare</code> 명령어와 <code class="language-plaintext highlighter-rouge">-a</code> 옵션을 통해 특정 변수를 배열로 선언할 수 있습니다. 이를 <strong>명시적 선언</strong>이라 합니다. <code class="language-plaintext highlighter-rouge">-a</code>는 “A”rray의 “A”에서 따왔습니다.</p>
<div class="example">
<p><span>예시:</span></p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">declare</span> <span class="nt">-a</span> ASDF
</code></pre></div> </div>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 선언과 동시에 초기화도 가능</span>
<span class="nb">declare</span> <span class="nt">-a</span> <span class="nv">QWER</span><span class="o">=(</span>100 200 300<span class="o">)</span>
</code></pre></div> </div>
</div>
<div class="note">
<p><span>참고:</span></p>
<p>명시적으로 배열을 선언하면 컴퓨터로 하여금 해당 변수를 최적화할 수 있는 여지를 줍니다. 이로 인해 속도가 향상될 수도 있습니다<sup id="fnref:may-speed-up" role="doc-noteref"><a href="#fn:may-speed-up" class="footnote" rel="footnote">15</a></sup>.</p>
</div>
<h2 id="연관-배열-인덱스-배열-키">연관 배열, 인덱스 배열, 키</h2>
<p>2009년 출시된 배시 4.0부터 <strong>연관 배열</strong>(associative array)이라는 새로운 문법이 추가되었습니다<sup id="fnref:new-goodies" role="doc-noteref"><a href="#fn:new-goodies" class="footnote" rel="footnote">16</a></sup>.</p>
<p>기존 배열과 달리, 연관 배열에는 인덱스가 들어갈 자리에 <strong>문자열</strong>이 들어갑니다. 이 문자열을 가리켜 <strong>키</strong>라고 합니다.</p>
<p>기존의 인덱스를 사용하던 배열은 연관 배열과 구분할 필요가 있으므로 따로 <strong>인덱스 배열</strong>(indexed array)이라고 부릅니다.</p>
<h2 id="인덱스-배열-vs-연관-배열">인덱스 배열 vs 연관 배열</h2>
<p>연관 배열의 기본적인 동작 방식은 인덱스 배열과 매우 유사합니다. 다음은 인덱스 배열과 연관 배열에서 제공하는 기능을 비교하는 표입니다:</p>
<table>
<thead>
<tr>
<th> </th>
<th>변경</th>
<th>얻기</th>
<th>전체 얻기</th>
<th>배열 크기</th>
<th>제거</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>인덱스 배열</strong></td>
<td><code class="language-plaintext highlighter-rouge">ASDF[3]=Hello</code></td>
<td><code class="language-plaintext highlighter-rouge">${ASDF[3]}</code></td>
<td><code class="language-plaintext highlighter-rouge">${ASDF[@]}</code></td>
<td><code class="language-plaintext highlighter-rouge">${#ASDF[@]}</code></td>
<td><code class="language-plaintext highlighter-rouge">unset ASDF[3]</code></td>
</tr>
<tr>
<td><strong>연관 배열</strong></td>
<td><code class="language-plaintext highlighter-rouge">ASDF[greeting]=Hello</code></td>
<td><code class="language-plaintext highlighter-rouge">${ASDF[greeting]}</code></td>
<td><code class="language-plaintext highlighter-rouge">${ASDF[@]}</code></td>
<td><code class="language-plaintext highlighter-rouge">${#ASDF[@]}</code></td>
<td><code class="language-plaintext highlighter-rouge">unset ASDF[greeting]</code></td>
</tr>
</tbody>
</table>
<p>다만 연관 배열은 인덱스 배열과는 다른 두 가지 차이점이 존재합니다:</p>
<h3 id="명시적-선언-필수">명시적 선언 필수</h3>
<p>연관 배열과 인덱스 배열에는 한 가지 중요한 차이점이 있습니다. 명시적 선언을 해도 되고 안 해도 되는 인덱스 배열과 달리, 연관 배열을 사용하기 위해서는 반드시 <strong>명시적으로 선언</strong>해야만 합니다<sup id="fnref:the-subscript-is-required" role="doc-noteref"><a href="#fn:the-subscript-is-required" class="footnote" rel="footnote">17</a></sup>. 다음과 같이 선언합니다:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">declare</span> <span class="nt">-A</span> 변수이름
</code></pre></div></div>
<p>연관 배열 역시 인데스 배열처럼 배열 생성과 함께 값을 추가할 수 있습니다. 다만 인덱스 배열과 달리 <strong>키를 자동으로 지정해주지 않습니다.</strong> 그러므로 다음과 같이 언제나 키를 명시해야 합니다 (앞서 설명한 ‘인덱스 배열을 생성하면서 인덱스를 명시하는 방법’과 동일한 형태입니다):</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">declare</span> <span class="nt">-A</span> 변수이름<span class="o">=([</span>키]<span class="o">=</span>값 <span class="o">[</span>키]<span class="o">=</span>값 <span class="o">[</span>키]<span class="o">=</span>값<span class="o">)</span>
</code></pre></div></div>
<p>키를 지정하지 않으면 오류가 발생합니다.</p>
<div class="example invalid">
<p><span>잘못된 예시:</span></p>
<p>다음은 연관 배열을 생성하면서 키를 명시하지 않는 잘못된 코드입니다:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">declare</span> <span class="nt">-A</span> <span class="nv">ASDF</span><span class="o">=(</span>123 456 789<span class="o">)</span>
</code></pre></div> </div>
<p>출력 결과:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-bash: ASDF: 123: must use subscript when assigning associative array
-bash: ASDF: 456: must use subscript when assigning associative array
-bash: ASDF: 789: must use subscript when assigning associative array
</code></pre></div> </div>
<p>예상대로 오류가 발생한 것을 확인할 수 있습니다.</p>
</div>
<h3 id="키에는-순서가-없다">키에는 순서가 없다</h3>
<p>인덱스 배열의 인덱스와 달리, 연관 배열의 키에는 <strong>순서가 없습니다.</strong> 그렇기 때문에 전체 값을 얻을 때에도 어떤 순서로 얻게 될지 알 수 없습니다.</p>
<div class="example">
<p><span>예시:</span></p>
<p>다음은 연관 배열의 키에 순서가 없음을 확인하는 코드입니다:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">declare</span> <span class="nt">-A</span> <span class="nv">ASDF</span><span class="o">=([</span>aaa]<span class="o">=</span>111 <span class="o">[</span>bbb]<span class="o">=</span>222 <span class="o">[</span>ccc]<span class="o">=</span>333 <span class="o">[</span>ddd]<span class="o">=</span>444 <span class="o">[</span>eee]<span class="o">=</span>555<span class="o">)</span>
<span class="nb">echo</span> <span class="k">${</span><span class="nv">ASDF</span><span class="p">[@]</span><span class="k">}</span>
</code></pre></div> </div>
<p>출력 결과:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>111 555 333 222 444
</code></pre></div> </div>
<p>연관 배열의 키에는 순서가 없으므로 출력 결과는 실행 환경마다 다를 수 있습니다.</p>
</div>
<h2 id="2차원-배열">2차원 배열</h2>
<p>배시는 공식적으로 1차원 배열만을 지원합니다<sup id="fnref:one-dimensional" role="doc-noteref"><a href="#fn:one-dimensional" class="footnote" rel="footnote">18</a></sup>. 2차원 배열을 위한 문법은 제공하지 않습니다.</p>
<h2 id="참고">참고</h2>
<ul>
<li><a href="https://www.gnu.org/software/bash/manual/html_node/Arrays.html">Arrays - Bash Reference Manual</a></li>
<li><a href="https://tldp.org/LDP/abs/html/arrays.html">Arrays - Advanced Bash-Scripting Guide</a></li>
</ul>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:way-1" role="doc-endnote">
<p><a href="https://www.gnu.org/software/bash/manual/html_node/Arrays.html">Arrays - Bash Reference Manual</a></p>
<blockquote>
<p>Arrays are assigned to using compound assignments of the form</p>
</blockquote>
<blockquote>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">name</span><span class="o">=(</span>value1 value2 … <span class="o">)</span>
</code></pre></div> </div>
</blockquote>
<p><a href="#fnref:way-1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:starts-at-zero" role="doc-endnote">
<p><a href="https://www.gnu.org/software/bash/manual/html_node/Arrays.html">Arrays - Bash Reference Manual</a></p>
<blockquote>
<p>Indexing starts at zero.</p>
</blockquote>
<p><a href="#fnref:starts-at-zero" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:braces-are-required" role="doc-endnote">
<p><a href="https://www.gnu.org/software/bash/manual/html_node/Arrays.html">Arrays - Bash Reference Manual</a></p>
<blockquote>
<p>The braces are required to avoid conflicts with the shell’s filename expansion operators.</p>
</blockquote>
<p><a href="#fnref:braces-are-required" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:at-or-asterisk" role="doc-endnote">
<p><a href="https://www.gnu.org/software/bash/manual/html_node/Arrays.html">Arrays - Bash Reference Manual</a></p>
<blockquote>
<p>If the subscript is ‘@’ or ‘<em>’, the word expands to all members of the array name. These subscripts differ only when the word appears within double quotes. If the word is double-quoted, ${name[</em>]} expands to a single word with the value of each array member separated by the first character of the IFS variable, and ${name[@]} expands each element of name to a separate word. When there are no array members, ${name[@]} expands to nothing.</p>
</blockquote>
<p><a href="#fnref:at-or-asterisk" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:way-2" role="doc-endnote">
<p><a href="https://www.gnu.org/software/bash/manual/html_node/Arrays.html">Arrays - Bash Reference Manual</a></p>
<blockquote>
<p>each value is of the form <code class="language-plaintext highlighter-rouge">[subscript]=string</code>.</p>
</blockquote>
<p><a href="#fnref:way-2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:created-automatically" role="doc-endnote">
<p><a href="https://www.gnu.org/software/bash/manual/html_node/Arrays.html">Arrays - Bash Reference Manual</a></p>
<blockquote>
<p>An indexed array is created automatically if any variable is assigned to using the syntax</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>name[subscript]=value
</code></pre></div> </div>
</blockquote>
<p><a href="#fnref:created-automatically" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:nor-contiguously" role="doc-endnote">
<p><a href="https://www.gnu.org/software/bash/manual/html_node/Arrays.html">Arrays - Bash Reference Manual</a></p>
<blockquote>
<p>nor any requirement that members be indexed or assigned contiguously.</p>
</blockquote>
<p><a href="#fnref:nor-contiguously" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:subscript-is-supplied" role="doc-endnote">
<p><a href="https://www.gnu.org/software/bash/manual/html_node/Arrays.html">Arrays - Bash Reference Manual</a></p>
<blockquote>
<p>where each value is of the form [subscript]=string. … if the optional subscript is supplied, that index is assigned to;</p>
</blockquote>
<p><a href="#fnref:subscript-is-supplied" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:length-of-array" role="doc-endnote">
<p><a href="https://www.gnu.org/software/bash/manual/html_node/Arrays.html">Arrays - Bash Reference Manual</a></p>
<blockquote>
<p>${#name[subscript]} expands to the length of ${name[subscript]}. If subscript is ‘@’ or ‘*’, the expansion is the number of elements in the array.</p>
</blockquote>
<p><a href="#fnref:length-of-array" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:obtain-the-keys" role="doc-endnote">
<p><a href="https://www.gnu.org/software/bash/manual/html_node/Arrays.html">Arrays - Bash Reference Manual</a></p>
<blockquote>
<p>It is possible to obtain the keys (indices) of an array as well as the values. ${!name[@]} and ${!name[<em>]} expand to the indices assigned in array variable name. The treatment when in double quotes is similar to the expansion of the special parameters ‘@’ and ‘</em>’ within double quotes.</p>
</blockquote>
<p><a href="#fnref:obtain-the-keys" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:unset" role="doc-endnote">
<p><a href="https://www.gnu.org/software/bash/manual/html_node/Arrays.html">Arrays - Bash Reference Manual</a></p>
<blockquote>
<p>The unset builtin is used to destroy arrays. <code class="language-plaintext highlighter-rouge">unset name[subscript]</code> destroys the array element at index subscript. Negative subscripts to indexed arrays are interpreted as described above. Unsetting the last element of an array variable does not unset the variable. <code class="language-plaintext highlighter-rouge">unset name</code>, where name is an array, removes the entire array.</p>
</blockquote>
<p><a href="#fnref:unset" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:null-string" role="doc-endnote">
<p><a href="https://www.gnu.org/software/bash/manual/html_node/Arrays.html">Arrays - Bash Reference Manual</a></p>
<blockquote>
<p>The null string is a valid value.</p>
</blockquote>
<p><a href="#fnref:null-string" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:negative-number" role="doc-endnote">
<p><a href="https://www.gnu.org/software/bash/manual/html_node/Arrays.html">Arrays - Bash Reference Manual</a></p>
<blockquote>
<p>When assigning to an indexed array, if name is subscripted by a negative number, that number is interpreted as relative to one greater than the maximum index of name, so negative indices count back from the end of the array, and an index of -1 references the last element.</p>
</blockquote>
<p><a href="#fnref:negative-number" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:without-a-subscript" role="doc-endnote">
<p><a href="https://www.gnu.org/software/bash/manual/html_node/Arrays.html">Arrays - Bash Reference Manual</a></p>
<blockquote>
<p>Referencing an array variable without a subscript is equivalent to referencing with a subscript of 0.</p>
</blockquote>
<p><a href="#fnref:without-a-subscript" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:may-speed-up" role="doc-endnote">
<p><a href="https://tldp.org/LDP/abs/html/arrays.html">Arrays - Advanced Bash-Scripting Guide</a></p>
<blockquote>
<p>Adding a superfluous declare -a statement to an array declaration may speed up execution of subsequent operations on the array.</p>
</blockquote>
<p><a href="#fnref:may-speed-up" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:new-goodies" role="doc-endnote">
<p><a href="https://tldp.org/LDP/abs/html/bashver4.html">Bash, version 4 - Advanced Bash-Scripting Guide</a></p>
<blockquote>
<p>Chet Ramey announced Version 4 of Bash on the 20th of February, 2009.
Among the new goodies:</p>
<ul>
<li>Associative arrays.</li>
</ul>
</blockquote>
<p><a href="#fnref:new-goodies" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:the-subscript-is-required" role="doc-endnote">
<p><a href="https://www.gnu.org/software/bash/manual/html_node/Arrays.html">Arrays - Bash Reference Manual</a></p>
<blockquote>
<p>When assigning to an associative array, the subscript is required.</p>
</blockquote>
<p><a href="#fnref:the-subscript-is-required" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:one-dimensional" role="doc-endnote">
<p><a href="https://www.gnu.org/software/bash/manual/html_node/Arrays.html">Arrays - Bash Reference Manual</a></p>
<blockquote>
<p>Bash provides one-dimensional indexed and associative array variables.</p>
</blockquote>
<p><a href="#fnref:one-dimensional" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>방성범 (Bang Seongbeom)배열을 통해 여러 값을 효과적으로 다룰 수 있습니다.source, . (점): 셸 스크립트 파일 실행2020-04-29T00:00:00+00:002020-04-29T00:00:00+00:00https://www.bangseongbeom.com/source-dot<p><a href="https://linux.die.net/man/1/bash">배시</a>(Bash)에서 <code class="language-plaintext highlighter-rouge">source</code>와 <code class="language-plaintext highlighter-rouge">.</code>은 동일한 기능을 하는 명령어로, 셸 스크립트 파일을 실행하는 데 사용합니다.</p>
<h2 id="소개">소개</h2>
<p><code class="language-plaintext highlighter-rouge">source</code>와 <code class="language-plaintext highlighter-rouge">.</code>은 파일을 실행하는 명령어입니다. 좀 이상하게 생기긴 했지만 <code class="language-plaintext highlighter-rouge">.</code>도 엄연한 하나의 명령어입니다.</p>
<div class="example">
<p><span>예시:</span></p>
<p>다음과 같이 셸 스크립트 파일을 만듭니다. 여기서는 <code class="language-plaintext highlighter-rouge">asdf.sh</code>라는 이름으로 만들겠습니다:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">echo </span>Hello
</code></pre></div> </div>
<p>다음 명령어로 셸 스크립트를 실행합니다:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">source </span>asdf.sh
<span class="nb">.</span> asdf.sh
</code></pre></div> </div>
<p><code class="language-plaintext highlighter-rouge">source</code>와 <code class="language-plaintext highlighter-rouge">.</code> 모두 <code class="language-plaintext highlighter-rouge">asdf.sh</code>를 실행합니다. <code class="language-plaintext highlighter-rouge">Hello</code>가 두 번 출력되는 것을 확인할 수 있습니다.</p>
</div>
<h2 id="source-vs-bash"><code class="language-plaintext highlighter-rouge">source</code> vs <code class="language-plaintext highlighter-rouge">bash</code></h2>
<p><code class="language-plaintext highlighter-rouge">source</code>(또는 <code class="language-plaintext highlighter-rouge">.</code>)와 유사한 명령어로 <code class="language-plaintext highlighter-rouge">bash</code>가 있습니다. 인자로 파일 이름을 주면 셸 스크립트 파일을 실행합니다. 다음과 같이 사용 방법도 같습니다:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">source </span>asdf.sh
bash asdf.h
</code></pre></div></div>
<p>이 둘의 차이가 무엇일까요?</p>
<h3 id="역할">역할</h3>
<p>두 명령어 모두 셸 스크립트 파일을 실행하는 데 사용할 수 있습니다.</p>
<h3 id="사용-셸">사용 셸</h3>
<p>반면 이 둘은 새로운 셸을 만드는 데 있어 차이를 보입니다.</p>
<p><code class="language-plaintext highlighter-rouge">source</code>: <strong>현재 셸</strong>에서 스크립트 파일을 실행합니다.</p>
<p><code class="language-plaintext highlighter-rouge">bash</code>: <strong>새로운 셸</strong>을 만들어 스크립트 파일을 실행합니다. 셸에서 선언된 변수나 현재 작업 디렉터리(<code class="language-plaintext highlighter-rouge">cd</code>를 통해 이동할 수 있는 그것)의 위치는 해당 셸에서만 유효하며, 셸이 종료되면 모두 사라집니다.</p>
<p>이로 인해 몇 가지 차이가 발생합니다. 대표적으로 <strong>환경 변수 유효성</strong>과 <strong><code class="language-plaintext highlighter-rouge">cd</code>로 인한 경로 유지</strong>에 관한 차이가 있습니다.</p>
<h3 id="환경-변수-유효성">환경 변수 유효성</h3>
<p><code class="language-plaintext highlighter-rouge">source</code>: 스크립트 안에서 선언한 환경 변수를 스크립트 바깥에서 접근할 수 있습니다.</p>
<p><code class="language-plaintext highlighter-rouge">bash</code>: 스크립트 안에서 선언한 환경 변수는 스크립트 바깥에서 접근할 수 없으며 스크립트가 끝나면 그대로 소멸합니다. 스크립트 바깥에서의 접근을 원한다면 <code class="language-plaintext highlighter-rouge">export</code> 명령어를 통해야 합니다.</p>
<div class="example">
<p><span>예시:</span></p>
<p>먼저 <code class="language-plaintext highlighter-rouge">asdf.sh</code> 파일을 만듭니다:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">TEST_VALUE</span><span class="o">=</span>123
<span class="nb">echo</span> <span class="s2">"In file: </span><span class="nv">$TEST_VALUE</span><span class="s2">"</span>
</code></pre></div> </div>
<p>이제 이 파일을 다음처럼 실행합니다 (반드시 <code class="language-plaintext highlighter-rouge">bash</code>가 <code class="language-plaintext highlighter-rouge">source</code>보다 앞서 등장해야 합니다):</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bash asdf.sh
<span class="nb">echo</span> <span class="s2">"bash asdf.sh: </span><span class="nv">$TEST_VALUE</span><span class="s2">"</span>
<span class="nb">source </span>asdf.sh
<span class="nb">echo</span> <span class="s2">"source asdf.sh: </span><span class="nv">$TEST_VALUE</span><span class="s2">"</span>
</code></pre></div> </div>
<p>출력 결과:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>In file: 123
bash asdf.sh:
In file: 123
source asdf.sh: 123
</code></pre></div> </div>
<p><code class="language-plaintext highlighter-rouge">bash</code>는 <code class="language-plaintext highlighter-rouge">123</code>이 출력되지 않은 반면, <code class="language-plaintext highlighter-rouge">source</code>는 <code class="language-plaintext highlighter-rouge">123</code>이 출력되었습니다. 여기서 우리는 <code class="language-plaintext highlighter-rouge">bash</code>로 인해 실행한 <code class="language-plaintext highlighter-rouge">asdf.sh</code>의 경우, 실행이 끝나면 더 이상 변수가 유효하지 않음을 확인할 수 있습니다.</p>
</div>
<h3 id="cd로-인한-경로-유지"><code class="language-plaintext highlighter-rouge">cd</code>로 인한 경로 유지</h3>
<p><code class="language-plaintext highlighter-rouge">source</code>: 스크립트 안에서 <code class="language-plaintext highlighter-rouge">cd</code>를 실행한 결과가 그대로 유지됩니다.</p>
<p><code class="language-plaintext highlighter-rouge">bash</code>: 스크립트 안에서의 <code class="language-plaintext highlighter-rouge">cd</code>는 스크립트 안에서 유지되지만, 스크립트 바깥에서는 유지되지 않습니다.</p>
<div class="example">
<p><span>예시:</span></p>
<p>먼저 <code class="language-plaintext highlighter-rouge">asdf.sh</code> 파일을 만듭니다. 이 예시에서는 <code class="language-plaintext highlighter-rouge">/home/ubuntu</code>에 만들겠습니다. (<a href="https://linux.die.net/man/1/pwd"><code class="language-plaintext highlighter-rouge">pwd</code></a>는 현재 작업 디렉터리를 출력하는 명령어입니다):</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">echo</span> <span class="s2">"In file 1: </span><span class="si">$(</span><span class="nb">pwd</span><span class="si">)</span><span class="s2">"</span>
<span class="nb">cd</span> ..
<span class="nb">echo</span> <span class="s2">"In file 2: </span><span class="si">$(</span><span class="nb">pwd</span><span class="si">)</span><span class="s2">"</span>
</code></pre></div> </div>
<p>이제 이 파일을 다음처럼 실행합니다. (반드시 <code class="language-plaintext highlighter-rouge">bash</code>가 <code class="language-plaintext highlighter-rouge">source</code>보다 앞서 등장해야 합니다):</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">pwd
</span>bash asdf.sh
<span class="nb">pwd
source </span>asdf.sh
<span class="nb">pwd</span>
</code></pre></div> </div>
<p>출력 결과:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/home/ubuntu
In file 1: /home/ubuntu
In file 2: /home
/home/ubuntu
In file 1: /home/ubuntu
In file 2: /home
/home
</code></pre></div> </div>
<p><code class="language-plaintext highlighter-rouge">bash asdf.sh</code>는 스크립트 파일 내부의 현재 작업 디렉터리를 바꾸었지만, 스크립트 파일 바깥은 연향을 받지 않았습니다. 반면, <code class="language-plaintext highlighter-rouge">source</code>는 스크립트 파일 바깥에서도 현재 작업 디렉터리에 영향을 주었습니다.</p>
</div>
<h3 id="요약">요약</h3>
<table>
<thead>
<tr>
<th> </th>
<th>역할</th>
<th>사용 셸</th>
<th>환경 변수 유효성</th>
<th><code class="language-plaintext highlighter-rouge">cd</code>로 인한 경로 유지</th>
</tr>
</thead>
<tbody>
<tr>
<td><code class="language-plaintext highlighter-rouge">source</code></td>
<td>스크립트 파일 실행</td>
<td><strong>현재 셸</strong></td>
<td>✔️ 파일 바깥에서 접근 가능</td>
<td>✔️ 파일 바깥에서 유지됨</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">bash</code></td>
<td>스크립트 파일 실행</td>
<td><strong>새로운 셸</strong></td>
<td>❌ 파일 바깥에서 접근 불가</td>
<td>❌ 파일 바깥에서 유지 안 됨</td>
</tr>
</tbody>
</table>
<h2 id="source-vs-"><code class="language-plaintext highlighter-rouge">source</code> vs <code class="language-plaintext highlighter-rouge">.</code></h2>
<h3 id="두-명령어의-기원-c-셸과-본-셸">두 명령어의 기원: C 셸과 본 셸</h3>
<p>C 셸(csh)과 본 셸(sh)은 각각 1978년, 1979년 만들어진 오래된 셸입니다. C 셸은 <code class="language-plaintext highlighter-rouge">source</code>, 본 셸은 <code class="language-plaintext highlighter-rouge">.</code> 명령어만 지원합니다.</p>
<p><a href="https://linux.die.net/man/1/bash">배시</a>의 두 명령어는 본 셸과 C 셸로부터 유래한 것으로 보입니다. 이를 통해 <a href="https://linux.die.net/man/1/bash">배시</a>는 오래된 셸들과의 호환성을 확보할 수 있습니다.</p>
<h3 id="왜-source가-더-권장되는가-fish-셸">왜 <code class="language-plaintext highlighter-rouge">source</code>가 더 권장되는가: fish 셸</h3>
<p><a href="https://fishshell.com/">fish 셸</a>의 개발자는 <code class="language-plaintext highlighter-rouge">.</code> 명령어에 대해 <strong>다른 명령어와 혼동되고, 발견하기 어려우며, <code class="language-plaintext highlighter-rouge">.</code>이라는 명령어를 모르는 사람으로 하여금 <code class="language-plaintext highlighter-rouge">.</code>을 지칭하거나 소리내어 읽을 수 없다</strong>는 문제가 있음을 지적했습니다. 이로 인해 아예 <code class="language-plaintext highlighter-rouge">.</code>을 폐기하고 <code class="language-plaintext highlighter-rouge">source</code>만을 지원하기로 했습니다<sup id="fnref:fish-issuecomment" role="doc-noteref"><a href="#fn:fish-issuecomment" class="footnote" rel="footnote">1</a></sup>.</p>
<h3 id="두-명령어의-기능이-다른-셸-z-셸">두 명령어의 기능이 다른 셸: Z 셸</h3>
<p><a href="http://zsh.sourceforge.net/">Z 셸</a>(zsh)은 조금 독특합니다.</p>
<p><code class="language-plaintext highlighter-rouge">source</code> 명령어의 경우 <a href="https://linux.die.net/man/1/bash">배시</a>처럼 현재 작업 디렉터리에서만 스크립트 파일을 찾는 것에서 끝나지 않습니다. 스크립트 파일을 찾을 수 없다면 <code class="language-plaintext highlighter-rouge">PATH</code> 환경 변수에 존재하는 경로들로부터도 스크립트 파일을 찾습니다<sup id="fnref:zsh-source" role="doc-noteref"><a href="#fn:zsh-source" class="footnote" rel="footnote">2</a></sup>.</p>
<p>특이하게도 <code class="language-plaintext highlighter-rouge">.</code>는 <code class="language-plaintext highlighter-rouge">source</code>와 동일한 동작을 하지 않고 약간 차이가 있습니다. <code class="language-plaintext highlighter-rouge">source</code>와 반대 순서로 먼저 <code class="language-plaintext highlighter-rouge">PATH</code> 환경 변수를 찾고 그 다음 현재 작업 디렉터리에서 스크립트 파일을 찾습니다<sup id="fnref:zsh-dot" role="doc-noteref"><a href="#fn:zsh-dot" class="footnote" rel="footnote">3</a></sup>.</p>
<h2 id="혼동-주의-asdfsh와--asdfsh">혼동 주의: <code class="language-plaintext highlighter-rouge">./asdf.sh</code>와 <code class="language-plaintext highlighter-rouge">. asdf.sh</code></h2>
<p><code class="language-plaintext highlighter-rouge">./asdf.sh</code>와 <code class="language-plaintext highlighter-rouge">. asdf.sh</code>를 혼동하지 마세요. <code class="language-plaintext highlighter-rouge">./asdf.sh</code>는 <code class="language-plaintext highlighter-rouge">bash asdf.sh</code>와 동일합니다.</p>
<h2 id="참고">참고</h2>
<ul>
<li><a href="https://en.wikipedia.org/wiki/Dot_(command)">Dot (command) - Wikipedia</a></li>
<li><a href="https://askubuntu.com/questions/25488/what-is-the-difference-between-source-and">What is the difference between “source” and “.”? - Ask Ubuntu</a>: 관련 스택오버플로 질문</li>
<li><a href="https://stackoverflow.com/questions/20094271/bash-using-dot-or-source-calling-another-script-what-is-difference">Bash: using dot or “source” calling another script - what is difference? - Stack Overflow</a>: 관련 스택오버플로 질문</li>
<li><a href="https://unix.stackexchange.com/questions/17815/running-script-with-and-with-source">running script with “. ” and with “source ” - Unix & Linux</a>: 관련 스택오버플로 질문</li>
</ul>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:fish-issuecomment" role="doc-endnote">
<p><a href="https://github.com/fish-shell/fish-shell/issues/310#issuecomment-22645318">https://github.com/fish-shell/fish-shell/issues/310#issuecomment-22645318</a></p>
<blockquote>
<p>I think fish simply shouldn’t ever have <code class="language-plaintext highlighter-rouge">.</code>, considering it’s confusing (with auto-cd), non-discoverable, and cryptic (if I would see it in code, without knowing about it, I simply couldn’t say anything about it). But considering changing <code class="language-plaintext highlighter-rouge">.</code> to <code class="language-plaintext highlighter-rouge">source</code> would break lots of scripts, I decided to go with soft deprecation - the <code class="language-plaintext highlighter-rouge">.</code> command still works, …</p>
</blockquote>
<p><a href="#fnref:fish-issuecomment" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:zsh-source" role="doc-endnote">
<p><a href="http://zsh.sourceforge.net/Doc/Release/Shell-Builtin-Commands.html">17 Shell Builtin Commands - zsh</a></p>
<blockquote>
<p>source file [ arg … ]
Same as ‘.’, except that the current directory is always searched and is always searched first, before directories in $path.</p>
</blockquote>
<p><a href="#fnref:zsh-source" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:zsh-dot" role="doc-endnote">
<p><a href="http://zsh.sourceforge.net/Doc/Release/Shell-Builtin-Commands.html">17 Shell Builtin Commands - zsh</a></p>
<blockquote>
<p>. file [ arg … ]
If file does not contain a slash, or if PATH_DIRS is set, the shell looks in the components of $path to find the directory containing file. Files in the current directory are not read unless ‘.’ appears somewhere in $path.</p>
</blockquote>
<p><a href="#fnref:zsh-dot" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>방성범 (Bang Seongbeom)배시(Bash)에서 source와 .은 동일한 기능을 하는 명령어로, 셸 스크립트 파일을 실행하는 데 사용합니다.셸에서 환경 변수와 함께 명령어를 실행하는 법2020-04-25T00:00:00+00:002020-04-25T00:00:00+00:00https://www.bangseongbeom.com/run-commands-with-env-vars-in-shell<p><code class="language-plaintext highlighter-rouge">ABC=123 bash asdf.sh</code>처럼 명령어 앞에 환경 변수를 선언할 수 있습니다. 이렇게 선언된 환경 변수는 해당 명령어를 실행하는 동안에만 유효합니다.</p>
<h2 id="선언">선언</h2>
<p>명령어 뒤에 <code class="language-plaintext highlighter-rouge">ABC=123</code>같이 환경 변수들을 배치하는 식으로 선언합니다<sup id="fnref:simple-command" role="doc-noteref"><a href="#fn:simple-command" class="footnote" rel="footnote">1</a></sup>.</p>
<div class="example">
<p><span>예시:</span></p>
<p><code class="language-plaintext highlighter-rouge">asdf.sh</code>를 만듭니다:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">echo</span> <span class="nv">$ABC</span>
</code></pre></div> </div>
<p>다음 명령어를 실행합니다:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">ABC</span><span class="o">=</span>123 bash asdf.sh
</code></pre></div> </div>
<p><code class="language-plaintext highlighter-rouge">123</code>이 출력되는 것을 확인할 수 있습니다.</p>
</div>
<h2 id="유효-범위">유효 범위</h2>
<p>이렇게 선언한 환경 변수는 실행 대상 프로그램 내에서만 유효합니다<sup id="fnref:current-shell" role="doc-noteref"><a href="#fn:current-shell" class="footnote" rel="footnote">2</a></sup>. 다음 명령어에서는 이전 명령어와 함께 선언한 환경 변수를 사용할 수 없습니다.</p>
<div class="example">
<p><span>예시:</span></p>
<p><code class="language-plaintext highlighter-rouge">asdf.sh</code>를 만듭니다:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">echo</span> <span class="nv">$ABC</span>
</code></pre></div> </div>
<p>다음 명령어를 실행합니다:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">ABC</span><span class="o">=</span>123 bash asdf.sh
</code></pre></div> </div>
<p><code class="language-plaintext highlighter-rouge">123</code>이 출력되는 것을 확인할 수 있습니다.</p>
<p>이후 입력할 명령어에서도 <code class="language-plaintext highlighter-rouge">ABC</code>를 계속 사용할 수 있는지 확인해봅시다.</p>
<p>다음 명령어를 실행합니다:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">echo</span> <span class="nv">$ABC</span>
</code></pre></div> </div>
<p>아무 것도 출력되지 않습니다.</p>
</div>
<h2 id="주의-명령어-내에서-환경-변수-사용-불가능">주의: 명령어 내에서 환경 변수 사용 불가능</h2>
<p>명령어 텍스트 내에서는 함께 선언한 환경 변수를 사용할 수 없습니다. <code class="language-plaintext highlighter-rouge">ABC=123</code>이라는 환경 변수 선언과 함께 <code class="language-plaintext highlighter-rouge">echo $ABC</code>를 실행한다고 해서 <code class="language-plaintext highlighter-rouge">$ABC</code>가 <code class="language-plaintext highlighter-rouge">123</code>으로 해석되지 않습니다. 함께 선언한 환경 변수는 우선 내부적으로 보관한 뒤, 실행 대상 프로그램을 실행할 때가 되서야 환경 변수를 추가되는 식으로 동작하기 때문입니다<sup id="fnref:environment-executed" role="doc-noteref"><a href="#fn:environment-executed" class="footnote" rel="footnote">3</a></sup>. 즉 ‘환경 변수 보관’ → ‘환경 변수 해석’ → ‘대상 프로그램 실행’ 순으로 진행합니다.</p>
<div class="example">
<p><span>예시:</span></p>
<p>다음 명령어를 실행합니다:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">ABC</span><span class="o">=</span>456 <span class="nb">echo</span> <span class="nv">$ABC</span>
</code></pre></div> </div>
<p>위의 명령어는 아무 것도 출력하지 않습니다. <code class="language-plaintext highlighter-rouge">ABC</code> 환경 변수의 할당은 <code class="language-plaintext highlighter-rouge">$ABC</code>에 대한 해석이 완료된 후에야 진행하기 때문입니다.</p>
</div>
<div class="example">
<p><span>예시:</span></p>
<p>환경 변수 할당 이후에 명령어를 실행하고 싶다면 다음과 같이 해야 합니다:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">ABC</span><span class="o">=</span>456 bash <span class="nt">-c</span> <span class="s1">'echo $ABC'</span>
</code></pre></div> </div>
<p>앞의 예시와 달리 <code class="language-plaintext highlighter-rouge">$ABC</code>가 바로 해석되지 않고, 그저 <code class="language-plaintext highlighter-rouge">bash</code> 프로그램을 실행하면서 <code class="language-plaintext highlighter-rouge">'echo $ABC'</code>라는 문자열을 함께 전달합니다. <code class="language-plaintext highlighter-rouge">bash</code>에 <code class="language-plaintext highlighter-rouge">ABC</code> 환경 변수가 전달되므로 <code class="language-plaintext highlighter-rouge">bash</code>는 <code class="language-plaintext highlighter-rouge">echo $ABC</code>를 실행하면서 <code class="language-plaintext highlighter-rouge">ABC</code>의 값인 456을 출력합니다.</p>
</div>
<h2 id="주의-실행-대상-없이-환경-변수만-선언">주의: 실행 대상 없이 환경 변수만 선언</h2>
<p>별다른 실행 대상 프로그램 없이 환경 변수만을 선언할 경우, 즉 <code class="language-plaintext highlighter-rouge">ABC=123 bash asdf.sh</code>가 아니라 <code class="language-plaintext highlighter-rouge">ABC=123</code>만 입력할 경우, 이 환경 변수는 해당 실행 대상에서만 유효한 게 아니라 현재 셸 환경 <strong>전체</strong>에서 유효하게 됩니다<sup id="fnref:current-shell-environment-executed" role="doc-noteref"><a href="#fn:current-shell-environment-executed" class="footnote" rel="footnote">4</a></sup>.</p>
<div class="example">
<p><span>예시:</span></p>
<p><code class="language-plaintext highlighter-rouge">asdf.sh</code>를 만듭니다:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">echo</span> <span class="nv">$ABC</span>
</code></pre></div> </div>
<p>다음 명령어를 실행합니다:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">ABC</span><span class="o">=</span>123 bash asdf.sh
</code></pre></div> </div>
<p><code class="language-plaintext highlighter-rouge">123</code>이 출력되는 것을 확인할 수 있습니다.</p>
<p>이제 다음 명령어를 실행합니다:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">echo</span> <span class="nv">$ABC</span>
</code></pre></div> </div>
<p>아무 것도 출력되지 않습니다. <code class="language-plaintext highlighter-rouge">ABC</code>는 <code class="language-plaintext highlighter-rouge">bash asdf.sh</code>를 실행하는 동안만 유효하기 때문입니다.</p>
<p>이번에는 <code class="language-plaintext highlighter-rouge">bash asdf.sh</code> 없이 <code class="language-plaintext highlighter-rouge">ABC=123</code>만 입력합니다:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">ABC</span><span class="o">=</span>123
</code></pre></div> </div>
<p>이제 <code class="language-plaintext highlighter-rouge">ABC</code>는 현재 셸에서라면 계속 유효합니다.</p>
<p>정말 그런지 확인해봅시다. 다음 명령어를 실행합니다:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">echo</span> <span class="nv">$ABC</span>
</code></pre></div> </div>
<p><code class="language-plaintext highlighter-rouge">123</code>이 출력됩니다. <code class="language-plaintext highlighter-rouge">ABC</code>가 셸 환경 전체에서 유효하도록 선언되었으므로, <code class="language-plaintext highlighter-rouge">ABC</code>를 출력할 수 있습니다.</p>
</div>
<h3 id="유효-범위와-제어-연산자">유효 범위와 제어 연산자</h3>
<p><code class="language-plaintext highlighter-rouge">;</code>, <code class="language-plaintext highlighter-rouge">&&</code>, <code class="language-plaintext highlighter-rouge">||</code>같은 제어 연산자로 인해 여러 부분 명령어로 나뉘어질 수 있는 경우, 환경 변수는 각 부분 명령어(매뉴얼에서는 단순 명령어라고 합니다)에만 적용됩니다<sup id="fnref:simple-command-variables" role="doc-noteref"><a href="#fn:simple-command-variables" class="footnote" rel="footnote">5</a></sup>.</p>
<p>환경 변수 선언 부분과 실행 대상 프로그램을 잘못 분리하면 자칫 <strong>실행 대상 없이 환경 변수만 선언</strong>한 것처럼 해석될 수도 있습니다.</p>
<div class="example">
<p><span>예시:</span></p>
<p>아래 두 명령어는 서로 다른 명령어입니다:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">ABCDEF</span><span class="o">=</span>123 <span class="nb">echo</span> <span class="nv">$ABCDEF</span>
</code></pre></div> </div>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">ABCDEF</span><span class="o">=</span>123<span class="p">;</span> <span class="nb">echo</span> <span class="nv">$ABCDEF</span>
</code></pre></div> </div>
<p>위 명령어는 아무 것도 출력되지 않는 반면, 아래 명령어는 <code class="language-plaintext highlighter-rouge">123</code>이 출력됩니다.</p>
<p>첫 번째 명령어와 달리 두 번째 명령어는 <code class="language-plaintext highlighter-rouge">;</code>이라는 제어 연산자로 분리되어 있습니다. 이로 인해 셸은 <code class="language-plaintext highlighter-rouge">ABCDEF=123</code>과 <code class="language-plaintext highlighter-rouge">echo $ABCDEF</code>를 각각 별개의 명령으로 바라봅니다. <code class="language-plaintext highlighter-rouge">ABCDEF=123</code>의 경우 별다른 실행 대상 프로그램 없이 환경 변수만을 선언한 것이므로, 이 환경 변수는 셸 환경 전체에서 유효하게 됩니다. 그러므로 다음 명령인 <code class="language-plaintext highlighter-rouge">echo $ABCDEF</code>에서도 <code class="language-plaintext highlighter-rouge">ABCDEF</code>를 출력할 수 있습니다.</p>
<p><code class="language-plaintext highlighter-rouge">ABCDEF</code>는 이후에도 계속 유효하니, <code class="language-plaintext highlighter-rouge">echo $ABCDEF</code>를 실행하면 또다시 <code class="language-plaintext highlighter-rouge">123</code>을 출력합니다.</p>
</div>
<div class="note">
<p><span>참고:</span></p>
<p>앞 예시에 <code class="language-plaintext highlighter-rouge">ABCDEF=123 || echo $ABCDEF</code>같이 <code class="language-plaintext highlighter-rouge">;</code>(세미콜론) 대신 <code class="language-plaintext highlighter-rouge">||</code>(OR 제어 연산자)를 사용하면 <strong>아무 것도 출력되지 않습니다.</strong> 다음 두 이유 때문입니다:</p>
<ul>
<li>실행 대상 없이 환경 변수만 선언한 경우 해당 명령은 성공한 것으로 간주합니다<sup id="fnref:status-of-zero" role="doc-noteref"><a href="#fn:status-of-zero" class="footnote" rel="footnote">6</a></sup>.</li>
<li><code class="language-plaintext highlighter-rouge">||</code> 제어 연산자는 첫 번째 명령의 실행에 성공할 경우 두 번째 명령을 아예 실행조차 하지 않습니다<sup id="fnref:or-list" role="doc-noteref"><a href="#fn:or-list" class="footnote" rel="footnote">7</a></sup>.</li>
</ul>
<p>물론 <code class="language-plaintext highlighter-rouge">echo $ABCDEF</code>가 실행되지 않은 것일 뿐, <code class="language-plaintext highlighter-rouge">ABCDEF=123</code>은 제대로 셸 전체 영역에 선언된 것이 맞습니다. 이후 다시 <code class="language-plaintext highlighter-rouge">echo $ABCDEF</code>를 실행하면 <code class="language-plaintext highlighter-rouge">123</code>이 출력됩니다.</p>
</div>
<div class="note">
<p><span>참고:</span></p>
<p>앞 예시에 <code class="language-plaintext highlighter-rouge">ABCDEF=123 | echo $ABCDEF</code>같이 <code class="language-plaintext highlighter-rouge">;</code>(세미콜론) 대신 <code class="language-plaintext highlighter-rouge">|</code>(파이프)를 사용하면 <strong>아무 것도 출력되지 않습니다.</strong> <code class="language-plaintext highlighter-rouge">|</code>로 인해 쪼개진 부분 명령어들은, 이들이 모두 현재 셸 환경에서 실행되는 것이 아니라 각각 개별적인 환경에서 실행되기 때문입니다<sup id="fnref:pipeline" role="doc-noteref"><a href="#fn:pipeline" class="footnote" rel="footnote">8</a></sup>.</p>
<p>즉 <code class="language-plaintext highlighter-rouge">ABCDEF=123</code>은 개별 환경에 선언된 것이지 현재 셸에 선언된 것이 아닙니다. 이후 <code class="language-plaintext highlighter-rouge">echo $ABCDEF</code>를 실행해도 아무런 결과가 나오지 않습니다.</p>
</div>
<h2 id="참고">참고</h2>
<ul>
<li><a href="https://unix.stackexchange.com/questions/126938/why-is-setting-a-variable-before-a-command-legal-in-bash">Why is setting a variable before a command legal in bash?</a>: 관련 스택오버플로 질문</li>
</ul>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:simple-command" role="doc-endnote">
<p><a href="http://man7.org/linux/man-pages/man1/bash.1.html#SHELL_GRAMMAR">bash(1) - Linux manual page</a></p>
<blockquote>
<p>A simple command is a sequence of optional variable assignments followed by blank-separated words and redirections, …</p>
</blockquote>
<p><a href="#fnref:simple-command" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:current-shell" role="doc-endnote">
<p><a href="http://man7.org/linux/man-pages/man1/bash.1.html#SIMPLE_COMMAND_EXPANSION">bash(1) - Linux manual page</a></p>
<blockquote>
<p>If no command name results, the variable assignments affect the current shell environment.</p>
</blockquote>
<p><a href="#fnref:current-shell" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:environment-executed" role="doc-endnote">
<p><a href="http://man7.org/linux/man-pages/man1/bash.1.html#SIMPLE_COMMAND_EXPANSION">bash(1) - Linux manual page</a></p>
<blockquote>
<ol>
<li>The words that the parser has marked as variable assignments (those preceding the command name) and redirections are saved for later processing.</li>
<li>The words that are not variable assignments or redirections are expanded.</li>
</ol>
<p>… the variables are added to the environment of the executed command …</p>
</blockquote>
<p><a href="#fnref:environment-executed" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:current-shell-environment-executed" role="doc-endnote">
<p><a href="http://man7.org/linux/man-pages/man1/bash.1.html#SIMPLE_COMMAND_EXPANSION">bash(1) - Linux manual page</a></p>
<blockquote>
<p>If no command name results, the variable assignments affect the current shell environment. Otherwise, the variables are added to the environment of the executed command and do not affect the current shell environment.</p>
</blockquote>
<p><a href="#fnref:current-shell-environment-executed" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:simple-command-variables" role="doc-endnote">
<p><a href="http://man7.org/linux/man-pages/man1/bash.1.html#SIMPLE_COMMAND_EXPANSION">bash(1) - Linux manual page</a></p>
<blockquote>
<p>When a simple command is executed, …</p>
<p>… the variables are added to the environment of the executed command …</p>
</blockquote>
<p><a href="#fnref:simple-command-variables" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:status-of-zero" role="doc-endnote">
<p><a href="http://man7.org/linux/man-pages/man1/bash.1.html#SIMPLE_COMMAND_EXPANSION">bash(1) - Linux manual page</a></p>
<blockquote>
<p>If there were no command substitutions, the command exits with a status of zero.</p>
</blockquote>
<p><a href="#fnref:status-of-zero" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:or-list" role="doc-endnote">
<p><a href="http://man7.org/linux/man-pages/man1/bash.1.html#SHELL_GRAMMAR">bash(1) - Linux manual page</a></p>
<blockquote>
<p>An OR list has the form</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>command1 || command2
</code></pre></div> </div>
<p>command2 is executed if, and only if, command1 returns a non-zero exit status.</p>
</blockquote>
<p><a href="#fnref:or-list" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:pipeline" role="doc-endnote">
<p><a href="http://man7.org/linux/man-pages/man1/bash.1.html#SHELL_GRAMMAR">bash(1) - Linux manual page</a></p>
<blockquote>
<p>Each command in a pipeline is executed as a separate process (i.e., in a subshell).</p>
</blockquote>
<p><a href="#fnref:pipeline" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>방성범 (Bang Seongbeom)ABC=123 bash asdf.sh처럼 명령어 앞에 환경 변수를 선언할 수 있습니다. 이렇게 선언된 환경 변수는 해당 명령어를 실행하는 동안에만 유효합니다.파이썬 내장 모듈과 동일한 이름 피하기2020-04-22T00:00:00+00:002020-04-22T00:00:00+00:00https://www.bangseongbeom.com/avoid-same-names-as-python-builtin-modules<p>내장 모듈과 동일한 이름으로 파이썬 파일을 만드는 것은 위험합니다. 파이썬에는 내장 모듈과 동일한 이름의 파이썬 파일이 존재할 경우 내장 모듈 대신 동일한 이름의 파일을 불러오는 현상이 있는데, 이 현상으로 인해 원인을 알기 어려운 오류가 발생할 수 있습니다.</p>
<h2 id="문제점">문제점</h2>
<p>파이썬에는 내장 모듈과 동일한 이름의 파이썬 파일이 존재할 경우 이를 <code class="language-plaintext highlighter-rouge">import</code>로 불러올 때 내장 모듈 대신 동일한 이름의 파일을 불러오는 현상이 있습니다<sup id="fnref:ahead" role="doc-noteref"><a href="#fn:ahead" class="footnote" rel="footnote">1</a></sup>. 현재 디렉터리에 <code class="language-plaintext highlighter-rouge">enum.py</code>라는 파일이 존재한다면, <code class="language-plaintext highlighter-rouge">import enum</code>이라는 코드를 실행했을 경우 <a href="https://docs.python.org/3/library/enum.htm"><code class="language-plaintext highlighter-rouge">enum</code></a> 내장 모듈을 불러오는 것이 아니라 현재 디렉터리의 <code class="language-plaintext highlighter-rouge">enum.py</code>를 불러오게 되는 것이죠.</p>
<p>이 현상은 언뜻 보기에 별 문제가 없어 보입니다. 해당 내장 모듈이 프로그램 내에서 전혀 쓰이지 않는다면, 내장 모듈 대신 자기가 만든 파이썬 파일을 사용하고 싶을 수도 있으니까요.</p>
<p>하지만 이 현상에는 한 가지 주의해야 할 점이 있습니다. <strong>타인이 작성한 코드(내장 모듈, 외부 라이브러리)가 내장 모듈을 <code class="language-plaintext highlighter-rouge">import</code>했을 때, 그 파일 안에 있는 <code class="language-plaintext highlighter-rouge">import</code>에도 이 현상이 적용됩니다.</strong> 우리 코드가 아님에도 내장 모듈과 동일한 이름의 파이썬 파일을 불러오게 되는 것이죠.</p>
<div class="example">
<p><span>예시:</span></p>
<p>실험해봅시다.</p>
<p>먼저 아무 내용도 없는 <code class="language-plaintext highlighter-rouge">enum.py</code>를 만듭니다:</p>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># 아무 것도 없음
</span></code></pre></div> </div>
<p>이제 같은 디렉터리에 <code class="language-plaintext highlighter-rouge">main.py</code>를 만들고, <a href="https://docs.python.org/3/library/re.html"><code class="language-plaintext highlighter-rouge">re</code></a> 내장 모듈을 <code class="language-plaintext highlighter-rouge">import</code>합니다:</p>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">re</span>
</code></pre></div> </div>
<p><code class="language-plaintext highlighter-rouge">main.py</code>를 실행하면 다음과 같은 오류가 출력됩니다:</p>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span>
<span class="n">File</span> <span class="s">"main.py"</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1</span><span class="p">,</span> <span class="ow">in</span> <span class="o"><</span><span class="n">module</span><span class="o">></span>
<span class="kn">import</span> <span class="nn">re</span>
<span class="n">File</span> <span class="s">"/usr/lib/python3.6/re.py"</span><span class="p">,</span> <span class="n">line</span> <span class="mi">142</span><span class="p">,</span> <span class="ow">in</span> <span class="o"><</span><span class="n">module</span><span class="o">></span>
<span class="k">class</span> <span class="nc">RegexFlag</span><span class="p">(</span><span class="n">enum</span><span class="p">.</span><span class="n">IntFlag</span><span class="p">):</span>
<span class="nb">AttributeError</span><span class="p">:</span> <span class="n">module</span> <span class="s">'enum'</span> <span class="n">has</span> <span class="n">no</span> <span class="n">attribute</span> <span class="s">'IntFlag'</span>
</code></pre></div> </div>
<p><code class="language-plaintext highlighter-rouge">enum.IntFlag</code>를 찾을 수 없다는 오류가 발생했습니다. 왜 이 오류가 발생했을까요?</p>
<p><a href="https://docs.python.org/3/library/re.html"><code class="language-plaintext highlighter-rouge">re</code></a> 내장 모듈에는 <a href="https://github.com/python/cpython/blob/686d508c26fafb57dfe463c4f55b20013dad1441/Lib/re.py#L124"><code class="language-plaintext highlighter-rouge">import enum</code></a>이란 코드가 있습니다. <strong>여기서 <code class="language-plaintext highlighter-rouge">import enum</code>은 <a href="https://docs.python.org/3/library/enum.htm"><code class="language-plaintext highlighter-rouge">enum</code></a> 내장 모듈을 불러오지 않습니다. 앞에서 만든 아무 내용도 없는 <code class="language-plaintext highlighter-rouge">enum.py</code>를 불러옵니다.</strong> <code class="language-plaintext highlighter-rouge">enum.py</code>에는 ‘당연히’ <code class="language-plaintext highlighter-rouge">IntFlag</code>가 없습니다. 때문에 이를 찾을 수 없다는 오류가 발생하게 되는 것이죠.</p>
</div>
<div class="note">
<p><span>참고:</span></p>
<p>파이썬 파일을 불러오는 절차에 대한 자세한 내용은 <a href="/sys-path-pythonpath.html"><sys.path, PYTHONPATH></a>를 참고하세요.</p>
</div>
<h2 id="부정확한-오류-메시지">부정확한 오류 메시지</h2>
<p><strong>이 오류가 무서운 점은 오류 메시지로부터 무엇이 문제인지 추론하기가 어렵다는 것입니다.</strong> ‘내장 모듈이 덮어씌워졌으니 확인해주세요.’같은 친절한 오류를 출력하지 않습니다. 대신 <code class="language-plaintext highlighter-rouge">AttributeError: module 'enum' has no attribute 'IntFlag'</code>같이 <a href="https://docs.python.org/3/library/exceptions.html#AttributeError"><code class="language-plaintext highlighter-rouge">AttributeError</code></a>가 발생합니다. <a href="https://docs.python.org/3/library/exceptions.html#AttributeError"><code class="language-plaintext highlighter-rouge">AttributeError</code></a>는 사용하고자 하는 특정한 메서드나 클래스가 없을 때 발생하는데, 이 정보만 가지고는 내장 모듈이 덮어씌워졌다는 것을 생각하기 어렵죠.</p>
<h2 id="일반적인-이름을-가진-내장-모듈">일반적인 이름을 가진 내장 모듈</h2>
<p>파이썬 내장 모듈 중에는 일반적인 단어로 이름지어진 것이 많다는 것도 이 현상을 회피하기 어렵게 합니다. 많은 내장 모듈이 <a href="https://docs.python.org/3/library/time.html"><code class="language-plaintext highlighter-rouge">time</code></a>, <a href="https://docs.python.org/3/library/calendar.html"><code class="language-plaintext highlighter-rouge">calendar</code></a>, <a href="https://docs.python.org/3/library/email.html"><code class="language-plaintext highlighter-rouge">email</code></a>처럼 단순한 이름을 가집니다. 심지어는 <a href="https://docs.python.org/3/library/test.html"><code class="language-plaintext highlighter-rouge">test</code></a>라는 이름의 모듈까지 존재합니다. 자기가 만드는 파일 이름이 너무 단순하다면, <a href="https://docs.python.org/3/library/index.html">파이썬 표준 라이브러리</a>에서 내장 모듈과 이름이 중복되는지 확인해봐야 할 정도입니다.</p>
<h2 id="완화-방법-디렉터리-생성">완화 방법: 디렉터리 생성</h2>
<p>약간의 번거로움만 감수한다면 이름 중복으로 인한 문제를 어느 정도 해결할 수 있습니다.</p>
<p>방법은 디렉터리를 만들어 작업하는 것입니다. 디렉터리 안에 있는 파이썬 파일을 불러오기 위해서는 불러올 파일 이름 앞에 디렉터리 이름을 붙여주어야 합니다. 이렇게 할 경우 파일 이름이 내장 모듈을 덮어씌우게 되는 지에 대한 여부는 해당 디렉터리의 이름에 대해서만 찾아보면 됩니다.</p>
<div class="example">
<p><span>예시:</span></p>
<p><code class="language-plaintext highlighter-rouge">mypackage/empty.py</code>를 만듭니다:</p>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># 아무 것도 없음
</span></code></pre></div> </div>
<p><code class="language-plaintext highlighter-rouge">mypackage/main.py</code>를 만듭니다:</p>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">mypackage.empty</span> <span class="c1"># mypackage를 앞에 붙여 empty 불러오기
</span></code></pre></div> </div>
<p><code class="language-plaintext highlighter-rouge">mypackage</code> 디렉터리 안에 있는 <code class="language-plaintext highlighter-rouge">empty.py</code>를 불러오기 위해, <code class="language-plaintext highlighter-rouge">empty</code> 앞에 <code class="language-plaintext highlighter-rouge">mypackage</code>를 붙여주었습니다.</p>
<p>이제</p>
</div>
<h3 id="__main__py를-통한-디렉터리-자체-실행"><code class="language-plaintext highlighter-rouge">__main__.py</code>를 통한 디렉터리 자체 실행</h3>
<p>디렉터리를 만드는 식으로 할 때 한 가지 주의해야 할 점이 있습니다. <code class="language-plaintext highlighter-rouge">import</code>는 처음 <code class="language-plaintext highlighter-rouge">python3</code> 명령어를 통해 실행된 파일을 기준으로 다른 파이썬 파일을 찾습니다. 이로 인해 <strong>파이썬 파일 실행 시 해당 파일을 직접 실행하면 <code class="language-plaintext highlighter-rouge">import</code> 문에 문제가 발생합니다.</strong> 해당 디렉터리 안에 들어와 있는 상태에서, 그 디렉터리와 같은 이름으로 된 디렉터리를 찾으려 하기 때문입니다.</p>
<p>대신 디렉터리 ‘자체’를 실행하도록 하여 이 문제를 해결할 수 있습니다. 디렉터리 안에 <code class="language-plaintext highlighter-rouge">__main__.py</code> 파일을 만들면 디렉터리 자체를 실행하려 할 때 <code class="language-plaintext highlighter-rouge">__main__.py</code>가 실행됩니다<sup id="fnref:package-main" role="doc-noteref"><a href="#fn:package-main" class="footnote" rel="footnote">2</a></sup>.</p>
<div class="example">
<p><span>예시:</span></p>
<p><code class="language-plaintext highlighter-rouge">mypackage/__main__.py</code>를 만듭니다 (내용은 상관 없음):</p>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># ...
</span></code></pre></div> </div>
<p>이제 다음과 같이 <strong>디렉터리를 직접 명시</strong>하여 <code class="language-plaintext highlighter-rouge">python3</code> 명령어를 실행합니다:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python3 mypackage
</code></pre></div> </div>
<p>디렉터리 안에 있는 <code class="language-plaintext highlighter-rouge">__main__.py</code> 실행되는 것을 확인하실 수 있습니다.</p>
</div>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:ahead" role="doc-endnote">
<p><a href="https://docs.python.org/3/tutorial/modules.html#the-module-search-path">The Module Search Path - The Python Tutorial</a></p>
<blockquote>
<p>The directory containing the script being run is placed at the beginning of the search path, ahead of the standard library path.</p>
</blockquote>
<p><a href="#fnref:ahead" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:package-main" role="doc-endnote">
<p><a href="__main__ — Top-level script environment - The Python Standard Library">https://docs.python.org/3/library/<strong>main</strong>.html</a></p>
<blockquote>
<p>For a package, the same effect can be achieved by including a <code class="language-plaintext highlighter-rouge">__main__.py</code> module, the contents of which will be executed when the module is run with <code class="language-plaintext highlighter-rouge">-m</code>.</p>
</blockquote>
<p><a href="#fnref:package-main" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>방성범 (Bang Seongbeom)내장 모듈과 동일한 이름으로 파이썬 파일을 만드는 것은 위험합니다. 파이썬에는 내장 모듈과 동일한 이름의 파이썬 파일이 존재할 경우 내장 모듈 대신 동일한 이름의 파일을 불러오는 현상이 있는데, 이 현상으로 인해 원인을 알기 어려운 오류가 발생할 수 있습니다.파일 이름 끝 하이픈2020-04-21T00:00:00+00:002020-04-21T00:00:00+00:00https://www.bangseongbeom.com/trailing-hyphens-in-file-names<p>리눅스에서 <code class="language-plaintext highlighter-rouge">/etc/group-</code>, <code class="language-plaintext highlighter-rouge">/etc/passwd-</code>, <code class="language-plaintext highlighter-rouge">/etc/shadow-</code>의 파일 이름 끝에 있는 하이픈(<code class="language-plaintext highlighter-rouge">-</code>)은 백업 파일을 의미합니다.</p>
<p>리눅스 매뉴얼에서 <code class="language-plaintext highlighter-rouge">/etc/shadow-</code>이 백업 용도로 사용된다는 사실을 확인할 수 있습니다<sup id="fnref:shadow" role="doc-noteref"><a href="#fn:shadow" class="footnote" rel="footnote">1</a></sup>.</p>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:shadow" role="doc-endnote">
<p><a href="http://man7.org/linux/man-pages/man5/shadow.5.html#FILES">SHADOW(5) - man7.org</a></p>
<blockquote>
<p>/etc/shadow-
Backup file for /etc/shadow.</p>
</blockquote>
<p><a href="#fnref:shadow" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>방성범 (Bang Seongbeom)리눅스에서 /etc/group-, /etc/passwd-, /etc/shadow-의 파일 이름 끝에 있는 하이픈(-)은 백업 파일을 의미합니다.sys.path, PYTHONPATH: 파이썬 파일 탐색 경로2020-04-19T00:00:00+00:002020-04-19T00:00:00+00:00https://www.bangseongbeom.com/sys-path-pythonpath<p><code class="language-plaintext highlighter-rouge">import</code> 문을 통해 다른 파이썬 파일을 불러올 때, 파이썬은 내부적으로 파일을 찾기 위해 <a href="https://docs.python.org/3/library/sys.html#sys.path"><code class="language-plaintext highlighter-rouge">sys.path</code></a>와 <a href="https://docs.python.org/3/using/cmdline.html#envvar-PYTHONPATH"><code class="language-plaintext highlighter-rouge">PYTHONPATH</code></a>에 있는 경로를 탐색합니다. 이 두 변수를 적절히 수정해 임의의 디렉터리에 있는 파이썬 파일을 손쉽게 불러올 수 있습니다.</p>
<h2 id="syspath에-append로-경로-추가"><code class="language-plaintext highlighter-rouge">sys.path</code>에 <code class="language-plaintext highlighter-rouge">append()</code>로 경로 추가</h2>
<p><a href="https://docs.python.org/3/library/sys.html#sys.path"><code class="language-plaintext highlighter-rouge">sys.path</code></a>는 디렉터리의 경로들이 기록된 문자열 리스트입니다. 이 리스트에 경로를 추가하면 해당 경로에 있는 파이썬 파일을 <code class="language-plaintext highlighter-rouge">import</code> 문으로 불러올 수 있습니다.</p>
<div class="example">
<p><span>예시:</span></p>
<p>먼저 아무 파이썬 파일을 하나 만듭시다. 내용은 상관 없습니다. 이 예시에서는 <code class="language-plaintext highlighter-rouge">/opt/common.py</code>에 만들겠습니다:</p>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># 아무 내용도 없음
</span></code></pre></div> </div>
<p>이제 <code class="language-plaintext highlighter-rouge">/home/ubuntu/example.py</code>를 만듭니다:</p>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">sys</span>
<span class="n">sys</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="s">"/opt"</span><span class="p">)</span>
<span class="kn">import</span> <span class="nn">common</span>
</code></pre></div> </div>
<p>예시에서 <code class="language-plaintext highlighter-rouge">sys.path.append("/opt")</code>를 통해 <code class="language-plaintext highlighter-rouge">/opt</code> 디렉터리를 추가했습니다. 이로 인해 <code class="language-plaintext highlighter-rouge">/opt/common.py</code>를 <code class="language-plaintext highlighter-rouge">import</code>할 수 있게 됩니다.</p>
</div>
<h2 id="syspath의-기본값"><code class="language-plaintext highlighter-rouge">sys.path</code>의 기본값</h2>
<p><a href="https://docs.python.org/3/library/sys.html#sys.path"><code class="language-plaintext highlighter-rouge">sys.path</code></a>에는 파이썬에 의해 기본적으로 몇 가지 경로가 미리 추가되어 있습니다.</p>
<h3 id="py-파일이-속한-디렉터리의-절대-경로"><code class="language-plaintext highlighter-rouge">.py</code> 파일이 속한 디렉터리의 절대 경로</h3>
<p><a href="https://docs.python.org/3/library/sys.html#sys.path"><code class="language-plaintext highlighter-rouge">sys.path</code></a>에는 가장 먼저 <code class="language-plaintext highlighter-rouge">.py</code> 파일이 속한 디렉터리의 절대 경로가 추가됩니다<sup id="fnref:input-script" role="doc-noteref"><a href="#fn:input-script" class="footnote" rel="footnote">1</a></sup>.</p>
<p>특정 파이썬 파일을 실행하는 것 대신, <strong>파이썬 인터프리터에서 직접 <code class="language-plaintext highlighter-rouge">print(sys.path)</code>를 실행</strong>하면 인터프리터를 실행할 당시의 경로(현재 작업 디렉터리)가 <a href="https://docs.python.org/3/library/sys.html#sys.path"><code class="language-plaintext highlighter-rouge">sys.path</code></a>에 추가됩니다<sup id="fnref:current-directory" role="doc-noteref"><a href="#fn:current-directory" class="footnote" rel="footnote">2</a></sup>.</p>
<div class="example">
<p><span>예시:</span></p>
<p>먼저 테스트 용 파일을 만듭니다. 여기서는 <code class="language-plaintext highlighter-rouge">/home/ubuntu</code>에 <code class="language-plaintext highlighter-rouge">example.py</code>라는 이름으로 만들겠습니다:</p>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">sys</span>
<span class="k">print</span><span class="p">(</span><span class="n">sys</span><span class="p">.</span><span class="n">path</span><span class="p">)</span>
</code></pre></div> </div>
<p>실행 결과:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[`'/home/ubuntu'`, ...]
</code></pre></div> </div>
<p>리스트의 첫 번째 값으로 <code class="language-plaintext highlighter-rouge">'/home/ubuntu'</code>가 들어있는 것을 확인할 수 있습니다.</p>
</div>
<div class="example">
<p><span>예시:</span></p>
<p><code class="language-plaintext highlighter-rouge">python3</code>을 실행해 파이썬 인터프리터에서 다음 내용을 입력합니다:</p>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">>>></span> <span class="kn">import</span> <span class="nn">sys</span>
<span class="o">>>></span> <span class="k">print</span><span class="p">(</span><span class="n">sys</span><span class="p">.</span><span class="n">path</span><span class="p">)</span>
</code></pre></div> </div>
<p>실행 결과 (일부 생략):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[`''`, ...]
</code></pre></div> </div>
<p>앞의 예시와 달리 빈 문자열인 <code class="language-plaintext highlighter-rouge">''</code>이 리스트 맨 앞에 존재하는 것을 확인할 수 있습니다. 빈 문자열은 유효한 상대 경로로, 현재 디렉터리를 의미합니다.</p>
</div>
<h3 id="pythonpath-환경-변수"><code class="language-plaintext highlighter-rouge">PYTHONPATH</code> 환경 변수</h3>
<p><a href="https://docs.python.org/3/using/cmdline.html#envvar-PYTHONPATH"><code class="language-plaintext highlighter-rouge">PYTHONPATH</code></a> 환경 변수에 경로를 추가하면, 파이썬은 이 경로들을 <a href="https://docs.python.org/3/library/sys.html#sys.path"><code class="language-plaintext highlighter-rouge">sys.path</code></a>에 추가해줍니다.</p>
<p>이를 통해 파이썬 코드 내부에서 뿐만 아니라 파이썬 코드 밖에서도 <a href="https://docs.python.org/3/library/sys.html#sys.path"><code class="language-plaintext highlighter-rouge">sys.path</code></a>를 조작할 수 있습니다.</p>
<p><a href="https://docs.python.org/3/using/cmdline.html#envvar-PYTHONPATH"><code class="language-plaintext highlighter-rouge">PYTHONPATH</code></a>에는 <a href="https://docs.python.org/3/library/sys.html#sys.path"><code class="language-plaintext highlighter-rouge">sys.path</code></a>에 추가할 여러 경로들이 들어갑니다. 리눅스에서는 <code class="language-plaintext highlighter-rouge">/foo:/bar</code>처럼 <code class="language-plaintext highlighter-rouge">:</code>로 두 경로를 구분하고, 윈도우에서는 <code class="language-plaintext highlighter-rouge">/foo;/bar</code>처럼 <code class="language-plaintext highlighter-rouge">;</code>로 두 경로를 구분합니다. (<code class="language-plaintext highlighter-rouge">PATH</code> 환경 변수와 동일한 방식)<sup id="fnref:pythonpath-format" role="doc-noteref"><a href="#fn:pythonpath-format" class="footnote" rel="footnote">3</a></sup></p>
<div class="example">
<p><span>예시:</span></p>
<p>테스트 용 파일을 하나 만듭니다:</p>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">sys</span>
<span class="k">print</span><span class="p">(</span><span class="n">sys</span><span class="p">.</span><span class="n">path</span><span class="p">)</span>
</code></pre></div> </div>
<p>다음과 같이 <a href="https://docs.python.org/3/using/cmdline.html#envvar-PYTHONPATH"><code class="language-plaintext highlighter-rouge">PYTHONPATH</code></a> 환경 변수에 <code class="language-plaintext highlighter-rouge">/foo</code>와 <code class="language-plaintext highlighter-rouge">/bar</code>를 넣은 채로 <code class="language-plaintext highlighter-rouge">python3</code> 명령어를 실행합니다:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">PYTHONPATH</span><span class="o">=</span>/foo:/bar python3 example.py
</code></pre></div> </div>
<p>실행 결과 (일부 생략):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[..., '/foo', '/bar', ...]
</code></pre></div> </div>
<p><code class="language-plaintext highlighter-rouge">'/foo'</code>, <code class="language-plaintext highlighter-rouge">'/bar'</code>가 추가된 것을 확인할 수 있습니다.</p>
</div>
<h3 id="기타-기본-경로">기타 기본 경로</h3>
<p>이외에도 <a href="https://docs.python.org/3/library/sys.html#sys.path"><code class="language-plaintext highlighter-rouge">sys.path</code></a>에는 파이썬에 포함된 여러 내장 모듈 등을 탐색하기 위한 기본 경로가 들어갑니다. 이 경로들은 운영 체제나 파이썬 버전에 따라 다릅니다<sup id="fnref:installation-dependent" role="doc-noteref"><a href="#fn:installation-dependent" class="footnote" rel="footnote">4</a></sup>.</p>
<div class="example">
<p><span>예시:</span></p>
<p>테스트 용 파일을 하나 만듭니다:</p>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">sys</span>
<span class="k">print</span><span class="p">(</span><span class="n">sys</span><span class="p">.</span><span class="n">path</span><span class="p">)</span>
</code></pre></div> </div>
<p>실행 결과 (일부 생략):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[..., `'/usr/lib/python36.zip'`, `'/usr/lib/python3.6'`, `'/usr/lib/python3.6/lib-dynload'`,
`'/usr/local/lib/python3.6/dist-packages'`, `'/usr/lib/python3/dist-packages'`]
</code></pre></div> </div>
<p><code class="language-plaintext highlighter-rouge">'/usr/lib/python36.zip'</code>, <code class="language-plaintext highlighter-rouge">'/usr/lib/python3.6'</code>, <code class="language-plaintext highlighter-rouge">'/usr/lib/python3.6/lib-dynload'</code> 등 내장 모듈을 위한 여러 경로들을 확인할 수 있습니다.</p>
</div>
<div class="note">
<p><span>참고:</span></p>
<p><a href="https://docs.python.org/3/library/sys.html#sys.path"><code class="language-plaintext highlighter-rouge">sys.path</code></a>에는 디렉터리 경로뿐만 아니라 <code class="language-plaintext highlighter-rouge">'/usr/lib/python36.zip'</code>처럼 압축 파일도 추가할 수 있습니다. 자세한 내용은 <a href="https://docs.python.org/3/library/zipimport.html"><code class="language-plaintext highlighter-rouge">zipimport</code></a> 모듈을 참고하세요.</p>
</div>
<h2 id="주의-syspath의-순서">주의: <code class="language-plaintext highlighter-rouge">sys.path</code>의 순서</h2>
<p><code class="language-plaintext highlighter-rouge">import</code>는 <a href="https://docs.python.org/3/library/sys.html#sys.path"><code class="language-plaintext highlighter-rouge">sys.path</code></a> 리스트에 들어있는 경로들을 탐색하며 불러올 파이썬 파일을 찾습니다. 리스트에 들어있는 맨 처음 경로부터 탐색을 시작합니다. 특정 경로에서 불러올 파일을 찾았다면 남은 경로를 더 찾아보지 않고 탐색을 중지합니다<sup id="fnref:ahead" role="doc-noteref"><a href="#fn:ahead" class="footnote" rel="footnote">5</a></sup>.</p>
<p><a href="https://docs.python.org/3/library/sys.html#sys.path"><code class="language-plaintext highlighter-rouge">sys.path</code></a>의 기본값은 이 문서에서 언급한 순서대로 추가됩니다<sup id="fnref:order" role="doc-noteref"><a href="#fn:order" class="footnote" rel="footnote">6</a></sup>:</p>
<ol>
<li><code class="language-plaintext highlighter-rouge">.py</code> 파일이 속한 디렉터리의 절대 경로</li>
<li><code class="language-plaintext highlighter-rouge">PYTHONPATH</code> 환경 변수</li>
<li>기타 기본 경로</li>
</ol>
<p>만약 내장 모듈과 같은 이름으로 로컬 파일을 만들게 되면, 위의 순서로 인해 로컬 파일을 우선하여 불러옵니다<sup id="fnref:error" role="doc-noteref"><a href="#fn:error" class="footnote" rel="footnote">7</a></sup>.</p>
<div class="note">
<p><span>참고:</span></p>
<p>내장 모듈을 덮어쓰는 현상으로 인해 발견하기 어려운 오류가 발생할 수 있습니다. 자세한 내용은 [<파이썬 내장="" 모듈과="" 동일한="" 이름="" 피하기="">](/avoid-python-builtin-module-names.html)를 참고하세요.</파이썬></p>
</div>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:input-script" role="doc-endnote">
<p><a href="https://docs.python.org/3/tutorial/modules.html#the-module-search-path">The Module Search Path - The Python Tutorial</a></p>
<blockquote>
<p>The directory containing the input script</p>
</blockquote>
<p><a href="#fnref:input-script" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:current-directory" role="doc-endnote">
<p><a href="https://docs.python.org/3/tutorial/modules.html#the-module-search-path">The Module Search Path - The Python Tutorial</a></p>
<blockquote>
<p>(or the current directory when no file is specified)</p>
</blockquote>
<p><a href="#fnref:current-directory" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:pythonpath-format" role="doc-endnote">
<p><a href="https://docs.python.org/3/using/cmdline.html#envvar-PYTHONPATH">PYTHONPATH - Python Setup and Usage</a></p>
<blockquote>
<p>The format is the same as the shell’s PATH: one or more directory pathnames separated by os.pathsep (e.g. colons on Unix or semicolons on Windows).</p>
</blockquote>
<p><a href="#fnref:pythonpath-format" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:installation-dependent" role="doc-endnote">
<p><a href="https://docs.python.org/3/tutorial/modules.html#the-module-search-path">The Module Search Path - The Python Tutorial</a></p>
<blockquote>
<p>The installation-dependent default.</p>
</blockquote>
<p><a href="#fnref:installation-dependent" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:ahead" role="doc-endnote">
<p><a href="https://docs.python.org/3/tutorial/modules.html#the-module-search-path">The Module Search Path - The Python Tutorial</a></p>
<blockquote>
<p>The directory containing the script being run is placed at the beginning of the search path, ahead of the standard library path.</p>
</blockquote>
<p><a href="#fnref:ahead" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:order" role="doc-endnote">
<p><a href="https://docs.python.org/3/tutorial/modules.html#the-module-search-path">The Module Search Path - The Python Tutorial</a></p>
<blockquote>
<ul>
<li>The directory containing the input script (or the current directory when no file is specified).</li>
<li>PYTHONPATH (a list of directory names, with the same syntax as the shell variable PATH).</li>
<li>The installation-dependent default.</li>
</ul>
</blockquote>
<p><a href="#fnref:order" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:error" role="doc-endnote">
<p><a href="https://docs.python.org/3/tutorial/modules.html#the-module-search-path">The Module Search Path - The Python Tutorial</a></p>
<blockquote>
<p>This means that scripts in that directory will be loaded instead of modules of the same name in the library directory. This is an error unless the replacement is intended.</p>
</blockquote>
<p><a href="#fnref:error" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>방성범 (Bang Seongbeom)import 문을 통해 다른 파이썬 파일을 불러올 때, 파이썬은 내부적으로 파일을 찾기 위해 sys.path와 PYTHONPATH에 있는 경로를 탐색합니다. 이 두 변수를 적절히 수정해 임의의 디렉터리에 있는 파이썬 파일을 손쉽게 불러올 수 있습니다.