Bash에서 강력한 기능 sed의 모든것, 텍스트 내용은 sed로 많은 걸 처리 할 수 있다.

입질쾌감 물때표

Bash에서는 sed라는 커맨드가 있다. 내가 전에 매우 주의 깊게 공부했던 커맨드인데 보통 사람들은 간단하게만 활용하고 깊이있게 다룰려고 하지 않는 것 같아 아래에 정리해서 설명하기로 했다.

사실 sed 커맨드의 “꽃”은 loop 기능이다. 이걸 활용할줄 알아야 진정한 효력(?)을 볼수 있다.

전체라인을 변수에 담는 방법

TEST=$(cat sample.txt | sed -n -r '/<a.*/{/\/a>/!{:loopA;N;/\/a>/!bloopA;s/\n//g};p}' | grep -o -E '<a([^<]|<[^a])*<\/a>');
echo "$TEST"
--------------------------------------------------------------------------------
#전체 변수 for문으로 쪼개서 읽는 방법
for line in "$TEST"; do
    echo "$line"
done

--------------------------------------------------------------------------------
# IFS 변수는 초기화 시켜야 한다.
while IFS= read
do
    echo $REPLY
done <<< "$TEST"

저 위의 커맨드가 무엇을 하는지는 차차 알아보기로 하고 각 기능들에 대해서 알아보자.
참고 URL : http://www.thegeekstuff.com/2009/11/unix-sed-tutorial-append-insert-replace-and-count-file-lines/?ref=driverlayer.com

1. Insert a line before every line with the pattern

$ cat 일본어.txt | sed '$i\a'

 

2. Append a line after every line matching the pattern

$ cat 일본어.txt | sed '$a\a'

 

3. Replace Lines Using Sed Command

– “c” command in sed is used to replace every line matches with the pattern or ranges with the new given line.

cat 일본어.txt | sed '$c\a'

 

전체 문자열 교체

$ sed -i 's/hdb/cup/g' *
$ sed -i 's/hdb/cup/g' FILENAME

 

Lookahead, Lookbehind 활용하기

이러한 문제를 코드 없이 바로 정규 표현식으로만으로도 해결이 가능한데 이때 사용하는 것이 Lookahead, Lookbehind입니다. 각각은Positive와 Negative 로 또다시 나뉩니다.
* Lookahead와 Lookbehind의 용어에 의미를 혼동하지 않도록 주의하여야 합니다.

regex1(?=(regex2)) : Positive Lookahead : regex1 다음 regex2의 정규표현식이 일치할 경우 반환
regex1(?!(regex2)) : Negative Lookahead : regex1 다음 regex2의 정규표현식이 일치하지 않을 경우 반환
(?<=(regex2))regex1 : Positive Lookbehind : regex2의 정규표현식이 일치하고 regex1가 나올 경우 반환
(?<!(regex2))regex1 : Negative Lookbehind : regex2의 정규표현식이 일치하지 않고 regex1가 나올 경우 반환

예) 처음 시작부터 badword가 아닌것만 검색해서 나옴
/^((?!badword).)*$/g
badword
test
one two
abadwords
three

html에세 link태그만 추출해서 출력

아래의 텍스트파일을 예제로 삼고 커맨드를 실행해 보라. link 태그만 추출하는 걸 프로그래밍해서 처리할수 있지만, sed 커맨드를 호출해서 사용하면 좀더 편리하고 정확한 방법으로 처리될수도 있다. 여기서 중요한 점은 아래처럼 link 태그가 한 Line에 모두 있지 않다는 것이다.

예제 텍스트

The awakened sages call a person wise when all his undertakings are free from anxiety about results.
And when immorality prevails, O Krishna, the women of the family become corrupted;
when women are corrupted, social problems arise.
Be fearless and pure; never waver in your determination or your dedication to the spiritual life.
Give freely. Be self-controlled, sincere, truthful, loving, and full of the desire to serve…

<meta content=’blogger’ name=’generator’/>
<lin
k href=’http://www.theunixschool.com/favicon.ico’ rel=’icon’ type=’image/x-icon’/>
<link href=’http:
//www.theunixschool.com/2012/12/sed-10-examples-to-print-lines-from-file.html’ rel=’canonical’/>
<link rel=”alternate” type=”application/atom+xml” title=”The UNIX School – Atom” href=”http://www.theunixschool.com/feeds/posts/default” />
<link rel=”alternate” type=”appli
cation/rss+xml” title=”The UNIX School – RSS” href=”http://www.theunixschool.com/feeds/posts/default?alt=rss” />
<link rel=”service.post” type=”application/atom+xml” title=”The UNIX School – Atom” href=”https://www.blogger.com/feeds/1255024703457423340/posts/default” />

<link rel=”alternate” type=”application/atom+
xml” title=”The UNIX School – Atom” href=”http://www.theun
ixschool.com/feeds/8579015762304838582/comments/default” />
<!–[if IE]><script type=”text/javascript” src=”https://www.blogger.com/static/v1/jsbin/3512845138-ieretrofit.js”></script>
<![endif]–>
<link href=’https://plus.google.com/117230502021459835787′ rel=’publisher’/>
<meta content=’http://www.theunixschool.com/2012/12/sed-10-exampl
es-to-print-lines-from-file.html’ property=’og:url’/>
<!–[if IE]> <script> (function() { var html5 = (“abbr,article,aside,audio,canvas,datalist,details,” + “figure,footer,header,hgroup,mark,menu,meter,nav,output,” + “progress,section,time,video”).split(‘,’); for (var i = 0; i < html5.length; i++) { document.createElement(html5[i]); } try { document.execCommand(‘BackgroundImageCache’, false, true); } catch(e) {} })(); </script> <![endif]–>
<title>The UNIX School: sed – 10 examples to print lines from a file</title>
<!– gpr metacontent starts –>
<title>The UNIX School</title>
<!– gpr alexa code –>
<!– sdT3R36D1Rz67G2pBSZRg04pFFk –>
<!– gpr alexa code ends –>
<link href=’mailto:guruprasadpr@theunixschool.com’ rev=’made’/>

$ cat sample.txt | sed -n -r '/\<link.*/{/\/>/!{:loopA;N;/\/>/!bloopA;p}}'
$ cat sample.txt | sed -n -r '/\<link.*/{/\/>/!{:loopA;N;/\/>/!bloopA;s/\n//g;s/.*(<link.*\/>).*/\1/gp}}'

@@ 해석
-n : suppress automatic printing of pattern space
-r : 확장 정규 표현식 사용
/\<link.*/{} : “<link” 텍스트를 만났을시 커맨드 확장 : 이때에는 pattern space에 현재 라인이 저장되어 있음
/\/>/! { }: 해당 라인에서 “/>” 텍스트가 없는지 체크,
:loopA;N;/\/>/!bloopA; loopA 라벨 지정; 다음라인을 읽어 pattern space에 추가로 붙임; “/>”텍스트가 없는지 체크함, 없으면 다시 loopA로 롤백;
(여기서 두번 체크 하는 이유는 한 정규표현식에서 “/>”의 여부를 중복체크하는 Negative Lookahead 표현식을 만들수 없기 때문)
최종적으로 “/>”를 만나면 pattern space에 모아둔 라인을 newline을 제거하고 <link **** />태그만 남기고 양옆 텍스트 날림

==> 위의 방법은 하나의 라인으로 이루어진 link태그는 출력되지 않는다, 따라서 아래처럼 작성해야 모두 출력된다 d커맨드로 앞부분 필터된 pattern space 내용은 삭제한다.

$ cat sample.txt | sed -n -r '/\<link.*/{/\/>/!{:loopA;N;/\/>/!bloopA;s/\n//g;s/.*(<link.*\/>).*/\1/gp;d}};/<link.*\/>/p'

<link href='http://www.theunixschool.com/favicon.ico' rel='icon' type='image/x-icon'/>
<link href='http://www.theunixschool.com/2012/12/sed-10-examples-to-print-lines-from-file.html' rel='canonical'/>
<link rel="alternate" type="application/atom+xml" title="The UNIX School - Atom" href="http://www.theunixschool.com/feeds/posts/default" />
<link rel="alternate" type="application/rss+xml" title="The UNIX School - RSS" href="http://www.theunixschool.com/feeds/posts/default?alt=rss" />
<link rel="service.post" type="application/atom+xml" title="The UNIX School - Atom" href="https://www.blogger.com/feeds/1255024703457423340/posts/default" />
<link rel="alternate" type="application/atom+xml" title="The UNIX School - Atom" href="http://www.theunixschool.com/feeds/8579015762304838582/comments/default" />
<link href='https://plus.google.com/117230502021459835787' rel='publisher'/>
<link href='mailto:guruprasadpr@theunixschool.com' rev='made'/>

위의 커맨드를 실행하면 위와 같은 결과가 나온다. 하나하나 커맨드가 어떤 역할을 하는 잘 살펴보기 바란다.
/\<link.*/{/\/>/!{:loopA;N;/\/>/!bloopA;s/\n//g;s/.*(<link.*\/>).*/\1/gp;d}};

  1. 위의 커맨드에서 :loop”A”의 A는 loop 이름을 “A”라고 지정한것이고 N기능은 은 검색중에 버퍼에 계속적으로 해당 라인을 추가하라는 의미이다.
  2. /\<link.*/ –> “<link”의 문자로 시작하는 문자를 찾으면 뒤의 {} 블럭을 실행하라는 의미
  3. /\<link.*/{/\/>/!{:loopA;N;/\/>/!bloopA;s/\n//g;s/.*(<link.*\/>).*/\1/gp;d}} –> “<link”의 문자로 시작하는 문자를 찾으면 “/>”가 포함된 라인을 찾아야 하는데 “!” 기능을 사용했으므로 “/>”가 없다면 계속적으로 “N” 기능으로 A라는 loop구역을 설정하고 계속적으로 버퍼에 내용을 붙이라는 의미이다.
  4. {:loopA;N;/\/>/!bloopA;s/\n//g;s/.*(<link.*\/>).*/\1/gp;d} –> 내부의 sed 커맨드 동작은 현 라인에서 “/>” 문자를 발견하지 못하면 loopA로 back(b)하라는 의미이다. 그러니 :loopA;N;/\/>/!bloopA; 는 “/>”가 만날때 까지 계속 라인을 버퍼에 붙이라는 것이다.
  5. 만약 발견했다면 s/\n//g; –> 커맨드로 각 newline을 날리라는 의미이다.
  6. s/.*(<link.*\/>).*/\1/gp; –> newline을 날렸으면 지금까지 읽었던 버퍼의 내용을 <link ……….. /> 의 텍스트로 replace하라는 것이다. “\1” 표시자에는 매칭된 텍스트를 말한다. g 기능으로 전체 구역에 대해서 처리하라는 것이고 처리되면 p 기능으로 print 하라는 것이다.
  7. d기능은 삭제하라는 것

webterror : 위의 내용은 오래간만에 내 메모장에서 퍼온 글이라서 다소 오류가 있을수도 있음을 알린다. 다른 좋은 방법이 있을수 있으니 공부차원에서 확인하길 바란다.

sed -n '/<tr>/{h; :loop; n; H; /<\/tr>/{x;p}; /<\/tr>/!b loop};' sample4 | less

“<tr> 태그를 만나면 ‘h’로 Hold Space에 현재라인을 저장한다(기존 버퍼는 휘발한다). 이후 라벨을 설정하고 루핑한다. 다음라인을 읽어 Pattern Space에 저장하고

“H” 함수로 Pattern Space의 내용을 Hold Space로 저장한다. 이후 마지막 “</tr>”를 만나는지 체크한후 만약 마지막 태그에 도착하면
Hold Space의 자료를 Pattern Space로 “x”로 치환해 꺼내온후 “p” 화면에 프린팅한다. 만일 마지막 “</tr>” 아니라면 계속 다음라인을
읽어 루핑한다.

sed function 설명

H/g 중에서 소문자는 Copy를 의미, 대문자는 Append를 의미

Sed H function
The H function appends the contents of the pattern space to the contents of the holding area. The former and new contents are separated by a newline.
– H : Append pattern space to hold space.

Sed h function
– h : Copy pattern space to hold space.

Sed g function
The g function copies the contents of the holding area into the pattern space, destroying the previous contents of the pattern space.
– g : Copy hold space to pattern space.

Sed G function
The G function appends the contents of the holding area to the contents of the pattern space. The former and new contents are separated by a newline. The maximum number of addresses is two.

Sed x function
The exchange function interchanges the contents of the pattern space and the holding area. The maximum number of addresses is two.

q : 커맨드를 종료한다.

e : 발견된 패턴으로 명령어 실행

x : 패턴스페이스와 홀드스페이스를 서로 교체

패턴스페이스:  매 라인을 만날때마다 계속해서 임시저장되는 휘발성 저장 공간이다.
홀드스페이스 : 홀드스페이스는 계속해서 기록되어 있고 필요시에 “x” 명령어로 꺼내서 “p” 명령어로 다시 출력할수 있다.

예제 텍스트

Host: foo1
some junk, doesnt matter
some junk, doesnt matter
Info: about foo1 that I really care about!!
some junk, doesnt matter
some junk, doesnt matter
some junk, doesnt matter
some junk, doesnt matter
Host: foo2
some junk, doesnt matter
Info: about foo2 that I really care about!!
some junk, doesnt matter
some junk, doesnt matter

sed -n '/Host:/{h}; /Info/{x;p;x;p;}' myfile.txt

[ 풀이 ]

(1) Host라는 텍스트를 만나면 해당 라인을 홀드 스페이스로 임시 저장한다.
(2) 이후 Info라는 텍스트를 만나면 Info 패턴이 있는 라인은 패턴스페이스로 저장되어 있고 Host라인으로 시작하는 라인은 홀드 스페이스에 저장되어 있는 상황이다.
(3) 홀드스페이스 <> 패턴스페이스 -> 이후 “패턴스페이스” 출력 -> 홀드스페이스 <> 패턴스페이스 -> 이후 “패턴스페이스 출력”

따라서 아래처럼 나오게 된다.

Host: foo1
Info: about foo1 that I really care about!!
Host: foo2
Info: about foo2 that I really care about!!

 

치환기능 활용

# ‘   ‘를 사용하지 않는다면 모두 escape 문자를 써서 특수문자를 구분해 줘야 한다.

sed -n -r /[A-Z]\{10\}/p

차이점은 아래내용을 확인하면 된다.

sed -r -n 's/([0-9]{3}\*)/AAA/p'
sed -r -n s/\([0-9]\{3\}\\*\)/AAA/p

아래 두가지의 차이점은? 만일 ( )로 \1 임시 저장공간을 사용하고자 한다면 반드시 셀전체에 대한 패턴을 인지 시키야 한다. /(…..).*/로 시작하면 라인의 시작 부분을 모두 포함한다고 판단된다. 따라서 반드시 앞과 뒤에 .*를 반드시 명기한다.

sed -r -n 's/.*([0-9]{3}\*.*\|[^\|]{30,}).*/\1/p'
sed -r -n 's/([0-9]{3}\*.*\|[^\|]{30,}).*/\1/p'

\\& –> 변경된 자기 자신을 말함

sedeasy() {
    sed "s/$(echo $1 | sed -e 's/\([[\/.*]\|\]\)/\\&/g')/$(echo $2 | sed -e 's/[\/&]/\\&/g')/g" $3
}

$ sedeasy "include /etc/nginx/conf.d/*" "include /apps/*/conf/nginx.conf" /etc/nginx/nginx.conf

 

윈도우 방식의 파일 NEWLINE 문자를 제거하기

\r\n을 스페이스로 교체해야 한다.

sed ':a;N;$!ba;s/\r\n/ /g' sample > sample2

sed 몇가지 기능 소개

sed ‘/[a-z]/!d’ # !x runs x if the pattern doesn’t match
grep -v ‘[a-z]’ # -v means print if the regexp doesn’t match
awk ‘!/[a-z]/’ # !expr negates expr

When sed reads a file line by line, the line that has been currently read is inserted into the pattern buffer (pattern space).
Pattern buffer is like the temporary buffer, the scratchpad where the current information is stored.

When you tell sed to print, it prints the pattern buffer.

Hold buffer / hold space is like a long-term storage, such that you can catch something,
store it and reuse it later when sed is processing another line.
You do not directly process the hold space, instead, you need to copy it or append to the pattern space if you want to do something with it.
For example, the print command p prints the pattern space only. Likewise, s operates on the pattern space.

Here is an example:

sed -n ‘1!G;h;$p’ <– 전체 라인이 반대로 뒤집어 진다.
(the -n option suppresses automatic printing of lines)
-n 옵션은 기본적으로 sed는 일치되는 패턴 라인을 만나면 출력하게 되어 있는데 출력을 시키지 말라는 것이다.

There are three commands here: 1!G, h and $p. 1!G has an address, 1 (first line), but the ! means that the command will be executed everywhere but on the first line. $p on the other hand will only be executed on the last line. So what happens is this:

first line is read and inserted automatically into the pattern space
on the first line, first command is not executed; h copies the first line into the hold space.
now the second line replaces whatever was in the pattern space
on the second line, first we execute G, appending the contents of the hold buffer to the pattern buffer, separating it by a newline. The pattern space now contains the second line, a newline, and the first line.
Then, h command inserts the concatenated contents of the pattern buffer into the hold space, which now holds the reversed lines two and one.
We proceed to line number three — go to the point (3) above.
Finally, after the last line has been read and the hold space (containing all the previous lines in a reverse order) have been appended to the pattern space, pattern space is printed with p. As you have guessed, the above does exactly what the tac command does — prints the file in reverse.

형식 1
sed ‘:label
command1
command2
b label’

형식 2
sed ‘command1
:label
command2
command3
b label’

형식 3
sed ‘/match/{
:label
command1
command2
b label}’

@@ 예제
The awakened sages call a person wise when all his undertakings are free from anxiety about results.
And when immorality prevails, O Krishna, the women of the family become corrupted;
when women are corrupted, social problems arise.
Be fearless and pure; never waver in your determination or your dedication to the spiritual life.
Give freely. Be self-controlled, sincere, truthful, loving, and full of the desire to serve…
Learn to be detached and to take joy in renunciation.
Do not get angry or harm any living creature, but be compassionate and gentle;
show good will to all. Cultivate vigor, patience, will, purity;

@@ 전체가 모두 출력
#!/bin/bash
sed ‘:loop; n; b loop’ quotes.txt

@@ 앞 2줄만 출력, Krishna를 Loard Krishna로 치환
#!/bin/bash
sed ‘/Krishna/{bstop};:stop;n;s/Krishna/Loard Krishna/;q’ quotes.txt

@@ sed는 ‘;’를 기준으로 모든 것이 모두 커맨드이다. ‘:’로 시작하면 라벨을 지칭하는 것이고 그 뒤로는 모두 실행을 하는 커맨드로 인식한다.
그러나 위의 커맨드처럼 ‘/Krishna/bstop 까지 오면 bstop은 대체 문자로 인식하기 때문에 커맨드로 강제로 인식시켜야 한다.
bstop은 stop라벨로 점프를 하라는 것인데 이 명령어가 실행되기 위해 강제로 { }로 감싸줘야 한다.
따라서 아래의 커맨드도 동작이 가능하다.
sed ‘/Krishna/{bstop};{:stop;n;n;n;s/Krishna/Loard Krishna/;q}’ quotes.txt

 

sed 검색&캡춰된 문자열에 부가적인 텍스트 넣기

http://stackoverflow.com/questions/3218789/adding-characters-at-the-start-and-end-of-each-line-in-a-file

vim에서 활용

:%s/^\(.*\)$/”\1″/
s/regex/replace/ is vim command for search n replace.
% makes it apply throughout the file
^ and $ denotes start and end of the line respectively.
(.*) captures everything in between. ( and ) needs to be escaped in vim regexes.
\1 puts the captured content between two quotes.

@@ 예제
%s/\(^[[:space:]]*\)[^\”|^\{|^\[]*/=====\1=====/g
그룹 1번으로 묶는 방법은 \(…..\)로 묶으면 되고 각각 할당된 번호로 매칭된다.

@@ 예제

{[
  {
    1113786970,
    "[소호신상5%]묶음배송 테스트",
    9,
    "N", 
    "N", 
    10000276,
    "1", 
    103,
    100,
]}

%s/\(^[[:space:]]*\)\([^\”|\}|\{]*\),$/\1\”\2\”\,/g
위처럼 하게 되면 더블쿼터가 없는 곳은 다 달아준다.
만약 20* 이라면 2, 22, 20 등에 매칭이 된다. ‘0’이라는 숫자가 0개 이상이 오는 문자를 찾기 때문에 22는
두번 찾은 셈이 된다.

 

sed에서 활용

“카테고리전체경로”:”에어컨/선풍기/제습기>공기청정기/에어워셔>공기청정기 기타”,

sed -s 's/카테고리\(.*\)경로/\1/g'

“전체”:”에어컨/선풍기/제습기>공기청정기/에어워셔>공기청정기 기타”, 위와 같이 출력된다. \(.*\) 가 \1번의 그룹으로 캡춰되어 치환되게 된다.

@@ 참고
http://www.tutorialspoint.com/unix/unix-regular-expressions.htm
@@ 활용
cat sample100.json | sed -s ‘s/\”\(.*\)\”\://g’ | sed -s ‘s/\(^[[:space:]]*\)\([^\”|\}|\{]*\)\,/\1\”\2\”\,/g’

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다