bash script (backup)2021. 9. 24. 16:56

 

반달가면 이글루에서 백업 - http://bahndal.egloos.com/524868

 

find 명령에서 -maxdepth 옵션과 -mindepth 옵션을 사용하면 검색하는 디렉토리의 범위(깊이)를 조정할 수 있다. 깊이 0이면 시작하는 디렉토리 자체이다. 깊이 1이면 시작하는 디렉토리 내부의 파일/디렉토리를, 깊이 2이면 그 아래에 있는 하위 디렉토리 내부의 파일/디렉토리를 의미한다. 말로 설명하면 좀 복잡한데, 아래의 예시들을 보면 어느 정도 감이 잡힐 것이다.

# /tmp 디렉토리 및 하위의 파일/디렉토리 모두 출력

find /tmp

/tmp

/tmp/my_file.txt

/tmp/my_dir1/my_file1.txt

...

 

# /tmp 디렉토리만 출력

find /tmp -maxdepth 0

/tmp

 

# /tmp 디렉토리안에 있는 파일/디렉토리 출력, 하위 디렉토리는 제외(-maxdepth 1)

find /tmp -maxdepth 1

/tmp/my_file.txt

/tmp/my_dir1

...

 

# /tmp 디렉토리 안에 있는 파일(-type f)만 출력, 하위 디렉토리 제외

find /tmp -maxdepth 1 -type f

/tmp/my_file.txt

...

 

# 홈디렉토리를 제외하고 그 하위디렉토리부터(-mindepth 2) 검색

find ~/ -mindepth 2

/tmp/my_dir1/my_file1.txt

...

 

위의 예시에서 보듯이, 시작 디렉토리가 깊이 0, 그 아래의 파일/디렉토리가 깊이 1, 그 아래의 파일/디렉토리가 깊이 2 이런식으로 생각하면 된다.

 

디렉토리 구조가 복잡할 경우에 특정 범위의 항목에서만 파일이나 디렉토리를 검색할 수 있어서 매우 편리하다.

 

Posted by 반달가면

댓글을 달아 주세요

bash script (backup)2021. 9. 24. 16:50

 

반달가면 이글루에서 백업 - http://bahndal.egloos.com/524038

 

특정 디렉토리에서 용량을 가장 많이 차지하고 있는 파일을 찾아야 할 때가 있다. 일일이 용량을 눈으로 확인해도 되지만 파일 수가 아주 많을 때는 좀 더 우아한(?) 방법을 찾아야 한다.

우선 정렬을 위한 sort 명령을 생각해 보자.

# 8 에서 11까지 출력하고 정렬
seq 8 11 | sort
10
11
8
9

 

seq 명령을 이용해서 8에서 11까지 출력한 후 이것을 sort 명령으로 정렬하면 출력한 수자를 문자로 간주하고 정렬하므로 위의 예시와 같은 결과가 나온다. 수자를 수자로 인식하기 위해서는 -g 옵션을 사용하면 된다.

seq 8 11 | sort -g
8
9
10
11

자, 이제 가장 큰 수부터 출력하도록 정렬 순서를 반대로 한다. -r 옵션을 사용하자.

seq 8 11 | sort -g -r
11
10
9
8

큰 파일들을 찾기 위한 준비가 끝났다. ls -l 명령으로 디렉토리에 있는 파일명과 크기를 같이 출력한 후에 awk 명령으로 파일 크기와 파일명만 골라내고, 이 결과값을 sort 명령으로 정렬한다.

# ~/my_dir 디렉토리의 파일 용량 TOP10 출력
ls -l ~/my_dir | grep "^\-" | awk '{print $5 "\t" $9}' | sort -g -r | head -10

 

ls -l 명령의 출력에서 디렉토리나 심볼릭 링크를 제외하고 파일만 골라내기 위해 '-' 부호로 시작하는 행만 선별하고(grep 명령)

파일 용량(5번째 항목)과 파일명(9번째 항목)만 골라서 탭(tab)을 구분자로 해서 출력한 후(awk 명령)

가장 용량이 큰 항목부터 출력하도록 정렬(sort 명령), 여기서 맨 처음 10개 행만 표시(head 명령)

용량을 많이 차지하고 있는 디렉토리를 찾고 싶다면 du 명령 이용하면 된다. 아래의 예시를 보자.

# ~/my_dir 디렉토리 하위에 있는 디렉토리 용량 TOP10 출력
du ~/my_dir | sort -g -r | head -10

 

 

Posted by 반달가면

댓글을 달아 주세요

bash script (backup)2021. 8. 10. 18:01

 

반달가면 이글루에서 백업 - http://bahndal.egloos.com/523010

 

top 명령과 awk 명령을 조합해서 어느 프로세스가 자원을 많이 점유하고 있는지 확인해 볼 수 있다. 아무런 옵션 지정 없이 top 명령을 실행하면 현재 프로세스 상황을 실시간으로 보여주는데, 스크립트에서 다른 명령들과 조합하기 위해서는 실행 시점에서 상황을 한번만 표시하도록 아래와 같이 옵션을 주자.

# 결과를 한번만 표시하고 종료
top -bn 1

만약 CPU 점유율이 90% 이상인 프로세스만 골라내고 싶다면 아래와 같이 awk 명령을 조합할 수 있다. 아래의 예시를 보자. awk 명령으로 CPU 점유율과 프로세스 이름만 골라낸 후, grep 명령의 정규표현(regular expression)을 이용해서 90~99 또는 100이 포함된 부분만 추려냈다.

# CPU 점유율 90% 이상인 프로세스 출력
# top 출력에서 CPU 점유율은 9번째 항목이고 프로세스 이름은 12번째 항목에 있음
top -bn 1 | awk '{ print $9 " " $12 }' | grep "^9[0-9]\|^100"

grep 명령을 이용한 다중 문자열 검색은 이전 게시물을 참조하자. 여기로

메모리 점유율이 높은 프로세스를 찾는 것도 같은 방식으로 하면 되겠다. 아래의 예시를 보자.

# 메모리 점유율 50% 이상인 프로세스 출력
# top 출력에서 메모리 점유율은 10번째 항목에 있음
top -bn 1 | awk '{ print $10 " " $12 }' | grep "^[5-9][0-9]\|^100"

CPU 또는 메모리를 유난히 많이 점유하고 있는 프로세스가 있는지 확인을 하거나, 특정 작업을 실행하기 전에 자원이 충분한지 점검해야 할 경우에 스크립트 형태로 작성해서 유용하게 쓸 수 있다.

예를 들어 전체 CPU 코어의 수와 점유율 90% 이상인 프로세스의 수가 같다면 모든 코어에 상당한 부하가 걸려 있는 셈이므로 경고문을 출력해 준다거나 할 수 있을 것이다. CPU 코어 개수 파악은 nproc 명령을 이용하면 된다. 아래의 예시를 보자.

# nproc 명령으로 전체 CPU 코어 개수 파악
total_cores=`nproc`
# CPU 점유 90%  이상인 프로세스 개수 파악
busy_cores=`top -bn 1 | awk '{ print $9 }' | grep "^9[0-9]\|^100" | wc -l`
# 양쪽 개수가 같을 경우 경고문 출력
if [ $busy_cores -eq $total_cores ]
then echo "모든 CPU 코어가 90% 이상 점유되어 있습니다!"
fi

 

Posted by 반달가면

댓글을 달아 주세요

bash script (backup)2021. 7. 20. 16:32

 

반달가면 이글루에서 백업 - http://bahndal.egloos.com/522053

 

문서(txt) 파일에서 중복되는 행(line)을 제거하는 방법은 두가지를 생각해 볼 수 있는데, 하나는 정렬한 후에 제거하는 것이고 나머지 하나는 정렬하지 않고 제거하는 것이다.

행 순서가 바뀌어도 문제가 없는 경우라면 sort 명령을 이용해서 정렬한 후에 중복을 제거하면 된다. -u 옵션을 사용하면 중복된 행이 제거된다. 아래의 예시를 보자.

 

# my_file.txt 내용 확인

cat my_file.txt

def

abc

abc

def

 

# 정렬 및 중복 제거

sort -u my_file.txt

abc

def

 

또는 아래와 같이 사용할 수도 있다. 결과는 동일하다.

 

cat my_file.txt | sort -u

 

그렇다면 행 순서를 바꾸지 않은 상태에서 중복된 행들만 제거하려면 어떻게 할까?

 

심오하고도 위대한 awk 명령을 이용하면 되겠다. 좀 복잡하긴 한데, 일단 아래의 예시를 보자.

 

# 정렬하지 않고 중복 제거

awk '!x[$0]++ {print $0}' my_file.txt

def

abc

 

awk의 기본 동작이 화면에 출력하는 것이기 때문에 {print $0} 부분은 생략해도 무관하다.

 

awk '!x[$0]++' my_file.txt

 

아래과 같이 해도 된다. 결과는 동일.

 

cat my_file.txt | awk '!x[$0]++'

 

'!x[$0]++ {print $0}' 이 표현이 나름 심오한데, awk의 배열(array)이 가진 특징을 생각해 보면 감을 잡을 수 있다. awk에서는 배열의 인덱스(index)가 반드시 0 이상의 정수일 필요가 없고 문자열도 인덱스가 된다. C언어에서는 x[0], x[1] 이렇지만 awk에서는 x[my name is john], x[foo] 이런 것이 가능하다. 게다가 배열의 최대 크기를 지정할 필요도 없다.

 

자, 그러면 위의 표현이 어떤 일을 하는지 생각해 보자. awk에서 행 전체를 지칭하는 변수는 $0이다. 즉, 한 행의 문자열을 x라는 배열의 인덱스로 사용하는 것이다. 차근차근 보면 대강 이렇다.

 

명령을 실행하면 awk는 우선 my_file.txt의 첫 행을 읽어서 배열을 만든다. 해당 변수의 값의 기본 초기값은 0이다. 위의 예시에서 첫 행은 def였다. 즉 x[def]=0 (false)

 

그런데 앞에 ! 기호가 있으니 해당 값의 역(inverse)을 취한다. !x[def]=1 (true)

 

조건값이 non-zero, 즉 0이 아니므로 지정된 명령을 실행한다. {print $0} (문자열 출력)

 

그리고 나서 x[def]++(post-increment)에 의해 x[def]의 값은 1이 된다. x[def]=1

 

my_file.txt의 두번째 행을 읽어서 동일한 절차를 수행한다. 두번째 행은 abc이므로 출력이 끝나고 나면 x[abc]=1

 

이런식으로 가다가 어떤 행을 읽었는데 문자열 def였다고 하자. 즉 중복이 발생한 것이다. 이전에 이미 x[def]=1이므로 !x[def]=0, 즉 조건값이 0(false)이므로 출력되지 않는다. 그리고나서 x[def]++에 따라 x[def]=2 이렇게 된다.

 

이런 원리로 행 순서가 바뀌지 않으면서 중복된 행만 깔끔하게 제거된 결과를 얻을 수 있다. 속도 역시 상당히 빠르다.

 

만약 행 전체가 아니라 첫번째 단어만 기준으로 삼아서 중복을 제거하고 싶다면 x[$0] 대신에 x[$1] 이렇게 바꾸면 된다. (두번째 단어라면 x[$2])

 

awk '!x[$1]++ {print $0}' my_file.txt

 

만약 첫번째 단어만 기준으로 삼아 중복을 제거한 후, 출력은 두번째 단어만 골라서 하고 싶다면 print 부분의 인자를 바꿔주면 되겠다. 아래와 같이 해 보자.

 

awk '!x[$1]++ {print $2}' my_file.txt

 

 

Posted by 반달가면

댓글을 달아 주세요

bash script (backup)2021. 7. 13. 13:35

 

반달가면 이글루에서 백업 - http://bahndal.egloos.com/520761

 

파일 내용을 출력하기 위해 주로 사용하는 cat 명령은 내용만 출력하고 파일명은 출력하지 않는다. 여러개의 파일 내용을 한꺼번에 확인하고 싶을 때는 파일명을 식별할 수 없기 때문에 불편한 경우가 생길 수 있다.

 

# .txt 확장자인 파일의 내용을 출력, 파일명은 출력되지 않음

cat *.txt

 

파일명을 같이 출력하고 싶다면 cat 명령 대신 head 명령을 사용하자. 여러개의 파일에 대해 적용했을 경우 파일명이 함께 출력된다. 원래 head 명령은 파일의 앞쪽 내용 일부를 출력하기 위한 것이나, 옵션을 조정하면 cat 명령처럼 내용 전체를 출력할 수 있다. 아래의 예시를 참고하자.

 

# myfile.txt에서 맨 앞 5개 행을 출력(-n 옵션값이 양수)

head -n 5 myfile.txt

 

# myfile.txt에서 맨 뒤 3개 행을 제외한 나머지를 출력(-n 옵션 값이 음수)

head -n -3 myfile.txt

 

# myfile.txt 내용 전체를 출력

head -n -0 myfile.txt

 

자, 이제 여러개의 파일에 대해 파일명과 함께 내용을 출력해 보자.

 

# .txt 파일에 대해 파일명과 내용 전체를 출력

head -n -0 *.txt

==> filename1.txt <==

[filename1.txt의 내용]

==> filename2.txt <==

[filename2.txt의 내용]

...

Posted by 반달가면

댓글을 달아 주세요

bash script (backup)2021. 7. 6. 17:32

 

반달가면 이글루에서 백업 - http://bahndal.egloos.com/518271

 

정규표현(regular expression)을 이용하면 grep이나 sed에서 특정한 길이의 문자열에 대한 작업을 할 수 있다. 아래의 예시를 보자. my_file.txt 파일에서 영문 소문자로만 되고 길이가 5바이트(byte)인 행만 출력하는 경우이다.

 

cat my_file.txt | grep "^[a-z]\{5\}$"

 

중괄호({}) 안에 수자를 넣어서 바로 앞 표현이 얼만큼 반복되는지 지정할 수 있다. 중괄호가 shell에서 처리되는 것을 막고 grep 명령에 온전하게 인자로 전달되도록 역슬래시(\)로 보호해 준다. 중괄호를 이용한 반복 설정은 아래와 같다.

 

{n} 바로 앞의 항목이 정확히 n번 반복되는 경우

{n,} 최소 n번 이상 반복

{,m} 최대 m번 반복

{n,m} 최소 n번 이상, 최대 m번 반복

 

예시를 하나 더 보자. my_file.txt에서 영문 대소문자로 된 5바이트 이상 10바이트 이하 문자열이 있는 행만 출력하는 경우이다.

 

cat my_file.txt | grep "[[:alpha:]]\{5,10\}"

 

이 기능을 이용해서 2개 이상의 문자열이 동시에 포함된 행을 골라낼 수도 있다. 예를 들어 한 행에 abc와 def가 모두 있으면서 abc가 나온 후에 def가 나오는 문자열이 있는 부분을 찾는 경우이다. 아래의 예시를 보자.

 

cat my_file.txt | grep "abc[[:print:]]\{0,\}def"

 

위의 예시에서 grep 명령의 인자를 해석해 보자면, 문자열 abc 이후에 출력 가능한 문자([[:print:]])가 0번 이상(\{0,\}) 나온 후에 def가 나오는 경우를 찾아서 출력하라는 것이다.

 

2개 이상의 문자열이 동시에 포함된 행을 찾는 경우는 grep 명령을 파이프(|)로 연결하여 연속해서 사용할 수도 있다.

 

cat my_file.txt | grep "abc" | grep "def"

 

그러나, -v 옵션을 통해서 2개 이상의 문자열이 동시에 포함된 행을 제외하는 경우는 중괄호를 사용해야 한다.

 

# 검색한 문자열이 포함된 행을 제외(-v 옵션)

cat my_file.txt | grep -v "abc[[:print:]]\{0,\}def"

 

 

 

Posted by 반달가면

댓글을 달아 주세요

bash script (backup)2021. 6. 28. 12:58

 

반달가면 이글루에서 백업 - http://bahndal.egloos.com/517484

 

특정한 파일이 현재 전송되고 있는지 판단하기 위한 방법이다. 다수의 파일을 ftp로 받고 있거나 NFS를 통해서 복사하는 상황에서 이 파일들에 대한 작업을 해야 하는데, 전송중인 파일과 전송완료된 파일을 구분하기 위한 것.

파일이 마지막으로 수정된 시간과 현재 시간과의 차이를 확인하여 정해 둔 기준과 비교하면 되겠다. 파일이 마지막으로 수정된 시간은 stat 명령으로 확인한다. 아래의 예시를 보자.

# 마지막으로 수정된 시간, 1970.1.1 0시부터 경과한 시간을 초(second)로 표시

stat -c %Y my_file.txt

 

-c 옵션으로 출력할 항목을 지정하는데, %Y 항목을 사용. stat 명령을 이용하면 그 외에도 파일에 대한 다양한 정보를 확인할 수 있다. 자세한 내용은 man 페이지를 참고하자.

 

man stat

 

현재 시간은 date 명령을 사용하여 출력. 앞의 stat 명령과 동일한 형태로 출력할 수 있다.

 

# 현재 시간, 1970.1.1 0시부터 경과한 시간을 초(second)로 표시

date "+%s"

 

대상 파일이 현재 전송중이라면, 마지막으로 수정된 시간과 현재 시간과의 차이가 근소해야 한다. 별 문제가 없다면 1초 이하일 것이다. 만약 부하가 많이 걸리거나 네트워크 상태가 별로 좋지 않아서 전송이 지연되고 있다면 좀 더 차이가 날 수도 있을 것이다.

 

대강 시간차가 10초 이하면 현재 전송중이라고 판단하고 그 이상이라면 전송이 끝났다고 판단하는 경우의 예시를 보자.

 

# 대상 파일: my_file.txt

target_file=my_file.txt

# 시간차 기준: 10초

diff_limit=10

# 마지막으로 수정한 시간

mod_time=`stat -c %Y $target_file`

# 현재 시간

cur_time=`date "+%s"`

# 시간 차이 계산

diff=`echo "$cur_time - $mod_time" | bc`

# 시간차가 $diff_limit 이하면 전송중이라고 판단

if [ $diff -le $diff_limit ]

  then echo "$target_file: 전송중입니다."

  else echo "$target_file: 전송완료되었습니다."

fi

 

Posted by 반달가면

댓글을 달아 주세요

bash script (backup)2021. 6. 8. 14:20

 

반달가면 이글루에서 백업 - http://bahndal.egloos.com/517067

 

특정 파일 또는 디렉토리가 존재하는지 확인하는 방법이다. 스크립트의 인자(argument)로 파일이나 디렉토리를 입력받았을 경우 인자의 유효성을 확인할 때 요긴하게 사용 가능.

우선 파일의 존재 여부를 확인해 보자. if 조건문에서 test 명령의 -e 옵션을 사용하면 된다. 아래의 예시를 참고하자.

 

file_name="my_file.txt"

# 파일이 있을 경우 메세지 출력

if test -e $file_name

then echo "$file_name 파일을 찾았습니다."

fi

 

# 파일이 없을 경우 메세지 출력(! 사용)

if ! test -e $file_name

then echo "$file_name 파일이 없습니다."

fi

 

디렉토리의 존재 여부는 -d 옵션을 사용하면 된다.

 

dir_name="/var/log/"

# 디렉토리가 있을 경우 메세지 출력

if test -d $dir_name

then echo "$dir_name 디렉토리를 찾았습니다."

fi

 

test 명령은 대괄호([ ])로 대체해서 쓸 수도 있다. 바로 위의 if 조건문 예시를 아래과 같이 써도 된다.

 

if [ -d $dir_name ]

then echo "$dir_name 디렉토리를 찾았습니다."

fi

 

대괄호를 사용할 때는 띄어쓰기에 주의하자. 대괄호 안의 표현과 대괄호 사이에 반드시 공백이 한칸 들어가야 한다.

Posted by 반달가면

댓글을 달아 주세요

bash script (backup)2021. 5. 10. 15:36

 

반달가면 이글루에서 백업 - bahndal.egloos.com/514695

 

조금 생각해 보면 금방 알 수 있는 내용이긴 하지만, 최근 쇠퇴하고 있는 기억력을 절감하는 중이라 적어 두기로 했다.

grep 명령을 이용해서 특정 문자열을 검색하고 해당 문자열을 포함한 파일명을 찾는 자세한 방법은 이전에 올린 게시물을 참고하자.

 

일단 아래의 예시를 보자. -H 옵션을 사용하면 결과가 파일명:문자열 형태로 출력된다. -r(recursive) 옵션은 지정한 디렉토리와 그 하위 디렉토리에서 검색하기 위한 것이다.

# /home/john/Documents 디렉토리와 그 하위 디렉토리에서 abcd를 포함한 파일명과 해당 문자열을 출력
grep -Hr abcd /home/john/Documents

/home/john/Documents/my_file.txt:abcdefg

/home/john/Documents/my_file2.dat:abcde_xyz

 

다단계로 복잡한 검색을 하면서 파일명도 확인하려고 한다면, 일단 파일명을 확인하기 위한 -H 옵션으로 검색을 하고, 나머지 복잡한 검색을 파이프(|)를 이용해서 뒤쪽으로 이어주면 된다. 아래의 예시를 보자.

 

# abcd가 포함된 파일명과 해당 문자열을 검색한 후에 xyz가 포함된 항목을 제외(-v 옵션)

grep -Hr abcd /home/john/Documents | grep -v xyz

/home/john/Documents/my_file.txt:abcdefg

 

파이프 이후에는 -H 옵션이 의미가 없어진다. 입력이 파일이 아니라 이전 명령의 출력값이 되기 때문이다. 아래와 같이 사용하면 파일명을 확인할 수 없다.

 

# 이렇게 하면 파일명 확인 불가능

grep -r abcd /home/john/Documents | grep -H -v xyz

 

Posted by 반달가면

댓글을 달아 주세요

bash script (backup)2021. 4. 16. 13:36

 

반달가면 이글루에서 백업 - bahndal.egloos.com/510895

 

한 문장에서 단어 구분자가 여러개인 경우에 단어를 하나씩 뽑아내는 방법니다. 예를 들어 "Hello, my name is John. What's yours?" 이런 문장이 있는데 여기서 단어를 하나씩 가져오려면, 단어 구분자가 공백/쉼표/마침표/물음표 이렇게 여러가지가 되기 때문에 cut 명령으로는 어렵고 awk 명령을 써야 한다.

awk의 -F 옵션을 이용해서 구분자(field separator)를 지정할 때 정규표현(regular expression)을 사용하면 되겠다. 아래의 예시를 보자.

# 구분자는 공백/쉼표/마침표/물음표, 첫번째($1)와 세번째($3) 항목을 출력하되 항목 사이에 콜론을 출력
echo "Hello, my name is John. What's yours?" | awk -F '[ ,.?]' '{print $1 ":" $3}'
Hello:my

-F 옵션에서 구분자를 대괄호([])로 묶으면, 대괄호 안에 있는 캐럭터들이 모두 구분자로 인식된다. awk에 적용되는 명령문을 shell에서 해석하지 않도록 하기 위해 작은 따옴표(')를 사용하고 있음에 유의.

위의 예에서 Hello 다음에 구분자에 속하는 쉼표와 공백이 연속해서 나왔기 때문에, 첫번째 항목은 Hello, 두번째 항목은 아무것도 없고(null), 세번째 항목은 my가 될 것이다.

또 다른 예로, 위의 문장으로부터 쉼표나 마침표 등을 제외하고 단어만 골라서 출력을 해 보자. awk에서 for 명령을 이용한다.

# 유효한 단어만 골라내서 출력, 구분자는 공백/쉼표/마침표/물음표
echo "Hello, my name is John. What's yours?" | awk -F '[ ,.?]' '{ for (i=1;i<=NF;i++) print $i }' | grep [[:alnum:]]
Hello
my
name
is
John
What's
yours

for문을 이용해서 1부터 NF까지 하나씩 증가시키면서 i번째 항목을 출력한 것이다. NF 변수는 awk에서 항목의 수(the number of fields)를 뜻한다. 구분자가 연속해 있을 경우 빈 줄이 출력되므로, 빈 줄은 빼고 유효한 단어만 골라내기 위해 grep 명령을 연결했다.

 

Posted by 반달가면

댓글을 달아 주세요