#TIL #bash 문자열 부분 삭제

문자열 부분 삭제로 원하는 정보를 간단하게 추출할 수 있다.

$ value="version=1.0.1"
$ echo ${value##*=}
1.0.1
$ echo ${value%%=*}
version

추출이 아니라 삭제다. ##*= 연산자는 앞부분에서 *= 패턴에 가장 길게 일치하는 문자열을 삭제한다. 즉, 가장 길게 일치하는 version= 문자열을 삭제한다. %%=* 연산자는 반대다. 뒷부분에서 삭제한다. =1.0.1 문자열을 삭제한다.

참고


#TIL #jenkins choice 파라미터를 중복해서 정의하고 있다면 Extensible Choice Parameter 플러그인

/ddiary/assets/2019-12-04-til-jenkins-extensible-choice-parameter-00.jpg

jenkins 프로젝트를 빌드할 때, 파라미터를 받게 할 수 있다. branch 이름을 파라미터로 추가할 때, text 파라미터가 아닌 choice 파라미터를 선호한다. 사용하는 branch 이름이 뻔할 때, 타이핑 실수가 없기 때문이다.

타이핑 실수도 없고 좋다. 문제는 choice 파라미터의 항목 업데이트다. 만약 여러 프로젝트에서 똑같은 choice 파라미터를 사용하면 하나하나 다 고쳐줘야 한다.

하나씩 업데이트하다가 분명히 나만 괴로운 게 아닐 거란 생각이 들었다. Extensible Choice Parameter 플러그인을 찾았다. 나보다 더 적극적인 프로그래머가 문제를 해결해놨다. 전역 choice 파라미터를 정의하고 필요한 프로젝트에서 쓸 수 있다. 복사가 아니라 참조 개념이다. 전역 choice 파라미터 항목을 변경하면 사용하는 프로젝트에 반영된다.

Jenkins 관리 > 환경 설정 > Extensible Choice: Available Choice Providers

Global Choice Parameter 항목을 선택하고 프로젝트 공통으로 사용할 choice 항목을 정의한다.

프로젝트 선택 > 구성 > 이 빌드는 매개변수가 있습니다 > 매개변수 추가

Extensible Choice 매개변수를 추가하고 Choice Provider로 Global Choice Parameter를 선택한다.


#TIL #gitlab CI/CD 파이프라인 시작 조건을 rules로 제어

gitlab CI/CD 파이프라인 rules 설정 파라미터를 사용하면 특정 조건에만 파이프라인을 시작할 수 있다.

rules:
  - if: '$CI_COMMIT_REF_NAME == "master" || $CI_COMMIT_REF_NAME =~ /^deploy_.*$/'
    changes:
      - src/*
      - src/**/*
      - data_table/*
      - data_table/**/*
    when: always
  - if: '$CI_PIPELINE_SOURCE == "web"'
    when: manual

master 브랜치와 deploy_ 문자열로 시작하는 브랜치에 머지하고 특정 디렉터리 파일이 변경됐을 때, 실행한다. 웹에서 버튼을 눌러 시작했을 때, 동작을 다르게 할 수 있다. 웹에서 버튼을 누르면 조건 검사를 하지 않고 무조건 파이프라인을 시작한다.

12.3.5 버전 기준으로 only/except 설정 파라미터도 있지만 언제 변경될 지 모른다는 경고가 있다. 복잡하지만 사라질 위험이 적은 rules를 사용했다.

trigger:
  script:
    - 'TOKEN="PRIVATE-TOKEN: XXXXXXXXXXXXXXXXXXXXXXXX"'
    - export LAST_COMMIT=$(curl --header "$TOKEN" "$API_URL" | python -c "import sys, json; print json.load(sys.stdin)[1]['sha']")
    - echo "" >trigger_variables
    - if git diff $LAST_COMMIT HEAD --name-only|grep "src/|data_table/"; then
    -     echo 'export TRIGGER="true"' >>trigger_variables
    - fi
    stage: trigger
    artifacts:
      paths:
        - trigger_variables

rules 설정 파라미터가 나오기 전에는 첫 파이프라인 스테이지에서 변경 사항을 직접 조사했다. 파이프라인 스테이지 간 정보를 주고받는 방법이 없어서 파일로 만들어서 artifacts로 다음 스테이지에 전달한다.

예전 방식도 그럭저럭 동작은 했다. 파이프라인 시작 후 검사해서 시작은 했지만, 조건에 안 맞아 실행은 안 한 파이프라이이 많다. 파이프라인이 연속으로 여러 개 생기면 gitlab이 파이프라인을 취소시키는 최적화를 한다. 특정 디렉터리 변경 사항을 관찰하고 실행하는 설정을 알 리 없는 gitlab이 중요한 파이프라인을 취소시키곤 했다.


#TIL #jenkins #git sparse checkout으로 사용 공간을 아껴쓰자

빌드 머신 저장 공간이 가득 찰 일은 없겠지? 저장 공간 FULL이 나서 빌드 몇 번 실패해보면 정신을 차린다. 너무 헤프게 쓰고 있었구나. 게다가 mac mini라 저장 공간을 바로 늘릴 수도 없다. 필요한 것만 checkout 해서 쓰자. checkout 속도가 빨라져 빌드하는 데 걸리는 시간도 단축된다.

jenkins 프로젝트 설정에서 소스 코드 관리 > git 메뉴로 항목을 추가한다

Additional Behaviours > Sparse Checkout paths 추가

src

src 디렉터리만 checkout 한다

/*
!art

art 디렉터리만 빼고 checkout 한다

git lfs를 사용한다면 Additional Behaviours > Git LFS pull after checkout 항목을 빼야 한다. lfs pull에 exclude하는 옵션이 없어서 직접 실행해야 한다

git config lfs.fetchexclude "art"
git lfs pull origin

Execute shell 빌드 스텝을 추가하고 입력한다

참고 - git-read-tree - git-scm.com


#TIL #jenkins description setter 플러그인으로 빌드 히스토리를 한 눈에 파악

/ddiary/assets/2019-10-06-til-jenkins-description-setter-plugin-00.jpg

output 창에서 정규식에 일치하는 문자열을 빌드 설명으로 추가한다. 빌드 히스토리에서 빌드 항목을 클릭 안 해도 중요한 정보를 파악할 수 있어서 해놓으면 뿌듯한 설정이다

echo "[BUILD_DESCRIPTION] env=${BUILD_ENV}, branch=${BRANCH}"

빌드 설명으로 추가하고 싶은 문자열을 빌드할 때, stdout으로 출력한다.

Regular expression: \[BUILD_DESCRIPTION\] (.*)
Description: \1

정규식을 이용해 빌드 설명을 설정한다.

플러그인 홈페이지 - description setter - plugins.jenkins.io


#TIL #bash 찾은 파일 이름에 공백이 있어도 잘 처리하기

$ find . -type f -print0 -name '*.sh' | xargs -0 dos2unix
dos2unix: converting file ./hello world.sh to Unix format...
dos2unix: converting file ./sub_test/hello bash.sh to Unix format...

구분 문자(delimiter)를 공백이 아닌 null character로 바꾸면 된다. find -print0 옵션을 사용해 줄 바꾼 대신 null character로 파일 이름을 구분해서 출력하고 xargs -0 옵션을 사용해 null character로 구분해서 실행하게 한다.

$ find . -type f -name '*.sh' | xargs dos2unix
dos2unix: ./hello: No such file or directory
dos2unix: Skipping ./hello, not a regular file.
dos2unix: world.sh: No such file or directory
dos2unix: Skipping world.sh, not a regular file.
dos2unix: ./sub_test/hello: No such file or directory
dos2unix: Skipping ./sub_test/hello, not a regular file.
dos2unix: bash.sh: No such file or directory
dos2unix: Skipping bash.sh, not a regular file.

-print0, -0 옵션을 안 쓰면 whitespace로 구분하기 때문에 파일 이름에 공백이 있으면 실패한다.

$ find . -type f -name '*.sh'
./hello world.sh
./sub_test/hello bash.sh

테스트에 사용한 파일 목록이다.

참고 - How can I run dos2unix on an entire directory? - stackoverflow.com


#TIL #batch 무한 루프

C:\> help for
FOR /L %변수 IN (시작,단계,끝) DO 명령 [명령-매개 변수]

   집합은 단계별로 증가/감소하는 시작부터 끝까지의 일련의 숫자입니다.
   따라서 (1,1,5)는 1 2 3 4 5를 나타내며 (5,-1,1)은 5 4 3 2 1을
   나타냅니다.
...

FOR /L 명령을 사용하면 된다.

C:\>FOR /L %i IN (1, 1, 5) DO echo %i
C:\>echo 1
1
C:\>echo 2
2
C:\>echo 3
3
C:\>echo 4
4
C:\>echo 5
5

(1, 1, 5)를 넣어서 5번 루프를 한다.

C:\>FOR /L %i IN (0, 0, 1) DO echo %i
C:\>echo 0
0
C:\>echo 0
0
...

끝값이 절대 안 나오게 세팅하면 무한 루프가 된다. while 문이 없으니 for 문으로 때워야 한다.

참고 - FOR /L - ss64.com


#TIL #bash 스크립트에서 here document로 긴 파일을 쉽게 만드는 법

cat << EOF > /tmp/yourfilehere
These contents will be written to the file.
        This line is indented.
EOF

here document라고 한다. EOF 사이에 내용을 분리된 파일처럼 취급한다. 즉 here document 내용을 cat 명령어로 stdout으로 출력한다. 출력 내용을 리다이렉션 연산자를 써서 파일로 저장한다.

한두 줄이면 리다이렉션 연산자로 파일을 만들어도 괜찮다. 하지만 길면 줄을 맞춘다고 해도 괴롭다. 이럴 때, here document를 요긴하게 쓸 수 있다.

참고 - How can I write a heredoc to a file in Bash script? - stackoverflow.com


#TIL #bash 설치 안 한 프로그램이 있으면 빠르게 실패하기

$ command -v emacs
/usr/local/bin/emacs
$ echo $?
0
$ command -v vim
$ echo $?
1

command -v 명령을 사용하면 된다. 종료 상태(exit status)로 설치했는지 여부를 알 수 있다.

$ if ! command -v elixir > /dev/null; then
> echo >&2 "failed: cannot find elixir"
> exit 1;
> fi

bash 스크립트에서 사용하는 프로그램이 있는지 검사하고 시작하면 시간을 절약할 수 있다. 30분이 넘는 시간을 쓰고 다음 스텝에 쓰는 프로그램이 없다고 종료해버리면 허탈하다.


#TIL #mysql #docker MySQL 초기화가 끝나고 연결 가능한 상태인지 확인하기

$ docker run \
>        --name mysql-temp \
>        -e MYSQL_ROOT_PASSWORD=supersecret \
>        -d \
>        --rm \
>        -p 3310:3306 \
>        mysql:5.7.23

-d 옵션을 사용해 백그라운드로 실행하는 mysql docker 컨테이너를 만들었다. mysql 초기화가 다 끝나고 명령을 받을 준비가 됐다는 걸 어떻게 확인할 수 있을까?

$ while ! mysqladmin ping -hlocalhost -uroot -psupersecret -P3310; do
> sleep 1
> done
mysqladmin: [Warning] Using a password on the command line interface can be insecure.
mysqladmin: connect to server at 'localhost' failed
error: 'Lost connection to MySQL server at 'reading initial communication packet', system error: 0'
mysqladmin: [Warning] Using a password on the command line interface can be insecure.
mysqladmin: connect to server at 'localhost' failed
error: 'Lost connection to MySQL server at 'reading initial communication packet', system error: 0'
mysqladmin: [Warning] Using a password on the command line interface can be insecure.
mysqladmin: connect to server at 'localhost' failed
error: 'Lost connection to MySQL server at 'reading initial communication packet', system error: 0'
mysqladmin: [Warning] Using a password on the command line interface can be insecure.
mysqladmin: connect to server at 'localhost' failed
error: 'Lost connection to MySQL server at 'reading initial communication packet', system error: 0'
mysqladmin: [Warning] Using a password on the command line interface can be insecure.
mysqld is alive

mysqladmin 프로그램의 ping 명령을 사용하면 된다. 에러 메시지를 안 찍는 -s 옵션도 있다.

참고


#TIL #vim :s 명령어에서 현재 커서 단어를 입력하기

:%s/foo/<c-r><c-w>/g

Replace each occurrence of ‘foo’ with the word under the cursor. <c-r><c-w> means that you press Ctrl-R then Ctrl-W The word under the cursor will be inserted as though you typed it.

Search and replace - vim.fandom.com

* 키와 조합이 좋다. * 키를 눌러 찾은 단어를 :s 명령어에서 쓰고 싶을 때, 주로 사용한다.

참고


#TIL #git windows에서 파일 권한 변경하기

$ git add sample.sh
$ git update-index --chmod=+x sample.sh

windows에서 파일을 만들면 권한을 644로 세팅한다. windows에 배포하면 문제가 없다. 무시하니깐. 하지만 linux에 배포하면 문제다. 실행하기 전에 파일 권한을 고쳐야 한다. update-index --chmod=+x 옵션을 사용하면 windows에서도 파일 권한을 755로 변경해 커밋할 수 있다.

참고


#TIL #bash . filename 혹은 source filename

$ echo -e '\n. $HOME/.asdf/asdf.sh' >> ~/.bashrc

asdf를 설치하다가 본 . 연산자. 뭔가 해서 찾아보니 source 명령이랑 같다. 현재 shell 컨텍스트에서 파일을 읽고 실행하는 명령이다.

환경 변수를 조작하는 스크립트를 실행할 때, 많이 사용한다. source 명령으로 PATH를 조작하는 스크립트를 실행하면 현재 shell 컨텍스트에 있는 PATH 환경 변수를 수정할 수 있다. source 명령어를 안 쓰고 그냥 호출하면 실행하면서 세팅되는 shell 컨텍스트에만 적용되고 현재 shell 컨텍스트에는 영향을 못 준다.

참고


#TIL #elixirlang string으로 atom 만들기

iex> pool_config = %{name: "Auth"}
iex> String.to_atom("#{pool_config[:name]}Supervisor")
:AuthSupervisor

문자열 채우기(string interpolation)String.to_atom/1 함수를 사용하면 된다.

iex> pool_config = %{name: "Auth”}
iex> :"#{pool_config[:name]}Supervisor”
:AuthSupervisor

String.to_atom/1 함수까지 쓸 필요 있나? : 문자를 앞에 붙이면 된다.


#TIL #unity meta 파일을 버전 컨트롤 해야 하는가?

fileFormatVersion: 2
guid: de9a32f15f2628044842629a83d3d974
timeCreated: 1442592418
licenseType: Free
MonoImporter:
 serializedVersion: 2
 defaultReferences: []
 executionOrder: 0
 icon: {instanceID: 0}
 userData:
 assetBundleName:
 assetBundleVariant:

GUID가 저장되기 때문에 해야 한다. 작업자들이 각자 meta를 생성하면 reference가 다 깨진다. reference를 GUID로 기록하기 때문이다. 스크립트 실행 순서를 정하는 executionOrder도 있다. asset 단위로 쿵작쿵작하는 건 다 여기에 저장된다.

참고 - Managing Meta Files in Unity - blog.forrestthewoods.com


#TIL #bash 커맨드 라인에 입력할 긴 명령어를 에디터로 편집하기

$ docker run \
--name gerrit-mysql \
--volumes-from=gerrit-data \
-e MYSQL_ROOT_PASSWORD=123456 \
-e MYSQL_DATABASE=reviewdb \
-e MYSQL_USER=gerrit2 \
-e MYSQL_PASSWORD=gerrit \
-d \
mysql:5.6.30

이렇게 긴 명령어를 바로 처넣는다. “거, 실수하기 딱 좋을 위치네”

edit-and-execute-command (C-x C-e)

Invoke an editor on the current command line, and execute the result as shell commands. Bash attempts to invoke $VISUAL, $EDITOR, and emacs as the editor, in that order.

- 8.4.8 Some Miscellaneous Commands

이럴 때, C-x C-e 키를 누르면 된다. 임시파일을 에디터로 연다. 에디터로 편집하고 저장한 내용을 커맨드 라인에 입력한다. 아무것도 설정 안 하면 emacs로 연다. 선인들의 지혜에 감탄을 안 할 수가 없다.

$ export VISUAL=ec-wait
$ export EDITOR=ec-wait
$ cat ec-wait
#!/bin/sh

if [ "OS" = "Windows_NT" ]
then
emacsclientw --alternate-editor=runemacs "$@"
else
emacsclient --alternate-editor=/Applications/Emacs.app/Contents/MacOS/Emacs "$@"
fi

실행 중인 emacs에서 열려고 VISUAL, EDITOR 환경 변수를 설정했다.

참고


#TIL #elixirlang struct 생성 시 모든 키에 대한 값 세팅을 강제하려면

defmodule Ticket do
  @enforce_keys [:origin, :destination, :price]
  defstruct @enforce_keys
end

enforce_keys 모듈 속성(module attributes)에 모두 정의한 다음 defstruct/1 매크로 인자로 넘기면 된다. 예약된 enforce_keys 같은 모듈 속성에 정의된 걸 재사용할 수 있다는 걸 깜빡하곤 한다.

참고 - Enforce all keys in a struct - stackoverflow.com


#TIL #git archive 명령을 사용해 .git 파일을 제외한채 서버에 배포하는 방법

$ git archive --format=tar origin/master \
  | gzip -9c \
  | ssh USER@SERVER "mkdir -p TARGET_DIR; tar --directory=TARGET_DIR -xvzf -"

파일로 저장 안 하고 stdout으로 출력한 데이터를 압축하고 서버에 전송한 후 tar 프로그램으로 푼다. tar 프로그램 - 인자는 데이터를 stdin으로 받겠다는 뜻이다. 크 멋지구나. archive 옵션 같은 건 파일 이름을 받아야 하지 않나? 왜 강제하지 않는 걸까? 이런 의문이 한 번에 풀린다.

참고


#TIL #linux #sed 첫번째 라인만 교체

$ cat test.csv
header1,header2,header3
1,2,3
2,3,1
3,1,2
$ sed -i.bak -e "1s/[^,]/x/g” test.csv
$ cat test.csv
xxxxxxx,xxxxxxx,xxxxxxx
1,2,3
2,3,1
3,1,2

s 명령어 앞에 범위가 없으면 전체를 바꾼다. 1s 명령어로 첫 번째 라인만 바꾸게 했다. 1,3s 명령어는 첫 번째 라인부터 3번째 라인까지 변경한다.

sed 만세. 뭔가 교체하는 거면 sed를 가장 먼저 찾아보자.

참고 - Replace the first line in a text file by a string - stackoverflow


#TIL #svn 저장소(repository) 파일을 삭제하는 스크립트

배포에 SVN을 사용한다. 단순히 복사해서 커밋한다면 삭제한 파일 반영이 안 된다. 삭제된 파일을 반영해야 한다.

$ rm -rf $dest
$ cp -r $source $dest
$ cd $source
$ for i in $(svn st | grep \! | awk '{print $2}'); do svn delete $i; done
$ svn add * --force
$ svn commit -m “deploy"

배포에 많이 쓰는 패턴이다. 삭제 후 복사한다. 이렇게 하면 삭제된 파일이 티가 난다.

’!’ Item is missing (e.g., you moved or deleted it without using svn). This also indicates that a directory is incomplete (a checkout or update was interrupted).

svn st 명령을 입력했을 때, 파일이 저장소에는 있지만 작업 디렉터리(working directory)에 없으면 파일 이름 앞에 ! 문자를 출력한다. 이런 파일을 찾아서 svn delete 인자로 넘긴다. 바로 저장소에서 삭제는 안 된다. svn commit 명령을 실행할 때, 반영된다.

참고