2019-09-04
$ 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분이 넘는 시간을 쓰고 다음 스텝에 쓰는 프로그램이 없다고 종료해버리면 허탈하다.
2019-08-27
$ 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 옵션도 있다.
참고
2019-08-10
:%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
명령어에서 쓰고 싶을 때, 주로 사용한다.
참고
2019-07-20
$ git add sample.sh
$ git update-index --chmod=+x sample.sh
windows에서 파일을 만들면 권한을 644로 세팅한다. windows에 배포하면 문제가 없다. 무시하니깐. 하지만 linux에 배포하면 문제다. 실행하기 전에 파일 권한을 고쳐야 한다. update-index --chmod=+x
옵션을 사용하면 windows에서도 파일 권한을 755로 변경해 커밋할 수 있다.
참고
2019-06-09
$ echo -e '\n. $HOME/.asdf/asdf.sh' >> ~/.bashrc
asdf를 설치하다가 본 .
연산자. 뭔가 해서 찾아보니 source
명령이랑 같다. 현재 shell 컨텍스트에서 파일을 읽고 실행하는 명령이다.
환경 변수를 조작하는 스크립트를 실행할 때, 많이 사용한다. source 명령으로 PATH를 조작하는 스크립트를 실행하면 현재 shell 컨텍스트에 있는 PATH 환경 변수를 수정할 수 있다. source 명령어를 안 쓰고 그냥 호출하면 실행하면서 세팅되는 shell 컨텍스트에만 적용되고 현재 shell 컨텍스트에는 영향을 못 준다.
참고
2019-06-02
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 함수까지 쓸 필요 있나? :
문자를 앞에 붙이면 된다.
2019-05-24
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
2019-05-17
$ 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
환경 변수를 설정했다.
참고
2019-04-26
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
2019-04-05
$ 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 옵션 같은 건 파일 이름을 받아야 하지 않나? 왜 강제하지 않는 걸까? 이런 의문이 한 번에 풀린다.
참고
2019-03-27
$ 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
2019-03-21
배포에 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
명령을 실행할 때, 반영된다.
참고
2019-03-13
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
2019-02-03
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
2019-01-23
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 함수를 호출해도 된다.
참고
2019-01-15
$ ps -ax | grep beam
프로세스가 여러 개 보인다. 한 번에 지우고 싶다. grep 결과를 가공해서 pid를 추출하고 kill -9 인자로 넘기면 되겠다고 생각했다.
$ killall -9 beam.smp
한방에 하는 게 있네. 프로세스 이름을 인자로 넘기면 된다.
참고
2019-01-08
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 함수를 사용하면 큰따옴표로 쉽게 감쌀 수 있다.
2018-12-29
$ 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) 시그널을 무시하고 계속 실행한다.
참고
2018-12-19
$ 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]
형식으로 설치할 패키지 이름과 버전을 지시할 수 있다
참고
2018-12-13
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로 업데이트할 수 있다.