스크립트에서는 ~를 쓰지 않고 $HOME만 사용함. ~는 대화형으로 명령을 입력할 때 편의상 쓰는 정도임. $HOME에도 함정은 있지만 다른 환경 변수와 같은 종류임.
스크립트에서는 최대한 명시적으로 작성하려고 함. 홈 디렉터리는 $HOME, /home/foo/.local/share/는 $XDG_DATA_HOME 처럼 지정함. 구체적으로 쓸수록 함정에 빠질 가능성이 줄어듦.
그냥 틸드를 따옴표로 감싸지 않으면 됨. Bash에서 “문자열”이라면 반사적으로 따옴표를 붙이곤 하지만, 사실 거의 모든 것이 문자열이며 대부분은 따옴표 없이 사용함. "ls" "-a" "foo"처럼 쓰지 않는 것만 봐도 알 수 있음.
상식이라 굳이 설명할 필요가 없을 것 같지만, 이 글을 보면 꼭 그렇지도 않은 모양임. 나도 이 문제를 겪었고, 그 덕분에 필요한 곳에만 따옴표를 적절히 쓰는 법을 배웠음. Bourne 호환 셸은 익히는 데 시간과 경험이 필요함. 이 점은 달라지지 않으며, 대안에도 저마다 주의할 점이 있음.
Python에서 셸 명령을 실행할 때는 예외임. 명령 전체를 그런 식으로 각각의 “문자열” 인수로 쪼개는 것을 그때 처음 봤음.
그냥 Bash를 쓰지 않는 것도 방법임. 코드 작성에 형편없는 언어이고 실수하기 쉬운 함정이 너무 많음.
따옴표를 너무 많이 쓰기도 하고, 너무 적게 쓰기도 함. 일반적인 규칙은 달러 기호가 들어간 표현식만 큰따옴표로 감싸고, 문자 그대로 사용할 특수 문자가 들어간 단어만 작은따옴표로 감싸는 것임.
예를 들면 foo, "$bar"/foo, baz:"$(cat example.txt)", exec cmd "$@", 'Die Hard', 'ke$ha'처럼 작성함.
다만 글의 예제는 따옴표를 바꿔도 해결되지 않음. export PATH="$PATH:/.local/bin/"을 export PATH="$PATH":/.local/bin/으로 바꿔도, 틸드 확장은 단어 시작에서만 일어나기 때문임.
핵심은 따옴표를 명시적이고 정확하게 쓰는 것임. 큰따옴표와 작은따옴표는 다르게 동작함.
매뉴얼을 읽어야 하는 이유를 보여주는 예임. 확장과 변수는 다르며, 따옴표 안에서는 확장이 일어나지 않음. 괄호로 둘러싸인 변수 확장은 단순한 확장과 다름. 에이전트가 기적을 일으키길 기다리지 말고 매뉴얼부터 읽어야 함.
~/.local/bin의 PATH 설정이 작동하지 않는 것을 보고 알아차렸어야 하지 않을까?
틸드와 직접 관련되지는 않지만, 큰 조직에서 Bash 설정이 얼마나 복잡해질 수 있는지 보여주는 일이 있었음. 예전 직장에서 특정 환경 변수를 .bashrc의 어디에서 설정하는지 찾고 있었고, 사용자 프로필에 설치된 기본 설정이나 /etc/에서 물려받은 값일 거라고 생각함. .bashrc가 다른 파일을 불러온다는 건 금방 알았지만, 한 단계 정도일 거라는 예상과 달리 최상위 원형 설정부터 사용자 프로필까지 10단계 넘게 파일을 불러오는 구조였음.
너무 복잡해서 헤매다가 이 https://unix.stackexchange.com/questions/813/how-to-determin... 글을 찾았음. Bash의 파일 로딩 과정을 추적하도록 설정하면 환경 변수가 어디서 설정되는지 좁혀갈 수 있음.
Linux 배포판이 스크립트 언어와 분리된 터미널·CLI 프로그램을 제공하면 좋겠음. 특정 셸에 묶이지 않는 공통 PATH 환경 변수를 쓰고, cd 실행과 python/git/cargo 등 임의의 프로그램 실행, 편리한 북마크와 자동 완성을 지원하는 형태임. 스크립트 언어와 CLI 애플리케이션을 한데 섞는 것이 이런 복잡함과 미묘한 동작의 근원처럼 느껴짐.
셸 스크립트를 쓰고 Python보다 Bash 등을 선호한다면 Bash/Fish/Zsh를 계속 쓰면 됨. GUI 없는 프로그램을 실행하거나 디렉터리 이동과 파일 작업만 할 때는 단순한 터미널을 쓰면 되는 구조임.
$PATH는 특정 셸에 종속되지 않음. 그리고 설명한 프로그램 자체가 셸이므로, 기존 해결책과 무엇이 다른지 모르겠음.
사용자별 데이터를 가리키는 심볼릭 링크를 위한 proc 형태의 파일 시스템이 있어야 한다고 늘 생각함. 왜 불가능한지 모르겠음. 보안 담당자들이 수많은 이유를 들겠지만, 그중 하나쯤은 타당할 수도 있겠음.
제대로 이해했다면 기능이 극도로 제한된 셸을 원하는 것임. Python이나 Bash로 10줄 정도면 직접 만들 수 있을 듯하지만, 널리 쓰일 것 같지는 않음.
~username 도 다룰 줄 알았음. 예를 들어 echo ~root ~pulse ~sddm 같은 용법임.
“나는 늘 잘 되던데”라고 하려다가 “Bash와 Zsh에서는 실제로 작동한다”는 부분을 봤음. 이식성을 고려할 때 피해야 할 Bash 전용 동작이 하나 더 늘어남.
많은 시스템에서 /bin/sh는 Bash나 Zsh가 아님. Bash 전용 동작에 의존하거나 일일이 확인하기 귀찮다면 셔뱅을 #!/bin/bash로 명시해야 함. Debian 계열에서는 보통 dash를 사용함.
BusyBox를 사용하는 여러 임베디드 환경에서는 BusyBox의 ash를 사용함. 이런 경계 사례에 부딪히며 배우는 기회를 대체로 즐기는 편임.
초기 Cursor가 내 저장소 루트에 ~라는 이름의 디렉터리를 만들었던 일이 떠오름. 그 실수를 되돌리는 과정이 무척 무서웠음!
일부 이름이나 구문을 예약해 두는 것도 나쁜 생각은 아닐 수 있음. 열성 지지자들이 뭐라고 하든, 셸에는 기능이 충분히 많으니 어떤 것은 금지하는 편이 올바른 설계일 수도 있음.