LoginSignup
526
607

Linuxを触りたての頃に知っておきたかったよ〜ってことのまとめ

Last updated at Posted at 2019-03-05

B4になって研究室配属され、一年たって
なんで早く教えてくれなかったのってことが多数あるから
後輩のためにも記録を残そうと思う。

シェルの種類

ログインシェルのおすすめはzshである。

bashからzshに移行する方法は お前らのターミナルはダサいが非常によく参考になる。(さらbash

シェルの機能系

>,>>,|,&&について

リダイレクション / リダイレクト

> : 標準出力に表示されるものを指定ファイルに上書きする。ちょっとしたファイルを作成するときに使える。
example
$ echo mou_kenkyu_sitakunai > test.dat
$ cat test.dat
mou_kenkyu_sitakunai

仮に、指定ファイルと同名のものがあると上書きされるため注意が必要。

>> : 標準出力に表示されるものを指定ファイルの下に書き足す。

こちらは2つに分かれているプロットしたいデータを連結したり、
トポロジーファイルの合体に使えたりと、よく使う。

example
$ cat test.dat
ohayo
$ echo konnichiha >> test.dat
$ cat test.dat
ohayo
konnichiha

パイプ

| : パイプといい、左側の出力結果を右のコマンドに標準入力として渡せる。

例で説明すると、

example
$ ls #ディレクトリの中身の表示
aaa.mp3          aaa.png          aaa.dat
aaa.txt          bbb.png          ccc.png
ddd.dat

$ ls | grep png #ディレクトリの中身から[png]がついてるものだけを表示
aaa.png
bbb.png
ccc.png

このように、ディレクトリの中のファイル数が多くなって見づらい時とかに使える。

ただし、ディレクトリの中身から末尾に.pngと付くものだけを表示したければ

example
$ ls *.png

がより正確ではある。

また長いファイルから特定の文字が含まれる行だけを抜き出すこともできるため、

example
$ cat test.dat #ファイルの中身の表示
hogehoge        0
hogehoge        8
aaaaaaa         1
bbbbbbb         7
ccccccc         6
tip3p           10
hogehoge        20
hogehoge        38
aaaaaaa         16
bbbbbbb         76
ccccccc         67
tip3p           18
       :

$ cat test.dat | grep tip3p #ファイルの中身から[tip3p]がついてる行だけを表示
tip3p           10
tip3p           18

などの用途がある。
今回はパイプ記号の説明であるため、同様の実行結果となる書き方に関しては省略させていただく。

&& 左のコマンドが(正常に)終わったら右のコマンドを実行

簡単な例としては。新規ディレクトリを作りそこに移動とか?
ちなみに、左側のコマンドが正常に終了しなかった場合(エラーなど)は
右側のコマンドは実行されない。

example
#従来法
$ mkdir testdir
$ cd testdir

## こっちが楽
$ mkdir testdir && cd testdir

他にも、グラフをgnuplotでプロットしたあと画像ファイルに変換とかできる。

conv.sh
#!/bin/bash

pict_name=`ls -t | head -n1 | head -c-4`
#pict_name is "test."

echo " #################################################################"
echo ""
echo "      " `ls -t | head -n1` "  -->>  ${pict_name}png"
echo ""
echo " #################################################################"

convert -density 1000 `ls -t | head -n1` ${pict_name}png

display -resize 1680x1050 ${pict_name}png

このシェルスクリプトは、今いるデイレクトリで一番新しく生成された画像を
pngに変換するものである。
これをホームディレクトリに保存し、
gnuplot aaa.plt && ~/conv.sh
で画像の生成まで一発である。

ワイルドカード指定

  • *:任意の複数文字
  • ?:任意の1文字
example
##現ディレクトリから`.jpg`のファイルだけを移動する。
$ mv *.jpg ~/picture/  

ブレース展開

連番でループしたいときに便利。
いちいちシェルスクリプトを書く必要もない。

example
$ echo {1..10}
1 2 3 4 5 6 7 8 9 10

$ echo {A..Z}
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

$ echo test{1..30..2}
test1 test3 test5 test7 test9 test11 test13 test15 test17 test19 test21 test23 test25 test27 test29

$ echo {A..C}{1..3}
A1 A2 A3 B1 B2 B3 C1 C2 C3

このように、文字でもループできるし、インクリメント数も変更できる。
例ではechoを上げたが、当然すべてのコマンドで使える。
面白いのは、4番目の例のように二個の展開を試みると数学の分配法則のようになる点である。
なお、文字でのループはBashのバージョンによってはできないこともあるので注意。

コマンド系

cd

誰もが知っているディテクトリ移動のコマンドだが、一つ前にいたディレクトリへ戻りたいとき、

$ cd -

とすると戻れる。

example
$ pwd
test1/

$ cd test2
$ pwd
test1/test2/

$ cd -
$ pwd 
test1/

tail

ファイルの中身を表示するコマンドだが、ファイルの最後10行を表示できる。
長いファイルの最後だけを確認するときに使えるし、また
-fオプションをつける(tail -f)で見ているファイルが更新されたら順次表示も更新されていく。
MDのアウトプットファイルがちゃんと更新されていくかどうかの確認で使える。
ただし、ctrl + cを入力するまで終わらないことに注意。

example
$ tail -f mdrun.out
0 aaa
1 bbb
2 ccc
  :(どんどん更新されていく)
ctrl + c を押すまで表示される

エディター系(Vi,Vim)

** Vi(Vim)ははじめは使いにくいと思うかもしれないが、慣れたりカスタマイズするとすごく便利**

カスタマイズする

~/.vimrcに設定を書いておくと様々な設定を起動時に読み込んでくれる。
自分の使ってるものは、予測変換がついたり何かと高機能だがデフォルトのVimでは
一部機能が有効にならないので以下を参考に新しいVimをインストールすべし。
(centos vim lua 有効)とかでググる。

インストールの後、以下の内容を`~/.vimrc`に記述(クリックで展開)
~/.vimrc
set encoding=utf-8
scriptencoding utf-8
" ↑1行目は読み込み時の文字コードの設定
" ↑2行目はVim Script内でマルチバイトを使う場合の設定
" Vim scriptにvimrcも含まれるので、日本語でコメントを書く場合は先頭にこの設定が必要になる

"----------------------------------------------------------
" NeoBundle
"----------------------------------------------------------
if has('vim_starting')
    " 初回起動時のみruntimepathにNeoBundleのパスを指定する
    set runtimepath+=~/.vim/bundle/neobundle.vim/

    " NeoBundleが未インストールであればgit cloneする
    if !isdirectory(expand("~/.vim/bundle/neobundle.vim/"))
        echo "install NeoBundle..."
        :call system("git clone git://github.com/Shougo/neobundle.vim ~/.vim/bundle/neobundle.vim")
    endif
endif

call neobundle#begin(expand('~/.vim/bundle/'))
" インストールするVimプラグインを以下に記述
" NeoBundle自身を管理
NeoBundleFetch 'Shougo/neobundle.vim'
" カラースキームmolokai
NeoBundle 'tomasr/molokai'
"カラースキームiceberg
NeoBundle 'cocopon/iceberg.vim'
"カラースキームjellybeans
NeoBundle 'nanotech/jellybeans.vim'
"カラースキームhybrid
NeoBundle 'w0ng/vim-hybrid'
" ステータスラインの表示内容強化
NeoBundle 'itchyny/lightline.vim'
" インデントの可視化
NeoBundle 'Yggdroot/indentLine'
" 末尾の全角半角空白文字を赤くハイライト
NeoBundle 'bronson/vim-trailing-whitespace'
" 構文エラーチェック
NeoBundle 'scrooloose/syntastic'
" 多機能セレクタ
NeoBundle 'ctrlpvim/ctrlp.vim'
" CtrlPの拡張プラグイン. 関数検索
NeoBundle 'tacahiroy/ctrlp-funky'
" CtrlPの拡張プラグイン. コマンド履歴検索
NeoBundle 'suy/vim-ctrlp-commandline'
" CtrlPの検索にagを使う
NeoBundle 'rking/ag.vim'
" プロジェクトに入ってるESLintを読み込む
NeoBundle 'pmsorhaindo/syntastic-local-eslint.vim'
"カッコの補完
NeoBundle 'cohama/lexima.vim'
"ブロックのendを自動で挿入
NeoBundle 'tpope/vim-endwise'
"ツリー型のファイル表示
NeoBundle 'scrooloose/nerdtree'
"autocmd vimenter * NERDTree
let g:NERDTreeDirArrows = 1
let g:NERDTreeDirArrowExpandable  = '▶'
let g:NERDTreeDirArrowCollapsible = '▼'



"NeoBundle 'Shougo/neocomplete.vim'
"NeoBundle 'Shougo/vimproc.vim', {
"            \ 'build' : {
"            \ 'windows' : 'make -f make_mingw32.mak',
"            \ 'cygwin' : 'make -f make_cygwin.mak',
"            \ 'mac' : 'make -f make_mac.mak',
"            \ 'unix' : 'make -f make_unix.mak',
"            \ },
"            \ }
"NeoBundle 'justmao945/vim-clang'
"NeoBundle 'Shougo/neoinclude.vim'
"
"" 'Shougo/neocomplete.vim' {{{
"let g:neocomplete#enable_at_startup = 1
"if !exists('g:neocomplete#force_omni_input_patterns')
"        let g:neocomplete#force_omni_input_patterns = {}
"endif
"let g:neocomplete#force_overwrite_completefunc = 1
"let g:neocomplete#force_omni_input_patterns.c = '[^.[:digit:] *\t]\%(\.\|->\)'
"let g:neocomplete#force_omni_input_patterns.cpp = '[^.[:digit:] *\t]\%(\.\|->\)\|\h\w*::'
""""}}}
"
"" 'justmao945/vim-clang' {{{
"
"" disable auto completion for vim-clanG
"let g:clang_auto = 0
"let g:clang_complete_auto = 0
"let g:clang_auto_select = 0
"let g:clang_use_library = 1
"
"" default 'longest' can not work with neocomplete
"let g:clang_c_completeopt   = 'menuone'
"let g:clang_cpp_completeopt = 'menuone'
"
"if executable('clang-3.6')
"    let g:clang_exec = 'clang-3.6'
"elseif executable('clang-3.5')
"    let g:clang_exec = 'clang-3.5'
"elseif executable('clang-3.4')
"    let g:clang_exec = 'clang-3.4'
"else
"    let g:clang_exec = 'clang'
"endif
"
"if executable('clang-format-3.6')
"    let g:clang_format_exec = 'clang-format-3.6'
"elseif executable('clang-format-3.5')
"    let g:clang_format_exec = 'clang-format-3.5'
"elseif executable('clang-format-3.4')
"    let g:clang_format_exec = 'clang-format-3.4'
"else
"    let g:clang_exec = 'clang-format'
"endif
"
"let g:clang_c_options = '-std=c11'
"let g:clang_cpp_options = '-std=c++11 -stdlib=libc++'
"
"" }}}
"


"コメントアウト
NeoBundle 'tomtom/tcomment_vim'
" vimのlua機能が使える時だけ以下のVimプラグインをインストールする
if has('lua')
    " コードの自動補完
    NeoBundle 'Shougo/neocomplete.vim'
    " スニペットの補完機能
    NeoBundle "Shougo/neosnippet"
    " スニペット集
    NeoBundle 'Shougo/neosnippet-snippets'
endif

call neobundle#end()

" ファイルタイプ別のVimプラグイン/インデントを有効にする
filetype plugin indent on

" 未インストールのVimプラグインがある場合、インストールするかどうかを尋ねてくれるようにする設定
NeoBundleCheck

"----------------------------------------------------------
" カラースキーム
"----------------------------------------------------------
"if neobundle#is_installed('molokai')
"    colorscheme molokai " カラースキームにmolokaiを設定する
" endif

    colorscheme iceberg " カラースキームにicebergを設定する

"    colorscheme jellybeans " カラースキームにjellybeansを設定する

"    colorscheme hybrid" カラースキームにhybrid(in white background)を設定する

set t_Co=256 " iTerm2など既に256色環境なら無くても良い
syntax enable " 構文に色を付ける


"----------------------------------------------------------
" 文字
"----------------------------------------------------------
set fileencoding=utf-8 " 保存時の文字コード
set fileencodings=ucs-boms,utf-8,euc-jp,cp932 " 読み込み時の文字コードの自動判別. 左側が優先される
set fileformats=unix,dos,mac " 改行コードの自動判別. 左側が優先される
set ambiwidth=double " □や○文字が崩れる問題を解決

"----------------------------------------------------------
" ステータスライン
"----------------------------------------------------------
set laststatus=2 " ステータスラインを常に表示
set showmode " 現在のモードを表示
set showcmd " 打ったコマンドをステータスラインの下に表示
set ruler " ステータスラインの右側にカーソルの位置を表示する

"----------------------------------------------------------
" コマンドモード
"----------------------------------------------------------
set wildmenu " コマンドモードの補完
set history=5000 " 保存するコマンド履歴の数

"----------------------------------------------------------
" タブ・インデント
"----------------------------------------------------------
set expandtab " タブ入力を複数の空白入力に置き換える
set tabstop=4 " 画面上でタブ文字が占める幅
set softtabstop=4 " 連続した空白に対してタブキーやバックスペースキーでカーソルが動く幅
set autoindent " 改行時に前の行のインデントを継続する
set smartindent " 改行時に前の行の構文をチェックし次の行のインデントを増減する
set shiftwidth=4 " smartindentで増減する幅

"----------------------------------------------------------
" 文字列検索
"----------------------------------------------------------
set incsearch " インクリメンタルサーチ. 1文字入力毎に検索を行う
set ignorecase " 検索パターンに大文字小文字を区別しない
set smartcase " 検索パターンに大文字を含んでいたら大文字小文字を区別する
set hlsearch " 検索結果をハイライト

" ESCキー2度押しでハイライトの切り替え
nnoremap <silent><Esc><Esc> :<C-u>set nohlsearch!<CR>

"----------------------------------------------------------
" カーソル
"----------------------------------------------------------
set whichwrap=b,s,h,l,<,>,[,],~ " カーソルの左右移動で行末から次の行の行頭への移動が可能になる
set number " 行番号を表示
set cursorline " カーソルラインをハイライト

" 行が折り返し表示されていた場合、行単位ではなく表示行単位でカーソルを移動する
nnoremap j gj
nnoremap k gk
nnoremap <down> gj
nnoremap <up> gk

" バックスペースキーの有効化
set backspace=indent,eol,start

"カーソルの最終編集位置へ
augroup vimrcEx
  au BufRead * if line("'\"") > 0 && line("'\"") <= line("$") |
  \ exe "normal g`\"" | endif
augroup END

"----------------------------------------------------------
" カッコ・タグの対応
"----------------------------------------------------------
set showmatch " 括弧の対応関係を一瞬表示する
set matchtime=1 " 0.1秒だけ
"let loaded_matchparen=1 止めるとき
source $VIMRUNTIME/macros/matchit.vim " Vimの「%」を拡張する
"hi MatchParen ctermbg=1"
"hi MatchParen term=standout ctermbg=Black ctermfg=LightGrey guibg=Black guifg=LightGrey


"----------------------------------------------------------
" マウスでカーソル移動とスクロール
"----------------------------------------------------------
if has('mouse')
    set mouse=a
    if has('mouse_sgr')
        set ttymouse=sgr
    elseif v:version > 703 || v:version is 703 && has('patch632')
        set ttymouse=sgr
    else
        set ttymouse=xterm2
    endif
endif

"----------------------------------------------------------
" クリップボードからのペースト
"----------------------------------------------------------
" 挿入モードでクリップボードからペーストする時に自動でインデントさせないようにする
if &term =~ "xterm"
    let &t_SI .= "\e[?2004h"
    let &t_EI .= "\e[?2004l"
    let &pastetoggle = "\e[201~"

    function XTermPasteBegin(ret)
        set paste
        return a:ret
    endfunction

    inoremap <special> <expr> <Esc>[200~ XTermPasteBegin("")
endif

"----------------------------------------------------------
" height of menu
"----------------------------------------------------------
    set pumheight=10    "if value is 0 , show menu all
"----------------------------------------------------------
" スペルチェック(試験)
"----------------------------------------------------------
set spelllang=en,cjk

fun! s:SpellConf()
  redir! => syntax
  silent syntax
  redir END

  set spell

hi clear SpellBad
hi SpellBad cterm=underline
hi clear SpellCap
hi SpellCap cterm=underline,bold


  if syntax =~? '/<comment\>'
    syntax spell default
    syntax match SpellMaybeCode /\<\h\l*[_A-Z]\h\{-}\>/ contains=@NoSpell transparent containedin=Comment contained
  else
    syntax spell toplevel
    syntax match SpellMaybeCode /\<\h\l*[_A-Z]\h\{-}\>/ contains=@NoSpell transparent
  endif

  syntax cluster Spell add=SpellNotAscii,SpellMaybeCode
endfunc

augroup spell_check
  autocmd!
  autocmd BufReadPost,BufNewFile,Syntax * call s:SpellConf()
augroup END

"----------------------------------------------------------
" neocomplete・neosnippetの設定
"----------------------------------------------------------
if neobundle#is_installed('neocomplete.vim')
    " Vim起動時にneocompleteを有効にする
    let g:neocomplete#enable_at_startup = 1
    " smartcase有効化. 大文字が入力されるまで大文字小文字の区別を無視する
    let g:neocomplete#enable_smart_case = 1
    " 3文字以上の単語に対して補完を有効にする
    let g:neocomplete#min_keyword_length = 3
    " 区切り文字まで補完する
    let g:neocomplete#enable_auto_delimiter = 1
    " 1文字目の入力から補完のポッ"プアップを表示
    let g:neocomplete#auto_completion_start_length = 1
    " バックスペースで補完のポップアップを閉じる
    inoremap <expr><BS> neocomplete#smart_close_popup()."<C-h>"

    " エンターキーで補完候補の確定. スニペットの展開もエンターキーで確定
    imap <expr><CR> neosnippet#expandable() ? "<Plug>(neosnippet_expand_or_jump)" : pumvisible() ? "<C-y>" : "<CR>"
    " タブキーで補完候補の選択. スニペット内のジャンプもタブキーでジャンプ
    imap <expr><TAB> pumvisible() ? "<C-n>" : neosnippet#jumpable() ? "<Plug>(neosnippet_expand_or_jump)" : "<TAB>"
endif

"----------------------------------------------------------
" Syntastic
"----------------------------------------------------------
" 構文エラー行に「>>」を表示
let g:syntastic_enable_signs = 1
" 他のVimプラグインと競合するのを防ぐ
let g:syntastic_always_populate_loc_list = 1
" 構文エラーリストを非表示
let g:syntastic_auto_loc_list = 0
" ファイルを開いた時に構文エラーチェックを実行する
let g:syntastic_check_on_open = 1
" 「:wq」で終了する時も構文エラーチェックする
let g:syntastic_check_on_wq = 1


" python setting
let g:syntastic_python_checkers = ['flake8']

"" Javascript用. 構文エラーチェックにESLintを使用
"let g:syntastic_javascript_checkers=['eslint']
"" Javascript以外は構文エラーチェックをしない
"let g:syntastic_mode_map = { 'mode': 'passive',
"                           \ 'active_filetypes': ['javascript'],
"                           \ 'passive_filetypes': [] }
"
"----------------------------------------------------------
" CtrlP
"----------------------------------------------------------
let g:ctrlp_match_window = 'order:ttb,min:20,max:20,results:100' " マッチウインドウの設定. 「下部に表示, 大きさ20行で固定, 検索結果100件」
let g:ctrlp_show_hidden = 1 " .(ドット)から始まるファイルも検索対象にする
let g:ctrlp_types = ['fil'] "ファイル検索のみ使用
let g:ctrlp_extensions = ['funky', 'commandline'] " CtrlPの拡張として「funky」と「commandline」を使用

" CtrlPCommandLineの有効化
command! CtrlPCommandLine call ctrlp#init(ctrlp#commandline#id())

" CtrlPFunkyの絞り込み検索設定
let g:ctrlp_funky_matchtype = 'path'

if executable('ag')
  let g:ctrlp_use_caching=0 " CtrlPのキャッシュを使わない
  let g:ctrlp_user_command='ag %s -i --hidden -g ""' " 「ag」の検索設定
endif 

"---------------------------------------------------------
" CtrlP
"----------------------------------------------------------
let g:lightline = {
        \ 'colorscheme': 'wombat',
        \ 'mode_map': {'c': 'NORMAL'},
        \ 'active': {
        \   'left': [ [ 'mode', 'paste' ], [ 'fugitive', 'filename' ] ]
        \ },
        \ 'component_function': {
        \   'modified': 'LightlineModified',
        \   'readonly': 'LightlineReadonly',
        \   'fugitive': 'LightlineFugitive',
        \   'filename': 'LightlineFilename',
        \   'fileformat': 'LightlineFileformat',
        \   'filetype': 'LightlineFiletype',
        \   'fileencoding': 'LightlineFileencoding',
        \   'mode': 'LightlineMode'
        \ }
        \ }

function! LightlineModified()
  return &ft =~ 'help\|vimfiler\|gundo' ? '' : &modified ? '+' : &modifiable ? '' : '-'
endfunction

function! LightlineReadonly()
  return &ft !~? 'help\|vimfiler\|gundo' && &readonly ? 'x' : ''
endfunction

function! LightlineFilename()
  return ('' != LightlineReadonly() ? LightlineReadonly() . ' ' : '') .
        \ (&ft == 'vimfiler' ? vimfiler#get_status_string() :
        \  &ft == 'unite' ? unite#get_status_string() :
        \  &ft == 'vimshell' ? vimshell#get_status_string() :
        \ '' != expand('%:t') ? expand('%:t') : '[No Name]') .
        \ ('' != LightlineModified() ? ' ' . LightlineModified() : '')
endfunction

function! LightlineFugitive()
  if &ft !~? 'vimfiler\|gundo' && exists('*fugitive#head')
    return fugitive#head()
  else
    return ''
  endif
endfunction

function! LightlineFileformat()
  return winwidth(0) > 70 ? &fileformat : ''
endfunction

function! LightlineFiletype()
  return winwidth(0) > 70 ? (&filetype !=# '' ? &filetype : 'no ft') : ''
endfunction

function! LightlineFileencoding()
  return winwidth(0) > 70 ? (&fenc !=# '' ? &fenc : &enc) : ''
endfunction

function! LightlineMode()
  return winwidth(0) > 60 ? lightline#mode() : ''
endfunction

我ながら凄まじい量だと思うが、すべてを使う必要もないと思う。
幸いコメントが書いてあるので、使わない部分は消してもらって構わない。
ここからはVimの挿入・ヴィジュアルモードくらいは理解しているものとして説明する。

Vimの便利な機能

一括置換

  • :%s/置換したい文字/置換後の文字/g

文章中のすべての置換したい文字を置き換えてくれる。
また%s92sとかにすると92行目だけ置換が行われる。

文字列検索

  • /検索したい文字

一致する文字を検索し、表示してくれる。またnキーを押すことで次に一致する部分までジャンプ。
検索を辞めたければ、escキーを連打すればいい。
また先ほどの.vimrcを記述していれば
小文字で検索をかければ大文字も検索でき、
大文字で検索をすると大文字しかヒットしません。

一括挿入

①ctrl + vでヴィジュアルブロックモードに入る
②挿入したい範囲を選ぶ
③shift + iで挿入モードへ
④挿入する語句を打ち込む
⑤エスケープキーを押すと挿入される。

一括切り取り

上とおんなじ理屈で文字を切り取ることもできる。
コメントアウトした部分を一気に戻すのに便利。

①ctrl + vでヴィジュアルブロックモードに入る
②切り取りたい範囲を選ぶ
③xで切り取る

Vimdiff:差分表示

vimdiff ファイル1 ファイル2で2つのファイルの
異なる部分だけを表示できる。
MDの設定ファイルの間違い探しなどで便利

example
$ vimdiff test1.dat test2.dat

結果
Screenshot from 2019-03-06 14-42-34.png

日本語入力系

かな漢字を選べば、日本語入力は一応できるが、超絶使いにくいのでMozcのインストールを推奨。
Centos7でMozc(Google日本語入力)を使う方法
を、参考にしてほしい(宣伝)。

## MD系

VMDでのスナップショットの保存の仕方

VMD(Visual Molecular Dynamics)でMDシミュレーションの綺麗なスナップショットを作る
を、参考にしてほしい(宣伝)。

Pythonによる解析ツール

輪講で習うFortranなんて捨てたほうがいい。
今流行りのPythonを勉強して、便利な解析ツールを使おう!
MDの計算結果を解析できるPythonライブラリ:MDAnalysisのチュートリアルを日本語化する#1
を、参考にしてほしい(宣伝)。

526
607
14

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
526
607