Bash 스크립트를 즉시 더 깔끔하고 읽기 쉬운 코드로 바꾸는 4가지 간단한 습관
당신이 초보자이든, 경험 많은 Bash 작성 베테랑이든, 항상 새로운 팁을 배울 여지는 있습니다. 가장 좋은 팁 중 일부는 배우는 데 몇 초밖에 걸리지 않지만, 그 효과는 매우 커서 전체 접근 방식을 바꿔버립니다. 저는 Bash 스파게티를 잘 정리된 예술 작품으로 바꿔줄 네 가지 팁을 가지고 있습니다.
파이프라인을 여러 줄로 나누어 작성하세요
파이프라인은 "|" 문자로 연결된 명령어들의 연속입니다. 각 명령어는 이전 명령어의 출력을 처리합니다. 일반적으로 사람들은 파이프라인을 이렇게 작성합니다:
fd --type file --exec ls -l | awk '{print $3}' | grep --invert-match '^$' | sort | uniq --count | sed --regexp-extended 's/(root|user)/\1-owned files/'기능적이긴 하지만 통일성이 없고 편집하기 어렵다.
이전 명령어는 현재 디렉토리를 깊이 스캔하고 서로 다른 파일 소유자의 수를 합산합니다. 파이프라인을 설명하기 위해 일부러 길게 작성되었습니다. 가장 세련되거나 효율적인 방법은 아니며, 동일한 결과는 find . -type f -printf '%u\n' | sort | uniq --count 명령으로도 얻을 수 있습니다.
소스 코드(또는 모든 텍스트)를 스캔할 때 우리는 패턴을 찾고 있습니다. 위의 예시에서, 아마도 무심코 명령어 사이의 경계를 정의하는 "|" 문자를 찾았을 것입니다. 그러나 명령어와 경계가 모두 같은 수평 축에 존재하기 때문에, 파이프를 시각적으로 찾고 그 사이의 부분을 이해하는 데 노력을 기울여야 합니다. 이는 불필요하게 부담스러우므로, 집중력을 확보하기 위해 추가로 수직 공간을 사용할 수 있습니다:
fd --type file --exec ls -l \
| awk '{print $3}' \
| grep --invert-match '^$' \
| sort \
| uniq --count \
| sed --regexp-extended 's/(root|user)/\1-owned files/'우리의 뇌는 시각 처리에 매우 능숙하며, 이를 직접 활용할 수 있다면 작업 기억을 의도에 집중하는 데 사용할 수 있습니다. 각 명령이 어디서 시작하고 끝나는지 파악하려고 애쓰는 대신, 우리는 일관된 세로 파이프 줄을 볼 수 있습니다.
이 접근 방식은 IDE에서 편집하기도 더 쉽게 만듭니다. 왜냐하면 IDE는 종종 전체 줄을 삭제하거나 이동할 수 있게 해주기 때문입니다.
함수를 조합 가능하게 만드세요
이전 명령어를 더 읽기 쉽고 조합 가능하게 만들기 위해 나눠봅시다.
“Composable” 코드는 기본 요소(예: 함수)로 다양한 것들을 조립할 수 있다는 의미입니다—마치 벽돌로 벽을 조립하는 것처럼요.
우선, 아마도 원시 값을 제공하는 함수를 원할 것이고, 그런 다음 우리는 그것을 다양한 방식으로 확장할 수 있습니다:
file_stats() {
fd --type file --exec ls -l
}그런 다음 우리는 결과에서 무언가를 선택하고 싶습니다:
owner() {
awk '{print $3}'
}그런 다음 결과를 합산하세요:
tally() {
grep --invert-match '^$' \
| sort \
| uniq --count
}마지막으로, 최종 메시지를 더 명확하게 수정하세요:
clarify_ownership() {
sed --regexp-extended 's/(root|user)/\1-owned files/'
}이제 우리는 이 새로운 요소들로 파이프라인을 구성할 수 있습니다:
file_stats \
| owner \
| tally \
| clarify_ownership
더 읽기 쉬울 뿐만 아니라, 개별 요소들도 변경할 수 있습니다. 예를 들어:
clarify_date() {
# Replace a month (e.g., "Feb") with "files modified in <month>."
sed --regexp-extended 's/([[:alpha:]]+)/files modified in \1./'
}
mod_date() {
# Modification month column from "ls -l."
awk '{print $6}'
}
file_stats \
| mod_date \
| tally \
| clarify_date"clarify_date" 함수는 정규 표현식과 POSIX 문자 클래스를 사용합니다.
제 코드는 과도하게 설계되어 있지만, 이는 단지 예시용일 뿐입니다. 실제로는 처음에 더 큰 함수로 시작하고 필요할 때만 그것을 나누게 될 것입니다.
명령을 분기할 때 특히 if-else 블록보다 case 문을 사용하는 것을 선호하세요
나는 switch (case) 문을 좋아한다. if-else 조건 분기는 지저분해 보이지만, switch는 더 간결하고 집중된 느낌이 든다. 나는 보통 스크립트의 끝부분에서 CLI 인자를 함수와 매핑(일명 디스패처)하기 위해 사용한다.
이름이 잘 지어지고 조합 가능한 함수를 가진 디스패처가 있으면, 우리는 의도가 훨씬 더 명확한 스크립트를 얻습니다:
case "$1" in
dates)
file_stats \
| mod_date \
| tally \
| clarify_date;;
ownership)
file_stats \
| owner \
| tally \
| clarify_ownership;;
esac무슨 일이 일어나고 있는지 이해하는 것은 어렵지 않으며, 단지 몇 줄만 바꾸어도 결과를 완전히 바꿀 수 있습니다.
저는 다른 글에서 케이스 디스패처(case dispatcher)에 대해 더 자세히 설명합니다. 그 글에서는 CLI 인수를 처리하는 관용적(자연스럽거나 권장되는) 방법을 자세히 다룹니다. 여기서 읽은 내용과 그 조언을 결합하면 디스패처를 가능한 한 깔끔하게 만들 수 있습니다.
짧은 조건 분기에는 한 줄짜리 문장을 사용하세요
코드가 많을수록 읽는 것도 많아집니다. 작성하는 코드가 적을수록 좋습니다—이해하기 쉽기만 하다면요. if 문이 그중 하나의 원인입니다. 프로그램 상태를 확인하기 위해(즉, 어설션을 위해) 코드에 if 문이 흩어져 있을 때, 다음과 같이 보일 수 있습니다:
if [[ "$something" == "foo" ]]; then
exit 1
fi
if [[ "$something_else" == "bar" ]]; then
echo "'\$something_else' cannot be 'bar'"
exit 1
fi
if ! [[ -f "$file_path" ]]; then
echo "'\$file_path' must be a file path"
exit 1
fi
스크립트 전체의 모든 조건 분기를 고려하면, 위 코드는 가독성 문제를 일으키는 것처럼 보입니다. 앞서 언급했듯이, 저는 코드가 시각적으로 쉽게 훑어볼 수 있어야 한다고 믿으며, 이를 달성하기 위해 스타일을 다양하게 사용할 수 있습니다. 예를 들어, 저는 종종 다음과 같은 방식으로 어설션을 사용합니다:
[[ "$something" == "foo" ]] && exit 1
[[ "$something_else" == "bar" ]] && { echo "'\$something_else' cannot be 'bar'"; exit 1; }
[[ -f "$file_path" ]] || { echo "'\$file_path' must be a file path"; exit 1; }닫는 '}' 전에 반드시 ';'를 포함해야 합니다.
저는 이것들을 스크립트와 함수의 맨 위에서 사용합니다. 이런 한 줄 코드를 보면, 조건 분기문이 아니라 assertion임을 알 수 있습니다. 생각할 필요 없이 바로 무슨 일이 일어나고 있는지 빠르게 판단하는 데 도움이 됩니다. 에러 메시지로 코드가 의미하는 바를 이해할 수 있기 때문에 주석이 필요하지 않습니다.
가독성을 위해서는 일관성이 핵심입니다. 사소한 세부 사항에 반복되는 패턴을 사용할 때, 우리의 마음은 그것들을 무시하고 코드의 의도에 집중할 수 있습니다.
코드를 어떻게 작성할지 선택하는 것은 중요한 요소이며, 기능성만큼 시각적 미학과도 관련이 있습니다. 아마 인기 없는 의견일 수 있지만, 이것은 과학에 근거한 것입니다. 네, 제가 이렇게 주장하는 이유는 코드의 미학과 사용자 인터페이스 사이에 유사점을 그릴 수 있기 때문입니다.
사용자 경험 디자인(사용자 인터페이스와 관련된)의 과학은 사용자가 제품과 원하는 방식으로 상호작용하도록 유도하는 것과 관련이 있습니다. 여기에는 공백, 레이아웃 등을 사용하여 사용자의 결정을 영향을 주는 것이 포함됩니다. 일반적인 원칙은 사용자의 인지적 부담을 줄이고 명확한 신호를 보내는 것입니다. 미학적으로 만족스러운 코드를 통해 우리는 동일한 목표를 달성하려고 하지만, 이는 독자를 대상으로 합니다.
일관되고 직관적인 코드를 작성하고, 시각적 패턴을 통해 요소들을 구분하세요—잘 설계된 사용자 인터페이스가 그러하듯 독자에게 명확한 신호를 보내세요.


