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. 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 반달가면

댓글을 달아 주세요

bash script (backup)2020. 11. 3. 16:49

 

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

 

그냥 예제를 하나 적어두는 것이 가장 나을 것 같다.

 

예를 들어 people.txt 파일의 내용이 아래와 같이 이름::이메일::나이 형태라고 해 보자.

 

john::john@mail.com::24

mary::mary@mail.com::22

 

이것을 이메일[탭]이름[탭]나이 형태로 바꿔서 result.txt 파일에 저장하려고 한다. 아래와 같이 하면 되겠다.

 

cat people.txt | awk -v FS="::" -v OFS="\t" '{ print $2,$1,$3 }' > result.txt

 

-v 옵션으로 awk의 내부 변수를 설정해 주는데, FS는 입력에 적용하는 항목 구분자(field separator)이고 OFS는 출력에 적용하는 항목 구분자이다.

 

FS에 지정된 구분자를 기준으로 첫번째 항목은 $1에, 두번째 항목은 $2에 대응된다. 위의 예시를 기준으로 보면 $1은 이름, $2는 이메일에 대응될 것이다.

 

이름과 이메일의 순서를 바꿔야 하므로 print 명령에서 순서를 $2,$1,$3 이렇게 했다. 그리고 이 결과를 result.txt로 저장.

 

result.txt의 내용은 아래와 같이 될 것이다.

 

john@mail.com john 24 mary@mail.com mary 22

 

 

Posted by 반달가면

댓글을 달아 주세요

bash script (backup)2020. 10. 22. 12:13

 

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

 

탭이 포함된 문자열을 검색하려면 grep이 아니라 awk를 이용해야겠다.

 

test_file.txt에서 "abc[탭]def"를 검색하려면

 

cat test_file.txt | awk "/abc\tdef/"

 

또는

 

awk "/abc\tdef/" test_file.txt

 

이렇게 써야 한다.

 

Posted by 반달가면

댓글을 달아 주세요

bash script (backup)2020. 10. 15. 15:28

 

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

 

파일을 지우려면 우선 해당 파일명을 찾아야 한다. ls -l 명령으로 timestamp가 표시되도록 한 후 awk로 파일명을 골라내자.

예를 들어 5월 24일이 찍힌 파일을 찾으려면 이렇게 할 수 있다.

ls -l | grep "^\-r" | grep "May 24" | awk '{print $9}'

퍼미션 표시에서 (디렉토리나 링크가 아닌) 파일은 -rwxr--r-- 이런 식으로 나올 테니 "-r"로 시작하는 항목만 grep으로 고른다.('-'가 grep에서 연산자로 해석되지 않도록 '\-' 이런식으로 쓰자)

거기서 원하는 timestamp가 있는 항목을 골라낸다.

거기서 파일명이 표시되는 위치인 9번째 항목(field)을 awk 명령으로 골라낸다.

자, 이제 지워야겠으니 위의 명령을 역따옴표(`)로 묶어서 rm 명령에 넘기자.

rm `ls -l | grep "^\-r" | grep "May 24" | awk '{print $9}'`

 

 

Posted by 반달가면

댓글을 달아 주세요