mervia@learn:~/workshops/terminal-10x/105$ ← /learn
· Pichaya Puttekulangkura พิชยะ พุฒิกุลางกูร · #terminal#beginners#macos
reading mode โหมดอ่าน

terminal 105: PATH and environment variables terminal 105: PATH และตัวแปรแวดล้อม

use PATH as your first environment variable, then use `echo $PATH`, `which`, and `export` to understand how your shell finds commands and why a fix can disappear in a new window ใช้ PATH เป็นตัวแปรแวดล้อมตัวแรกของคุณ แล้วใช้ `echo $PATH`, `which`, และ `export` เพื่อเข้าใจว่า shell หาคำสั่งเจอได้อย่างไร และทำไมวิธีแก้บางอย่างถึงหายไปเมื่อเปิดหน้าต่างใหม่

in terminal 104 you saw the annoying version of the problem:

  • the tool exists
  • the install worked
  • and the shell still says “command not found”

105 is where that stops feeling random.

the name for the missing mental model is PATH.

PATH is just a list of folders your shell searches when you type a command name like rg, git, or python3.

in this lesson, “environment variables” mostly means one important example: PATH. that is intentional. you do not need a full survey yet. you need one variable that explains a lot of real beginner pain immediately.

once you understand that search order, a lot of terminal confusion collapses into three simple questions:

  1. does the file exist?
  2. is its folder in PATH?
  3. if more than one copy exists, which one wins?

what you’ll know by the end

  • what PATH is in plain language
  • how to inspect it with echo $PATH
  • how to ask which file wins with which
  • how to make a temporary PATH change with export
  • why PATH counts as an environment variable in the first place
  • why that temporary fix disappears in a new shell
  • the footgun for this lesson: a fix you make with export only changes the current shell

what this assumes

terminal 101 through terminal 104.

if the Homebrew lesson made sense and which brew no longer feels mysterious, you’re ready for the reason underneath it.

3 commands + 1 environment variable

this lesson stays Mac-first so it connects cleanly to 104, but the mental model is broader than macOS.

what you want to docommandwhat it means
show the shell’s search listecho $PATHprint the PATH directories in order
ask which file a command resolves towhich rgshow the first matching executable the shell will run
temporarily put a folder at the frontexport PATH="$HOME/bin:$PATH"change PATH for the current shell session only

the search rule is the real lesson:

the shell walks through PATH from left to right, and the first match wins

that one sentence explains both of these beginner problems:

  • “why is my custom script not found even though the file exists?”
  • “why is the wrong copy of python3 running?“

try it yourself

the interactive demo below is designed to make the invisible part visible.

the shipit preset is a stand-in for any small helper script you might save in ~/bin.

work through this order:

  1. leave the default python3 preset selected
  2. click run which
  3. notice that the first python3 match wins, even though another copy exists later
  4. click the shipit preset
  5. click run which again
  6. see the second beginner problem: the command exists, but the current PATH is not searching ~/bin
  7. click echo $PATH
  8. click export PATH="$HOME/bin:$PATH"
  9. click run which
  10. click open new shell
  11. click run which one more time

the teaching moment is steps 8 through 11:

  • export can fix the current shell immediately
  • but it does not automatically change future shells

that’s why “it worked a second ago” and “it disappeared in the next tab” can both be true.

ใน terminal 104 เราเห็นอาการที่ชวนรำคาญของปัญหานี้ไปแล้วครับ:

  • เครื่องมือมีอยู่จริง
  • การติดตั้งก็สำเร็จ
  • แต่ shell ยังบอกว่า “command not found”

105 คือจุดที่เรื่องนี้จะเลิกดูสุ่มครับ

ชื่อของภาพจำที่ขาดไปคือ PATH

PATH ก็คือ “รายชื่อโฟลเดอร์” ที่ shell จะไล่ค้นหาเวลาคุณพิมพ์ชื่อคำสั่งอย่าง rg, git, หรือ python3

ในบทนี้คำว่า “ตัวแปรแวดล้อม” จะหมายถึงตัวอย่างสำคัญตัวเดียวเป็นหลัก คือ PATH ครับ ตั้งใจให้แคบแบบนี้ เพราะคุณยังไม่ต้องได้สารานุกรมเรื่องนี้ คุณต้องการแค่ตัวแปรเดียวที่ช่วยอธิบายปัญหามือใหม่ได้ทันที

พอเข้าใจลำดับการค้นหานี้ ความงงเรื่อง terminal จำนวนมากจะยุบลงเหลือแค่ 3 คำถาม:

  1. ไฟล์นั้นมีอยู่จริงไหม
  2. โฟลเดอร์ของมันอยู่ใน PATH หรือเปล่า
  3. ถ้ามีมากกว่าหนึ่งชุด ตัวไหนชนะ

จบโพสต์นี้แล้วจะทำอะไรได้บ้าง

  • เข้าใจว่า PATH คืออะไรแบบไม่ต้องใช้ศัพท์ยาก
  • เช็กมันได้ด้วย echo $PATH
  • ถามได้ว่าคำสั่งจริงๆ ชี้ไปไฟล์ไหนด้วย which
  • เปลี่ยน PATH ชั่วคราวด้วย export
  • เข้าใจว่า PATH เองก็คือ environment variable ตัวหนึ่ง
  • เข้าใจว่าทำไมวิธีแก้แบบชั่วคราวถึงหายไปเมื่อเปิด shell ใหม่
  • จำจุดพลาดสำคัญของบทนี้ได้: สิ่งที่แก้ด้วย export มีผลแค่ shell ปัจจุบัน

โพสต์นี้เริ่มจากไหน

ผ่าน terminal 101 ถึง terminal 104 มาแล้วจะลื่นสุดครับ

ถ้าบท Homebrew เริ่มชัดขึ้นแล้ว และ which brew ไม่ดูเป็นเวทมนตร์อีกต่อไป ก็พร้อมสำหรับเหตุผลที่อยู่ข้างใต้แล้วครับ

3 คำสั่ง + 1 ตัวแปรแวดล้อม

บทนี้ยังยึดตัวอย่างแบบเริ่มจาก Mac เป็นหลัก เพื่อให้ต่อจาก 104 ได้ลื่น แต่ภาพจำหลักจริงๆ ใช้ได้กว้างกว่าบน macOS อย่างเดียวครับ

สิ่งที่อยากทำคำสั่งความหมาย
ดูรายการโฟลเดอร์ที่ shell ใช้ค้นหาecho $PATHพิมพ์ PATH ออกมาตามลำดับ
ถามว่าคำสั่งนี้จะวิ่งไปไฟล์ไหนwhich rgแสดง executable ตัวแรกที่ shell จะเลือกใช้
เอาโฟลเดอร์หนึ่งขึ้นมาไว้ด้านหน้าแบบชั่วคราวexport PATH="$HOME/bin:$PATH"เปลี่ยน PATH ของ shell ปัจจุบันเท่านั้น

กฎที่สำคัญจริงๆ มีประโยคเดียว:

shell จะไล่ PATH จากซ้ายไปขวา และ ตัวที่เจอก่อนชนะ

ประโยคนี้อธิบายปัญหามือใหม่ได้พร้อมกันสองแบบ:

  • “ทำไม script ของฉันมีไฟล์อยู่แล้ว แต่ยังหาไม่เจอ?”
  • “ทำไม python3 คนละตัวกับที่คิดถึงถูกเรียกขึ้นมา?”

ลองเล่นดู

เดโมด้านล่างออกแบบมาเพื่อทำให้สิ่งที่ปกติมองไม่เห็น มองเห็นได้ครับ

preset shipit ใช้แทน helper script เล็กๆ ที่คุณอาจเซฟไว้ใน ~/bin

ลองตามลำดับนี้:

  1. ปล่อย preset เริ่มต้นไว้ที่ python3
  2. กด run which
  3. สังเกตว่า python3 ตัวแรกชนะ ถึงแม้จะมีอีกตัวอยู่ลึกลงไป
  4. เปลี่ยนเป็นตัวอย่าง shipit
  5. กด run which อีกรอบ
  6. จะเห็นปัญหาอีกแบบ: คำสั่งมีอยู่จริง แต่ PATH ปัจจุบันยังไม่ค้นหา ~/bin
  7. กด echo $PATH
  8. กด export PATH="$HOME/bin:$PATH"
  9. กด run which
  10. กด open new shell
  11. กด run which อีกครั้ง

หัวใจของบทนี้อยู่ที่ข้อ 8 ถึง 11:

  • export แก้ shell ปัจจุบันได้ทันที
  • แต่มัน ไม่ได้ เปลี่ยน shell ใหม่ให้เองอัตโนมัติ

เพราะงั้นประโยคว่า “เมื่อกี้ยังใช้ได้อยู่เลย” กับ “เปิดแท็บใหม่แล้วหาย” จึงจริงพร้อมกันได้ครับ

terminal 105 interactiveterminal 105 แบบโต้ตอบlesson focus: PATH is a search order, and the first match winsโฟกัสของบท: PATH คือลำดับการค้นหา และตัวแรกที่เจอจะชนะ

Path visualizerตัวช่วยมอง PATH

Try a command name, watch the shell walk through PATH from top to bottom, and see why a temporary export works only in the current shell window.ลองพิมพ์ชื่อคำสั่ง แล้วดูว่า shell ไล่ค้นหา PATH จากบนลงล่างอย่างไร และทำไม export แบบชั่วคราวถึงมีผลแค่ใน shell ปัจจุบัน

$
Click a command card to run the guided path, or type the command yourself and press Enter.กดการ์ดคำสั่งเพื่อรันตามลำดับ หรือพิมพ์คำสั่งเองแล้วกด Enter
current lookup targetคำสั่งที่กำลังตรวจpython3
current teaching momentจุดเรียนรู้ตอนนี้PATH stops at the first match, so /opt/homebrew/bin/python3 wins before later copies of `python3`.PATH หยุดที่ตัวแรกที่เจอ ดังนั้น /opt/homebrew/bin/python3 จึงชนะก่อนสำเนา `python3` ที่อยู่ทีหลัง
session 1 — zsh
# session 1: inspect how PATH resolves a commandsession 1: ตรวจว่า PATH หา command อย่างไร
Start with `python3`, then try `shipit` to see what changes when ~/bin is missing.เริ่มด้วย `python3` แล้วลอง `shipit` เพื่อดูว่าเกิดอะไรขึ้นเมื่อยังไม่มี ~/bin ใน PATH
which resultผลลัพธ์ของ which/opt/homebrew/bin/python3
1./opt/homebrew/binmatchเจอ
/opt/homebrew/bin/python3
2./usr/local/binno matchไม่เจอ
no python3 hereไม่มี python3 ในโฟลเดอร์นี้
3./usr/binlater copyสำเนาที่อยู่ทีหลัง
/usr/bin/python3
4./binno matchไม่เจอ
no python3 hereไม่มี python3 ในโฟลเดอร์นี้
current shell PATHPATH ของ shell ปัจจุบัน
1./opt/homebrew/bin
2./usr/local/bin
3./usr/bin
4./bin
next shell if you open a new windowPATH ของ shell ใหม่ถ้าเปิดหน้าต่างใหม่
1./opt/homebrew/bin
2./usr/local/bin
3./usr/bin
4./bin
known locations for this commandตำแหน่งที่รู้จักของคำสั่งนี้
/opt/homebrew/bin/python3winsชนะ
/usr/bin/python3shadowedถูกบัง

what PATH really is

you do not need a textbook definition here.

PATH is just a colon-separated list of directories:

echo $PATH

when you type:

rg notes

the shell does not magically know where rg lives.

it checks each PATH directory in order until it finds a matching executable. if /opt/homebrew/bin comes before /usr/bin, then commands in /opt/homebrew/bin get first chance.

that is why order matters.

why PATH is an environment variable

an environment variable is just a named piece of shell state.

PATH is one of those named values. when you run:

echo $PATH

you are asking the shell to print the current value stored under the name PATH.

that is enough environment-variable theory for now. the point is not to memorize jargon. the point is to understand that PATH is data the shell carries around, uses while resolving commands, and can change for the current session.

why which is worth learning early

which answers a very practical question:

“if i type this command name right now, what file will the shell actually run?”

examples:

which brew
which rg
which python3

if which prints a path, the shell can resolve the command.

if it cannot, you know the problem is not “typing the command wrong.” the problem is that the shell cannot find a matching file in its current search path.

that distinction saves a lot of wasted reinstalling and random copying.

what export changes, and what it does not

this line is useful:

export PATH="$HOME/bin:$PATH"

it means:

  • take ~/bin
  • put it at the front
  • keep the old PATH after it

that change affects the current shell process.

it does not rewrite history, and it does not automatically configure future shells. if you close the window or open a fresh one, the temporary change is gone unless your startup files add it again.

that is exactly why 105 comes before 106.

105 teaches the mental model. 106 teaches where to put the change so it sticks.

one real-world task

imagine you made a tiny helper script called shipit and saved it in ~/bin.

the workflow looks like this:

  1. run which shipit
  2. if it does not resolve, check echo $PATH
  3. temporarily test with export PATH="$HOME/bin:$PATH"
  4. run which shipit again
  5. if which shipit now prints a path, the PATH part is fixed
  6. if that fixed it, make the change persistent in your shell config instead of retyping export forever

that is a real beginner workflow because it separates three jobs cleanly:

  • verify
  • test the fix
  • then make the fix permanent in the right place

whether the script itself runs correctly after that is a separate question. PATH only decides whether the shell can find it.

common mistakes

you treat PATH like one mysterious string instead of an ordered list. order is the whole point. leftmost directories get checked first.

you assume a file existing somewhere means the shell can run it. the shell only searches the directories currently in PATH.

you forget that export is temporary. this is the footgun for 105. if the fix vanishes in a new tab, nothing supernatural happened.

you keep guessing instead of checking with which. use the cheap evidence first.

you jump straight to editing config files without testing the idea in the current shell. a temporary export is a good experiment. if it works, then move the change into shell config.

what this unlocks

now “command not found” should feel narrower and easier to debug.

you can check PATH, see which executable wins, and prove whether a change is temporary or persistent.

next up: terminal 106 — shell config on macOS. that’s where PATH changes, aliases, and source ~/.zshrc stop being copy-paste rituals and start making sense.

PATH จริงๆ คืออะไร

ตอนนี้ยังไม่ต้องใช้คำนิยามแบบตำราก็ได้ครับ

PATH คือรายการโฟลเดอร์ที่คั่นด้วย ::

echo $PATH

เวลาเราพิมพ์:

rg notes

shell ไม่ได้ “รู้อยู่แล้ว” ว่า rg อยู่ตรงไหน

มันจะไล่เช็กทีละโฟลเดอร์ตามลำดับใน PATH จนกว่าจะเจอ executable ที่ชื่อตรงกัน ถ้า /opt/homebrew/bin มาก่อน /usr/bin คำสั่งใน /opt/homebrew/bin ก็ได้สิทธิ์ก่อน

เพราะงั้น “ลำดับ” จึงสำคัญครับ

ทำไม PATH ถึงนับเป็น environment variable

environment variable ก็คือค่าชิ้นหนึ่งที่ shell เก็บไว้โดยมีชื่อกำกับ

PATH ก็เป็นค่าชื่อหนึ่งในนั้น พอคุณรัน:

echo $PATH

คุณกำลังขอให้ shell พิมพ์ค่าปัจจุบันที่เก็บไว้ภายใต้ชื่อ PATH ออกมา

ทฤษฎีเรื่อง environment variable สำหรับตอนนี้เอาแค่นี้พอครับ จุดสำคัญไม่ใช่การท่องศัพท์ แต่คือการเห็นว่า PATH เป็นข้อมูลที่ shell พกอยู่ ใช้ตอนหาคำสั่ง และเปลี่ยนได้สำหรับ session ปัจจุบัน

ทำไมควรรู้จัก which ตั้งแต่เนิ่นๆ

which ตอบคำถามที่ใช้การได้จริงมาก:

“ถ้าฉันพิมพ์ชื่อนี้ตอนนี้ shell จะไปรันไฟล์ไหน?”

ตัวอย่าง:

which brew
which rg
which python3

ถ้า which พิมพ์ path ออกมา แปลว่า shell resolve คำสั่งนี้ได้

ถ้าไม่เจอ ก็แปลว่าปัญหาไม่ใช่ “พิมพ์ชื่อผิด” แต่เป็น “shell หาไฟล์ที่ตรงกันใน search path ปัจจุบันไม่เจอ”

แค่แยกสองกรณีนี้ให้ชัด ก็ช่วยลดการติดตั้งซ้ำแบบเดาสุ่มได้มากแล้วครับ

export เปลี่ยนอะไร และไม่ได้เปลี่ยนอะไร

บรรทัดนี้มีประโยชน์มาก:

export PATH="$HOME/bin:$PATH"

มันแปลว่า:

  • เอา ~/bin
  • วางไว้ด้านหน้า
  • แล้วต่อ PATH เดิมตามหลังไป

การเปลี่ยนนี้มีผลกับ process ของ shell ปัจจุบัน

มันไม่ได้ย้อนเวลาไปแก้ shell ที่เปิดใหม่ในอนาคตให้เอง และไม่ได้ทำให้ค่าถาวรโดยอัตโนมัติ ถ้าปิดหน้าต่างนี้หรือเปิดหน้าต่างใหม่ การเปลี่ยนชั่วคราวจะหายไป เว้นแต่ startup file ของคุณจะเป็นคนเติมมันกลับมา

นี่แหละครับว่าทำไม 105 ถึงมาก่อน 106

105 สอนภาพจำ ส่วน 106 จะสอนว่าควรวางค่าพวกนี้ไว้ตรงไหนถ้าอยากให้มันอยู่ถาวร

โจทย์จากโลกจริง

สมมติว่าคุณมี helper script เล็กๆ ชื่อ shipit แล้วเก็บไว้ใน ~/bin

flow จะเป็นแบบนี้:

  1. รัน which shipit
  2. ถ้ายังไม่เจอ ให้เช็ก echo $PATH
  3. ทดลองแก้แบบชั่วคราวด้วย export PATH="$HOME/bin:$PATH"
  4. รัน which shipit อีกรอบ
  5. ถ้า which shipit พิมพ์ path ออกมา แปลว่าส่วนของ PATH ถูกแก้แล้ว
  6. ถ้าใช่วิธีนี้จริง ค่อยย้ายวิธีแก้นั้นไปเก็บใน shell config แทนการพิมพ์ export ซ้ำไปเรื่อยๆ

นี่เป็น workflow ของมือใหม่ที่ใช้ได้จริง เพราะมันแยกงานออกจากกันชัด:

  • ตรวจให้เห็นก่อน
  • ทดลองแก้
  • แล้วค่อยทำให้ถาวรในที่ที่ถูกต้อง

ส่วนที่ว่า script จะรันได้สมบูรณ์ไหมหลังจากนั้น เป็นอีกคำถามหนึ่งครับ PATH มีหน้าที่แค่ทำให้ shell “หาเจอ” ก่อน

ข้อผิดพลาดที่พบบ่อย

มอง PATH เป็นสตริงลึกลับก้อนเดียว แทนที่จะมองเป็นรายการที่มีลำดับ ลำดับคือหัวใจเลยครับ โฟลเดอร์ทางซ้ายถูกค้นก่อน

คิดว่าแค่มีไฟล์อยู่ที่ไหนสักแห่งก็แปลว่า shell รันได้ shell จะค้นหาเฉพาะโฟลเดอร์ที่อยู่ใน PATH ตอนนี้เท่านั้น

ลืมว่า export เป็นของชั่วคราว นี่คือ footgun ของ 105 เลยครับ ถ้าวิธีแก้หายไปในแท็บใหม่ มันไม่ได้เกิดเรื่องลี้ลับอะไร

เดาไปเรื่อยแทนที่จะเช็กด้วย which ใช้หลักฐานที่ถูกและเร็วที่สุดก่อน

กระโดดไปแก้ config file ทันทีโดยยังไม่ได้ทดลองใน shell ปัจจุบัน export ชั่วคราวคือการทดลองที่ดี ถ้ามันเวิร์ก ค่อยย้ายไปไว้ใน shell config

เปิดประตูไปต่อที่ไหน

จากนี้คำว่า “command not found” ควรจะรู้สึกแคบลง และแกะได้ง่ายขึ้นแล้วครับ

คุณดู PATH ได้ ดูได้ว่า executable ตัวไหนชนะ และพิสูจน์ได้ว่าการเปลี่ยนหนึ่งๆ เป็นของชั่วคราวหรือถาวร

ตอนต่อไปคือ terminal 106 — shell config on macOS ตรงนั้นเรื่อง PATH, alias, และ source ~/.zshrc จะเลิกเป็นการคัดลอกคำสั่งตามคนอื่น แล้วกลายเป็นสิ่งที่อธิบายได้ว่ากำลังทำอะไรอยู่ครับ

$ share

if this lesson helped, share the page with someone learning this topic. ถ้าโพสต์นี้มีประโยชน์ ส่งต่อหน้านี้ให้คนที่กำลังเรียนเรื่องนี้อยู่ได้เลย

share on facebook แชร์ไป Facebook

← back to all lessons ← กลับหน้าหลัก