#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 명령을 실행할 때, 반영된다.

참고


#TIL #macos 자동 시작 프로그램 멈추기

FortiClient를 설치했더니 로그인할 때마다 시작한다. 가끔 쓰는데, 계속 떠 있으니 내 노트북이 더러워지는 느낌이다.

시스템 환경설정 > 사용자 및 그룹 > 로그인 항목

여기에 있겠지 했는데, 없다. plist를 직접 수정해야 한다.

$ sudo vi /Library/LaunchAgents/com.fortinet.forticlient.credential_store.plist
  <key>RunAtLoad</key>
- <true/>
+ <false/>
$ sudo vi /Library/LaunchAgents/com.fortinet.forticlient.fct_launcher.plist
  <key>RunAtLoad</key>
- <true/>
+ <false/>

RunAtLoad 값을 수정하면 된다. /Library/LaunchAgents 디렉터리에 다른 파일들도 있다. 로그인할 때, 시작하는 것들이 여기에 있었네.

These per-user processes are referred to as user agents. A user agent is essentially identical to a daemon, but is specific to a given logged-in user and executes only while that user is logged in.

- Creating Launch Daemons and Agents

디렉터리 이름에 agents가 들어가 있다. macos에선 뭘 agents라고 할까? 유저 별로 실행하는 프로세스를 user agents라고 한다. 여기서 agents만 따서 디렉터리 이름을 지은 것 같다.

참고 - Cant stop FortiClient from starting on startup


#TIL #elixir ets에서 key 리스트만 가져오기

iex> :ets.new(:test, [:named_table])
:test
iex> :ets.insert(:test, {:key1, :value1})
true
iex> :ets.insert(:test, {:key2, :value2})
true
iex> :ets.insert(:test, {:key3, :value3})
true
iex> :ets.first(:test)
:key1
iex> :ets.next(:test, :key1)
:key2
iex> :ets.next(:test, :key2)
:key3
iex> :ets.next(:test, :key3)
:"$end_of_table"

:ets.first/1 함수와 :ets.next/2 함수를 사용하면 key만 가져올 수 있다.

iex> keys = fn table_name ->
  Stream.resource(
    fn -> :ets.first(table_name) end,
    fn
      :"$end_of_table" -> {:halt, nil}
      previous_key -> {[previous_key], :ets.next(table_name, previous_key)}
    end,
    fn _ -> :ok end)
  end
#Function<6.128620087/1 in :erl_eval.expr/5>
iex> keys.(:test) |> Enum.to_list()
[:key1, :key2, :key3]

시작, 종료 조건, 다음을 구하는 함수까지 다 준비되어 있다. Stream.resource/3 함수로 스트림(stream)을 만들기. 딱 좋다.

참고 - How to retrieve a list of ets keys without scanning entire table? - stackoverflow.com


#TIL #erlang 함수를 호출할 때는 문자열 타입이 charlist인 걸 기억

iex> :dets.open_file(:storage, [{:type, :set}, {:file, "file"}])
** (ArgumentError) argument error

argument error 뜨면 속 터진다. 원인을 찾아 헤매야 한다.

=”file”= 인자가 잘못됐다. 문자열로 binary를 사용해서 에러가 났다. elixir에서 erlang 함수를 호출했기 때문에 binary가 아니라 charlist를 인자로 넘겨야 한다. elixir에서 byte 시퀀스를 binary라고 부른다.

iex> :dets.open_file(:storage, [{:type, :set}, {:file, 'file'}])

=’file’= 인자를 넘기면 된다. Kernel.to_charlist/1 함수를 호출해도 된다.

참고


#TIL #linux 프로세스 여러 개를 한 번에 죽이는 killall

$ ps -ax | grep beam

프로세스가 여러 개 보인다. 한 번에 지우고 싶다. grep 결과를 가공해서 pid를 추출하고 kill -9 인자로 넘기면 되겠다고 생각했다.

$ killall -9 beam.smp

한방에 하는 게 있네. 프로세스 이름을 인자로 넘기면 된다.

참고


#TIL #elixir 변수를 출력할 때, 큰따옴표로 감싸고 싶다면

iex> answer = “yes"
"yes"
iex> "the answer is #{answer}"
"the answer is yes"

string interpolation을 사용해서 문자열을 만들 때, 변수값을 넣을 수 있다.

변수에서 가져온 값이란 걸 강조하고 싶다. 어떻게?

iex> "the answer is \"#{answer}\""
"the answer is \"yes\”"

\ escape 문자를 사용해야 한다. 강조는 되지만 번거롭다. 좀 더 편한 괄호를 사용하고 말지.

iex> "the answer is #{inspect answer}"
"the answer is \"yes\”"

inspect/2 함수를 사용하면 큰따옴표로 쉽게 감쌀 수 있다.


#TIL #linux nohup 명령어를 사용해 백그라운드 작업 중지에 이뮨 속성을 부여

$ mix run --no-halt > logs 2>&1 &
[1] 2208
$ jobs
[1]+  Stopped                 mix run --no-halt > logs 2>&1

백그라운드로 실행하려고 했는데, 좀 지나면 중지된다.

$ nohup mix run --no-halt > logs 2>&1 &
[1] 2259
$ jobs
[1]+  Running                 nohup mix run --no-halt > logs 2>&1 &

nohup 명령어를 사용하면 된다. HUP(hangup) 시그널을 무시하고 계속 실행한다.

참고


#TIL #linux apt-get으로 특정 버전 패키지 설치

$ sudo apt-get update

최신 패키지 정보로 캐시를 업데이트한다

$ apt-cache policy mysql-server
mysql-server:
  Installed: (none)
  Candidate: 5.7.24-0ubuntu0.16.04.1
  Version table:
     5.7.24-0ubuntu0.16.04.1 500
        500 http://kr.archive.ubuntu.com/ubuntu xenial-updates/main amd64 Packages
        500 http://kr.archive.ubuntu.com/ubuntu xenial-updates/main i386 Packages
        500 http://security.ubuntu.com/ubuntu xenial-security/main amd64 Packages
        500 http://security.ubuntu.com/ubuntu xenial-security/main i386 Packages
     5.7.11-0ubuntu6 500
        500 http://kr.archive.ubuntu.com/ubuntu xenial/main amd64 Packages
        500 http://kr.archive.ubuntu.com/ubuntu xenial/main i386 Packages

apt-cache policy 명령으로 설치된 버전과 설치할 수 있는 버전을 찾는다.

$ sudo apt-get install mysql-server=5.7.24-0ubuntu0.16.04.1

[package]=[version] 형식으로 설치할 패키지 이름과 버전을 지시할 수 있다

참고


#TIL #elixir atom 키를 사용해 struct를 만들거나 업데이트하고 싶다면

defmodule User do
  defstruct name: "ohyecloudy"
end

iex> unknown = struct(User, name: "unknown")
%User{name: "unknown"}

물론 생성할 때도 사용할 수 있다. keyword로 바꾸고 싶은 키와 값을 넘기면 된다.

iex> put_in(unknown.name, "ohyecloudy")
%User{name: "ohyecloudy”}

Dot-based syntax로 접근하면 얼마든지 업데이트할 수 있다.

iex> put_in(unknown, [:name], "ohyecloudy")
** (UndefinedFunctionError) function User.get_and_update/3 is undefined (User does not implement the Access behaviour)
    User.get_and_update(%User{name: "unknown"}, :name, #Function<16.9473146/1 in Kernel.put_in/3>)
    (elixir) lib/access.ex:370: Access.get_and_update/3
    (elixir) lib/kernel.ex:2114: Kernel.put_in/3

map과 다르게 struct는 Bracket-based access는 불가능하다.

iex> struct(unknown, name: "ohyecloudy")
%User{name: "ohyecloudy”}

struct/2, struct!/2 함수를 사용하면 keyword로 업데이트할 수 있다.


#TIL #PostgreSQL pager off

대화형 터미널(interactive terminal)에서 쿼리 결과가 길면 pager가 편하다. 한 페이지에 결과가 다 안 보이기 때문이다. 앞뒤로 왔다 갔다 하면서 확인할 수 있어서 편하다. 하지만 결과가 짧거나 결과를 참고하며 이후 쿼리를 짜고 싶을 때는 pager가 방해된다.

=> \pset pager off

대화형 터미널에서 입력하면 pager를 끌 수 있다

참고 - Turn off pager for psql’s interactive output - serverfault.com


#TIL #elixir get_in 함수에 default 값을 넣는 곳은 없나?

리턴 값이 nil인지 검사해서 다른 값을 사용하는 것보단 nil일 때, 사용할 default 값을 인자로 넘길 수 있다면 로직이 깔끔해진다. 인자를 3개 받는 get_in/3을 기대했지만, get_in/2 밖에 없다. 필요하면 만들어 써야 한다.

def get_in(data, path, default) do
  case get_in(data, path) do
    nil   -> default
    other -> other
  end
end

이렇게 한번 감싸거나

get_in(data, path) || default

short-circuit evaluation에 살짝 기대거나. 이 바닥에선 많이 쓰니깐 이렇게 써도 무방하다.

참고 - get_in/3 with default value - elixir-lang-core


#TIL #linux 읽기 쉬운 파일 크기로 ls

/ddiary/assets/2018-11-09-til-linux-ls-with-unit-suffixes-00.jpg

$ ls -lh

천 단위로 끊어서 대충 읽으려고 안 해도 된다. -h 옵션을 사용하면 Byte, Kilobyte, Megabyte 과 같은 유닛 접미사를 출력한다.

참고


#TIL #docker 컨테이너 timezone 세팅하는 법

$ docker run -e "TZ=Asia/Seoul”

-e 옵션을 사용한다. 컨테이너 TZ 환경 변수에 원하는 시간대를 할당하면 된다.

참고


#TIL #vim URL을 여는 키바인딩 gx

gx 키를 누르면 url을 디폴트 웹브라우저로 연다.

gx 키를 알기 전에는 gf 키로 url을 열었다. emacs evil mode에서 gf를 누르면 find file을 실행한다. url을 인식해 웹브라우저를 호출한다. 열긴 열지만 한 단계를 더 거쳐야 했다. gx 키를 누르면 그런 거 없고 한 번에 팍 뜬다.


#TIL #git 구식 branch를 최신으로 갱신하고 싶다면

$ git checkout master
$ git merge -s ours obsolete-branch

다른 브랜치 변경 사항을 무시한다. merge 커밋이 생길 수 있다는 것만 빼면 obsolete-branch를 지우고 다시 따는 것과 같다.

-s <strategy>

--strategy=<strategy>

Use the given merge strategy; can be supplied more than once to specify them in the order they should be tried. If there is no -s option, a built-in list of strategies is used instead (git merge-recursive when merging a single head, git merge-octopus otherwise).

https://git-scm.com/docs/git-merge

ours, theirs 용어는 항상 헷갈린다. ours는 현재 체크아웃한 브랜치고 theirs는 merge 대상 브랜치다.


#TIL #elixir 리스트에서 마지막 원소만 제외 혹은 마지막 원소만 가져오기

iex> Enum.drop([1, 2, 3], -1)
[1, 2]

iex> Enum.take([1, 2, 3], -1)
[3]

C++이 고향이라서 음수 인덱스는 아직도 어색하다.

참고


#TIL #docker 종료하지 않고 계속 exec로 명령을 할 수 있게 하려면

docker를 tpass 프로그램을 테스트하는 데 사용했다. 간단한 테스트다. 다음과 같은 순서로 진행한다. docker exec 명령을 내린다. 결과값을 검사한다. 다만 이렇게 하려면 컨테이너가 입력을 받을 수 있는 상태여야 한다. 즉, 실행 상태여야 한다. 종료하면 안 된다.

$ docker run -di --rm --name user_a test-add-user bash

bash를 실행해놓고 detach를 하면 되지 않을까? 아니다. 그냥 실행만 하면 안 되고 --interactive , -i 옵션을 써서 STDIN 유지를 해야 한다. 그럼 종료되지 않는다. 입력을 기다리는 상태가 되기 때문이다.

참고 - Why docker container exits immediately - stackoverflow.com


#TIL #bash cut과 dirname으로 path 조작

$ echo a/b/c/d | cut -d/ -f 2
b

$ echo a/b/c/d | cut -d/ -f -2
a/b

$ echo a/b/c/d | cut -d/ -f 2-
b/c/d

-d 옵션으로 자를 구분 문자(delimiter)를 정의한다. index는 1부터 시작한다. -f 옵션으로 남길 index를 입력한다.

$ dirname a/b/c/d
a/b/c

마지막 디렉터리만 없애고 싶을 때는 dirname 명령어를 사용한다.

참고