cut {옵션} "파일이름"
-c 문자 위치를 기준으로 지정된 부분을 추출
-s <구분자> 구분자 사용
-d <구분자> 파일 내 필드로 따져서 추출
-f 필드 번호 기준으로 추출
ex) /etc/passwd 파일에서 첫 번째 필드와, 세 번째부터 다섯 번째 필드를 : 구분자를 기준으로 나누어서 마지막 5줄만 출력

ex) cut으로 첫 영역만 뽑기

paste {옵션} "파일이름" "파일이름"
-s 한 파일 내용 한줄로 보여준 뒤 다른 파일 내용 보여줌
-d <구분자> 기본 구분자인 탭 대신 다른 문자로 열을 구분

diff {옵션} "파일이름" "파일이름"
-b 스페이스 무시하고 비교

grep {옵션} {패턴} "파일이름"
-v 패턴을 포함하지 않는 행 출력
-i 대소문자 구분 X
-n 줄 번호 함께 추력
-l 파일명 출력
-c 일치하는 라인 개수


sort {옵션} "파일이름"
-f 대소문자 구분 X
-r 내림차순 정렬
-b 스페이스 무시
-k 필드 번호 표시
-t <구분자> 구부낮 사용
-n 숫자 순서로 정렬


sed {옵션} "파일이름"
p 행 출력
d 선택한 행 삭제
-f 파일 안 내용 실행
s/가/나/g '가'문자열을 '나'문자열로 대체
-e 다중 편집
-q sed 종료






현재 쉘은 아래 명령어를 통해 확인할 수 있다.
echo $SHELL

$0은 현재 실행중인 Shell 프로그램이며 $1 ~ $숫자는 매개변수를 나타낸다.

Shell에 의해 자동으로 지정되는 변수들이다.

백슬래쉬()
일반적인 프로그래밍 언어에서 문자 출력시 사용할 때와 같다.
백슬래쉬 뒤에 오는 문자를 그대로 출력한다.

단일 인용부호(')
단일 인용부호로 감싼 문자는 별도의 기능을 하지 않고 그대로 출력한다.
백슬래쉬를 범위지정해서 사용한다 생각하면 편하다.

이중 인용부호(")
이중 인용부호로 묶은 문자는 \,`,",$ 를 제외하고 그대로 출력한다.

&& -> 앞의 명령어가 참일 경우에만 뒤에 명령어 실행

|| -> 앞의 명령어가 거짓일 경우에만 뒤에 명령어 실행

; -> 앞의 명령어가 거짓이라도 뒤에 명령이 실행

while -> 조건이 참일 경우에만 반복
while [ condition ]
do
action
done

until -> 조건이 거짓일 경우에만 반복
Until [ end_condition ]
do
action
done
for
for variable_name in value1 value2 ...
do
action on $variable_name
done
이론만 본다고 사용할 수 있는게 아니다 직접 작성해 보자...
1. 로그인/로그아웃 감지
해당 기능 구현을 위해 알아야 될 게 몇가지 있다.

tty -> 내부에서 쉘 접근
pts -> 원격 쉘 접근
tty/whoami를 통해 현재 사용자와 현재 실행중인 쉘이 몇 번 tty인지 알 수 있다.
who를 통해 현재 시스템의 전체 사용자/tty 정보를 알 수 있다.
#!/bin/bash
CURRENT_USER="$(whoami)" #현재 사용자
CURRENT_TTY="$(tty)" #현재 실행중인 쉘이 몇번 tty인가?
declare -A KNOWN_SESSIONS #연관 배열임 declare에 A옵션 키:값
# 스크립트 시작 시점에 로그인해 있던 세션을 기록해 둠
while read user tty _; do
KNOWN_SESSIONS["$user:$tty"]=1
done < <(who) # who 실행 결과를 임시 파일 디스크립터로 만들고 그걸 while의 표준입력으로 써라
echo "$CURRENT_USER($CURRENT_TTY)에서 로그인/로그아웃 감시 시작"
while true; do
#로그인 감지
while read user tty _; do
key="$user:$tty"
if [ -z "${KNOWN_SESSIONS[$key]}" ]; then
echo "사용자 '$user'가 $tty에서 로그인했습니다."
KNOWN_SESSIONS["$key"]=1
fi
done < <(who)
#로그아웃 감지
unset CURRENT_SESSIONS
declare -A CURRENT_SESSIONS
while read user tty _; do
CURRENT_SESSIONS["$user:$tty"]=1
done < <(who)
for key in "${!KNOWN_SESSIONS[@]}"; do
if [ -z "${CURRENT_SESSIONS[$key]}" ]; then
user="${key%:*}"
tty="${key#*:}"
echo "사용자 '$user'가 $tty에서 로그아웃했습니다."
unset 'KNOWN_SESSIONS[$key]'
fi
done
sleep 5 # 5초마다 확인
done
CURRENT_USER="$(whoami)"
명령어를 변수에 넣을떄 $() 또는 백틱(`)으로 명령어를 감싸면 된다. 근데 $() 사용을 권장한다.
declare -A KNOWN_SESSIONS
연관 배열 → 딕셔너리 생각하면 편함 키:값 쌍으로 저장하는거
쉘 스크립트에서 연관 배열 사용하려면 declare -A 사용함
while ~ do ~ done < <(who)
프로세스 치환 <(명령어) → 명령어 출력을 파일처럼 취급해 넘길 때 사용한다.
who 명령어의 결과를 프로세스 치환을 통해 while의 표준입력(<)으로 넣는다.
who 실행 결과를 임시 파일 디스크립터로 만들고 그걸 while의 표준입력으로 써라
while read user tty _; do
while문 조건절에서 who의 값을 read로 받는다.
who 명령어는 wiker pts/0 2025-11-02 23:37 (192.168.72.1) 해당 형태로 들어온다 .
user에 첫번쨰 값인 사용자 이름(wiker)을 tty에 터미널 정보(pts/0)을 받는다. 이후 _를 통해 그 뒤 값은 무시한다. (_는 관용적으로 나머지는 안 쓴다는 뜻으로 쓴다고 한다)
KNOWN_SESSIONS["$user:$tty"]=1
KNOWN_SESSIONS 연관 배열에 키(user:tty):값(1) 형태로 넣는다.
key="$user:$tty”
프로세스 치환으로 who 결과 while에 표준입력으로 받아서 read를 통해 user, tty, _에 값을 받았고 key라는 변수에 user:tty 형태로 값을 저장함 → 연관배열 KNOWN_SESSIONS의 key 값임
if [ -z "${KNOWN_SESSIONS[$key]}" ]; then
if문에서 -z는 -z "$VAR” 형태로 쓰이며 $VAR가 비었으면 참을 반환함
여기서 만약 key값이 연관 배열에 없다? 그럼 처음 들어온 값임을 알수 있다.
KNOWN_SESSIONS["$key"]=1
그럼 echo를 통해 텍스트 출력후 key값에 value를 1을 넣어 저장하여 연관 배열에 기록함
#!/bin/bash
LOG=/var/log/secure
#tail로 로그 읽는데 -n 0 넣어서 이전거 무시 -F로 지속적으로 읽어오기 |로 결과 while로 넘김
sudo tail -n 0 -F "$LOG" | \
while read line; do
time=$(echo "$line" | awk '{printf "%s %s %s", $1, $2, $3}') #시간
case "$line" in
*"pam_unix(su:session): session opened for user "*) #su로 계정 변경
after_user=$(echo "$line" | awk -F'for user ' '{print $2}' | awk '{print $1}') #이동할 목적지
before_user=$(echo "$line" | awk -F' by ' '{print $2}') #출발지
echo "[$time] SU OPENED: $before_user -> $after_user"
;;
*"pam_unix(su:session): session closed for user "*) #exit로 원래 계정으로 복귀
before_user=$(echo "$line" | awk -F'for user ' '{print $2}' ) #출발지
echo "[$time]SU CLOSED: $before_user"
;;
*) #case default 동작 x
:
;;
esac
done
unset CURRENT_SESSIONS
declare -A CURRENT_SESSIONS
CURRENT_SESSIONS에 현재 who에 값을 저장하는데 unset을 통해 한번 비워준 이유는 루프를 돌떄 이전값을 초기화하기 위해서 이다.
for key in "${!KNOWN_SESSIONS[@]}"; do
for 문에서 !KNOWN_SESSIONS[@] 는 연관배열 KNOWN_SESSIONS의 모든 값(@)들에서 !는 키값을 가져와라 라는 뜻
KNOWN_SESSIONS의 모든 값에서 키값을 하나씩 가져와 key라는 변수에 넣는다
user="${key%:*}" tty="${key#*:}"
key가 wiker:pts/0 일 때
%는 뒤에 문자 때는 거 → %:* 는 :부터 뒤에 모든값 때는거 → wiker가 저장됨
#은 앞에 문자 때는 거 → #*: 는 :부터 앞에 모든 문자 때는 거 → pts/0이 저장됨
추가로 %%, ##는 가장 먼 값 기준으로 땜 ex) key가 wiker:user1:user2:pts/0 일때 %%:을 쓰면 wiker만 남고 %:를 쓰면 wiker:user1:user2가 남는다.
unset 'KNOWN_SESSIONS[$key]'
unset은 배열 값 해제할때 씀 key라는 키값을 가진 거 해제