#!/bin/bash
# Bash 스크립트: 이벤트 발생 시 현재 시간 기반 파일에 로그 기록하기
# 현재 시간의 파일 이름 생성 함수
generate_filename() {
# 현재 날짜와 시간 기반으로 파일 이름 생성
echo "$(date +'%Y%m%d%H%M%S').txt"
}
# 로그 기록 함수
log_event() {
local log_message=$1 # 로그 메시지 (사용자가 전달)
local filename=$(generate_filename) # 파일 이름 생성
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $log_message" > "$filename" # 로그 메시지 기록
echo "로그가 $filename 에 저장되었습니다."
}
# 이벤트 감지 및 실행 (여기서는 예제용으로 무한 루프와 키 입력 감지 사용)
while true; do
echo "아무 키나 눌러 이벤트를 트리거하거나 'q'를 눌러 종료하세요."
read -n 1 input # 한 문자 입력받기
if [[ $input == "q" ]]; then
echo -e "\n프로그램을 종료합니다."
break
else
# 로그 메시지를 작성 (여기서는 예제 메시지 사용)
log_event "사용자가 이벤트를 트리거했습니다."
fi
done
스크립트 설명
generate_filename 함수:
현재 시간을 기반으로 파일 이름을 생성합니다. 결과 예시: 20241210153030.txt.
pre-commit을 통해 ShellCheck를 실행하려면 .pre-commit-config.yaml에 후크를 추가하세요.
repos:
- repo: https://github.com/koalaman/shellcheck-precommit
rev: v0.7.2
hooks:
- id: shellcheck
# args: ["--severity=warning"] # Optionally only show errors and warnings
트래비스 CI
Travis CI는 이제 기본적으로 ShellCheck를 통합했으므로 수동으로 설치할 필요가 없습니다.
여유롭게 업그레이드하거나 다음 사항을 확인하기 위해 여전히 그렇게 하려는 경우
최신 릴리스를 사용하려면 아래 단계에 따라 바이너리 버전을 설치하세요.
사전 컴파일된 바이너리 설치
사전 컴파일된 바이너리는 tar.xz 파일로 제공됩니다. 압축을 풀려면 다음을 확인하세요.
xz가 설치되었습니다.
Debian/Ubuntu/Mint에서는 apt install xz-utils를 사용해 설치할 수 있습니다.
Redhat/Fedora/CentOS의 경우 yum -y install xz.
간단한 설치 프로그램은 다음과 같은 작업을 수행할 수 있습니다.
scversion="stable" # or "v0.4.7", or "latest"
wget -qO- "https://github.com/koalaman/shellcheck/releases/download/${scversion?}/shellcheck-${scversion?}.linux.x86_64.tar.xz" | tar -xJv
cp "shellcheck-${scversion}/shellcheck" /usr/bin/
shellcheck --version
소스에서 컴파일하기
이 섹션에서는 소스 디렉토리에서 ShellCheck를 빌드하는 방법을 설명합니다. ShellCheck는 Haskell로 작성되었으며 컴파일하는 데 2GB의 RAM이 필요합니다.
Cabal 설치
ShellCheck는 Cabal을 사용하여 빌드되고 패키징됩니다. 시스템의 패키지 관리자(예: apt-get, brew, emerge, yum 또는 zypper)에서 패키지 cabal-install을 설치합니다.
macOS(OS X)에서는 brew를 사용하여 Cabal을 빠르게 설치할 수 있습니다. 소스에서 컴파일하려고 하면 30분 이상 걸리는 반면, brew를 사용하면 몇 분 만에 설치가 완료됩니다.
git clone을 통해 이 저장소를 복제하고 cd를 통해 ShellCheck 소스 디렉토리로 이동하여 빌드/설치합니다.
$ cabal install
이렇게 하면 ShellCheck가 컴파일되어 ~/.cabal/bin 디렉토리에 설치됩니다.
이 디렉토리를 PATH에 추가합니다(bash의 경우 ~/.bashrc에 추가):
export PATH="$HOME/.cabal/bin:$PATH"
로그아웃했다가 다시 로그인하여 PATH가 올바르게 설정되었는지 확인하세요.
$ which shellcheck
~/.cabal/bin/shellcheck
기본 Windows에서는 PATH가 이미 설정되어 있어야 하지만 시스템은
레거시 코드 페이지를 사용할 수 있습니다. cmd.exe, powershell.exe 및 Powershell ISE에서
Raster 글꼴이 아닌 TrueType 글꼴을 사용하고 활성 글꼴을 설정해야 합니다.
chcp를 사용하여 코드 페이지를 UTF-8(65001)로 변경:
그렇다면 ShellCheck는 어떤 종류의 것들을 찾습니까? 감지된 문제의 불완전한 목록은 다음과 같습니다.
인용
ShellCheck는 여러 유형의 잘못된 인용을 인식할 수 있습니다.
echo $1 # Unquoted variables
find . -name *.ogg # Unquoted find/grep patterns
rm "~/my file.txt" # Quoted tilde expansion
v='--verbose="true"'; cmd $v # Literal quotes in variables
for f in "*.ogg" # Incorrectly quoted 'for' loops
touch $@ # Unquoted $@
echo 'Don't forget to restart!' # Singlequote closed by apostrophe
echo 'Don\'t try this at home' # Attempting to escape ' in ''
echo 'Path is $PATH' # Variables in single quotes
trap "echo Took ${SECONDS}s" 0 # Prematurely expanded trap
unset var[i] # Array index treated as glob
조건문
ShellCheck는 다양한 유형의 잘못된 테스트 문을 인식할 수 있습니다.
[[ n != 0 ]] # Constant test expressions
[[ -e *.mpg ]] # Existence checks of globs
[[ $foo==0 ]] # Always true due to missing spaces
[[ -n "$foo " ]] # Always true due to literals
[[ $foo =~ "fo+" ]] # Quoted regex in =~
[ foo =~ re ] # Unsupported [ ] operators
[ $1 -eq "shellcheck" ] # Numerical comparison of strings
[ $n && $m ] # && in [ .. ]
[ grep -q foo file ] # Command without $(..)
[[ "$$file" == *.jpg ]] # Comparisons that can't succeed
(( 1 -lt 2 )) # Using test operators in ((..))
[ x ] & [ y ] | [ z ] # Accidental backgrounding and piping
자주 오용되는 명령어
ShellCheck는 명령이 잘못 사용된 경우를 인식할 수 있습니다.
grep '*foo*' file # Globs in regex contexts
find . -exec foo {} && bar {} \; # Prematurely terminated find -exec
sudo echo 'Var=42' > /etc/profile # Redirecting sudo
time --format=%s sleep 10 # Passing time(1) flags to time builtin
while read h; do ssh "$h" uptime # Commands eating while loop input
alias archive='mv $1 /backup' # Defining aliases with arguments
tr -cd '[a-zA-Z0-9]' # [] around ranges in tr
exec foo; echo "Done!" # Misused 'exec'
find -name \*.bak -o -name \*~ -delete # Implicit precedence in find
# find . -exec foo > bar \; # Redirections in find
f() { whoami; }; sudo f # External use of internal functions
초보자들이 흔히 하는 실수
ShellCheck는 초보자가 흔히 겪는 구문 오류를 많이 인식합니다.
var = 42 # Spaces around = in assignments
$foo=42 # $ in assignments
for $var in *; do ... # $ in for loop variables
var$n="Hello" # Wrong indirect assignment
echo ${var$n} # Wrong indirect reference
var=(1, 2, 3) # Comma separated arrays
array=( [index] = value ) # Incorrect index initialization
echo $var[14] # Missing {} in array references
echo "Argument 10 is $10" # Positional parameter misreference
if $(myfunction); then ..; fi # Wrapping commands in $()
else if othercondition; then .. # Using 'else if'
f; f() { echo "hello world; } # Using function before definition
[ false ] # 'false' being true
if ( -f file ) # Using (..) instead of test
스타일
ShellCheck는 스타일 개선을 위한 제안을 할 수 있습니다:
[[ -z $(find /tmp | grep mpg) ]] # Use grep -q instead
a >> log; b >> log; c >> log # Use a redirection block instead
echo "The time is `date`" # Use $() instead
cd dir; process *; cd ..; # Use subshells instead
echo $[1+2] # Use standard $((..)) instead of old $[]
echo $(($RANDOM % 6)) # Don't use $ on variables in $((..))
echo "$(date)" # Useless use of echo
cat file | grep foo # Useless use of cat
데이터 및 입력 오류
ShellCheck는 데이터 및 입력과 관련된 문제를 인식할 수 있습니다.
args="$@" # Assigning arrays to strings
files=(foo bar); echo "$files" # Referencing arrays as strings
declare -A arr=(foo bar) # Associative arrays without index
printf "%s\n" "Arguments: $@." # Concatenating strings and arrays
[[ $# > 2 ]] # Comparing numbers as strings
var=World; echo "Hello " var # Unused lowercase variables
echo "Hello $name" # Unassigned lowercase variables
cmd | read bar; echo $bar # Assignments in subshells
cat foo | cp bar # Piping to commands that don't read
printf '%s: %s\n' foo # Mismatches in printf argument count
eval "${array[@]}" # Lost word boundaries in array eval
for i in "${x[@]}"; do ${x[$i]} # Using array value as key
견고성
ShellCheck는 스크립트의 견고성을 개선하기 위한 제안을 할 수 있습니다.
rm -rf "$STEAMROOT/"* # Catastrophic rm
touch ./-l; ls * # Globs that could become options
find . -exec sh -c 'a && b {}' \; # Find -exec shell injection
printf "Hello $name" # Variables in printf format
for f in $(ls *.txt); do # Iterating over ls output
export MYVAR=$(cmd) # Masked exit codes
case $version in 2.*) :;; 2.6.*) # Shadowed case branches
휴대성
ShellCheck는 shebang에서 지원하지 않는 기능을 사용할 때 경고합니다. 예를 들어, shebang을 #!/bin/sh로 설정하면 ShellCheck는 checkbashisms와 유사한 이식성 문제에 대해 경고합니다.
echo {1..$n} # Works in ksh, but not bash/dash/sh
echo {1..10} # Works in ksh and bash, but not dash/sh
echo -n 42 # Works in ksh, bash and dash, undefined in sh
expr match str regex # Unportable alias for `expr str : regex`
trap 'exit 42' sigint # Unportable signal spec
cmd &> file # Unportable redirection operator
read foo < /dev/tcp/host/22 # Unportable intercepted files
foo-bar() { ..; } # Undefined/unsupported function name
[ $UID = 0 ] # Variable undefined in dash/sh
local var=value # local is undefined in sh
time sleep 1 | sleep 5 # Undefined uses of 'time'
기타
ShellCheck는 다음과 같은 다양한 문제를 인식합니다.
PS1='\e[0;32m\$\e[0m ' # PS1 colors not in \[..\]
PATH="$PATH:~/bin" # Literal tilde in $PATH
rm “file” # Unicode quotes
echo "Hello world" # Carriage return / DOS line endings
echo hello \ # Trailing spaces after \
var=42 echo $var # Expansion of inlined environment
!# bin/bash -x -e # Common shebang errors
echo $((n/180*100)) # Unnecessary loss of precision
ls *[:digit:].txt # Bad character class globs
sed 's/foo/bar/' file > file # Redirecting to input
var2=$var2 # Variable assigned to itself
[ x$var = xval ] # Antiquated x-comparisons
ls() { ls -l "$@"; } # Infinitely recursive wrapper
alias ls='ls -l'; ls foo # Alias used before it takes effect
for x; do for x; do # Nested loop uses same variable
while getopts "a" f; do case $f in "b") # Unhandled getopts flags
추천사
처음에는 "shellcheck가 최고야"라고 생각하지만 그다음에는 "뭐야, 아직도 bash를 쓰고 있는 거야?"라고 생각하게 됩니다.
shc는 명령줄에 지정된 스크립트를 가져와 C 소스 코드를 생성합니다. 생성된 소스 코드는 컴파일되어 링크되어 스트립된 바이너리 실행 파일을 생성합니다.
컴파일된 바이너리는 여전히 쉘 코드의 첫 번째 줄에 지정된 쉘(즉, shebang)에 종속됩니다(즉, #!/bin/sh). 따라서 shc는 완전히 독립적인 바이너리를 생성하지 않습니다.
shc 자체는 cc와 같은 컴파일러가 아니라 셸 스크립트를 인코딩하고 암호화하여 만료 기능이 추가된 C 소스 코드를 생성합니다. 그런 다음 시스템 컴파일러를 사용하여 원래 스크립트와 정확히 동일하게 동작하는 스트립된 바이너리를 컴파일합니다. 실행 시 컴파일된 바이너리는 셸 -c 옵션으로 코드를 해독하고 실행합니다.
설치
./configure
make
sudo make install
참고automake 버전으로 인해 make가 실패하는 경우, 위 명령을 실행하기 전에 ./autogen.sh를 실행하세요.
이 플래그는 현재 실험 상태이며 모든 시스템에서 작동하지 않을 수 있습니다. 이 플래그는 기본 셸에서만 작동합니다. 예를 들어, -H 플래그로 bash 스크립트를 컴파일하면 결과 실행 파일은 기본 셸이 bash인 시스템에서만 작동합니다. 일반적으로 /bin/sh인 기본 셸을 변경할 수 있으며, 이는 bash 또는 dash 등과 같은 다른 셸에 대한 링크일 뿐입니다.
또한 -H는 위치 매개변수와 함께 작동하지 않습니다(아직은)
테스트
./configure
make
make check
알려진 제한 사항
shc를 사용할 때의 유일한 제한 사항은 _SC_ARG_MAX 시스템 구성 매개변수입니다.
이는 exec 함수의 인수의 최대 길이를 제한하여 shc의 실행 가능한 스크립트의 최대 길이를 제한합니다.