Closed13

drawthe.netを使ってシステム構成図を書く

Masayoshi Tohna@おれさまラボMasayoshi Tohna@おれさまラボ

drawthe.net

複雑なネットワーク図も「テキストで書いてブラウザ上でSVGレンダリングできるようにしよう」というコンセプトのもと開発されたツールです。下図のように複雑な構成図も精度高く描くことができます。

拡大してみると情報量が多いこと、またいかに整っているかがわかると思います。

デモサイトも用意されているので、サクッと試したい場合はコチラが便利です。コードはGitHubで公開されています。更新が2017年末で止まってしまっているのが玉に瑕ですが、十分な性能を発揮してくれます。

Masayoshi Tohna@おれさまラボMasayoshi Tohna@おれさまラボ

drawthe.netを使いたい理由

美しい構成図といえばInteropを思い出しますよね。

参考:Interopのネットワーク図を描くエンジニア:Geekなぺーじ

美しく整った図は人を魅了しますし、機能性にも優れています。システム構成図はさまざまな人(開発エンジニア、保守・運用エンジニア、オペレータなど)から参照される資料なので、なるべく美しく整った状態であるべきと考えます。しかし、Visoやdraw.ioでこのような図を書こうと思うと本当に大変ですし、人の能力に依存します。また、Visoやdraw.ioで書かれた美しい構成図は、メンテナンス性が悪く、1ピクセルの配置のために常にズームインとズームアウトを繰り返すこととなります。

前書きが長くなりましたが、drawthe.netを使いたい理由は上記背景から下記3つとなります。

  1. 誰でも整った図を書けるツールがほしい
  2. メンテナンス性に優れたツールがほしい
  3. 変更に対する差分がわかるようにしたい

これらを満たすツールがdrawthe.netだったわけです。

Masayoshi Tohna@おれさまラボMasayoshi Tohna@おれさまラボ

drawthe.netをdockerイメージで使ってみる

先述のデモサイトでも十分使えますが、興味本位から自環境内にdockerで建ててみます。

dockerイメージを探す

dockerイメージを探してみましたが、公式はなく、個人配布もほとんどありませんでした。

$ docker search drawthenet
NAME                      DESCRIPTION             STARS               OFFICIAL            AUTOMATED
thomasbridge/drawthenet   My drawthe.net images   1

こちらのリポジトリは1年以上にdockerhubに上げられたものですが、十分使えそうです。

とりあえずpullして

$ docker pull thomasbridge/drawthenet:nginx

イメージを確認して

$ docker images thomasbridge/drawthenet:nginx
REPOSITORY                TAG                 IMAGE ID            CREATED             SIZE
thomasbridge/drawthenet   nginx               72163b5fff23        15 months ago       237MB

runします。80番ポートをローカルの4000番ポートに紐づけてみました。

$ docker run --rm -d -p 4000:80 thomasbridge/drawthenet:nginx
0f21cb1d9cf90bd858c0dd9afe6893f8b7660f8c8ac1523fad2a1de9f4909f0d

起動を確認します。

$ docker ps
CONTAINER ID        IMAGE                            COMMAND                  CREATED             STATUS              PORTS                    NAMES
0f21cb1d9cf9        thomasbridge/drawthenet:nginx    "nginx -g 'daemon of…"   53 seconds ago      Up 52 seconds       0.0.0.0:4000->80/tcp     objective_saha

問題なくアクセスできました。

Masayoshi Tohna@おれさまラボMasayoshi Tohna@おれさまラボ

docker buildしてみる①

なんとなく、自前のdockerイメージをつくってみます。

分析

先のdockerイメージ(thomasbridge/drawthenet:nginx)のベースOSはDebianのようです。

$ docker exec 0f21cb1d9cf9 cat /etc/*-release
cat: /etc/lsb-release: No such file or directory
PRETTY_NAME="Debian GNU/Linux 9 (stretch)"
NAME="Debian GNU/Linux"
VERSION_ID="9"
VERSION="9 (stretch)"
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"

history を見てみるとnginxをビルドして、git cloneでdrawthe.netのリポジトリを引っ張ってきているだけのようです。

$ docker history thomasbridge/drawthenet:nginx
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
72163b5fff23        15 months ago       /bin/sh -c #(nop)  EXPOSE 80                    0B
<missing>           15 months ago       /bin/sh -c git clone https://github.com/cidr…   27.8MB
<missing>           15 months ago       /bin/sh -c rm index.html 50x.html               0B
<missing>           15 months ago       /bin/sh -c apt-get update && apt-get install…   99.4MB
<missing>           15 months ago       /bin/sh -c #(nop) WORKDIR /usr/share/nginx/h…   0B
<missing>           15 months ago       /bin/sh -c #(nop)  MAINTAINER Thomas Bridge …   0B
<missing>           19 months ago       /bin/sh -c #(nop)  CMD ["nginx" "-g" "daemon…   0B
<missing>           19 months ago       /bin/sh -c #(nop)  STOPSIGNAL SIGTERM           0B
<missing>           19 months ago       /bin/sh -c #(nop)  EXPOSE 80                    0B
<missing>           19 months ago       /bin/sh -c ln -sf /dev/stdout /var/log/nginx…   22B
<missing>           19 months ago       /bin/sh -c set -x  && apt-get update  && apt…   54.1MB
<missing>           19 months ago       /bin/sh -c #(nop)  ENV NJS_VERSION=1.15.12.0…   0B
<missing>           19 months ago       /bin/sh -c #(nop)  ENV NGINX_VERSION=1.15.12…   0B
<missing>           20 months ago       /bin/sh -c #(nop)  LABEL maintainer=NGINX Do…   0B
<missing>           20 months ago       /bin/sh -c #(nop)  CMD ["bash"]                 0B
<missing>           20 months ago       /bin/sh -c #(nop) ADD file:4fc310c0cb879c876…   55.3MB

省略なしでヒストリーを見たい場合は --no-trunc をつけてください。

$ docker history --no-trunc thomasbridge/drawthenet:nginx > history.txt

実践

とりあえず作ってみます。ベースイメージは nginx を使い、シンプルに git clone するだけにします。

$ mkdir ~/project/drawthenet-test
$ cd ~/project/drawthenet-test
$ git clone https://github.com/cidrblock/drawthe.net.git
Cloning into 'drawthe.net'...
remote: Enumerating objects: 13309, done.
remote: Total 13309 (delta 0), reused 0 (delta 0), pack-reused 13309
Receiving objects: 100% (13309/13309), 20.31 MiB | 1.61 MiB/s, done.
Resolving deltas: 100% (4359/4359), done.
$ vi Dockerfile
$ cat Dockerfile
FROM nginx:latest
MAINTAINER Masayoshi Tohna
WORKDIR /usr/share/nginx/html
COPY ./drawthe.net/* /usr/share/nginx/html/

では、ビルドします。

$ docker build --no-cache -t drawthenet-test:0.1.1 .
Sending build context to Docker daemon  28.61MB
Step 1/4 : FROM nginx:latest
 ---> daee903b4e43
Step 2/4 : MAINTAINER Masayoshi Tohna
 ---> Running in ce257ba2fda5
Removing intermediate container ce257ba2fda5
 ---> b982bb23d361
Step 3/4 : WORKDIR /usr/share/nginx/html
 ---> Running in 16aaead7564f
Removing intermediate container 16aaead7564f
 ---> 8f47acfb0bbc
Step 4/4 : COPY ./drawthe.net/* /usr/share/nginx/html/
 ---> 363c26bea7ac
Successfully built 363c26bea7ac
Successfully tagged drawthenet-test:0.1.1

イメージを確認。

$ docker images drawthenet-test:0.1.1
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
drawthenet-test     0.1.1               363c26bea7ac        54 seconds ago      161MB

起動します。

$ docker run -d -p 4001:80 drawthenet-test:0.1.1
94de3748233e3191b787f1365043c5f428a5c6b768c4cf8eb46716672643d5d2
$ docker ps
CONTAINER ID        IMAGE                            COMMAND                  CREATED             STATUS              PORTS                    NAMES
94de3748233e        drawthenet-test:0.1.1            "/docker-entrypoint.…"   10 seconds ago      Up 9 seconds        0.0.0.0:4001->80/tcp     exciting_elgamal

残念。表示が崩れてしまいました。

Masayoshi Tohna@おれさまラボMasayoshi Tohna@おれさまラボ

docker buildしてみる②

調べてみたところ、nodeが必要なようです。ベースイメージをnodeにして、nginxを導入する流れにしてみます。

$ cat Dockerfile
FROM node:6.17.0-alpine
MAINTAINER Masayoshi Tohna
RUN apk update && apk --no-cache add nginx
WORKDIR /usr/share/nginx/html
RUN rm -f *
COPY ./drawthe.net/* /usr/share/nginx/html/
CMD nginx -g "daemon off;"

ビルドします。

$ docker build --no-cache -t drawthenet-test:0.1.2 .
Sending build context to Docker daemon  28.61MB
Step 1/7 : FROM node:6.17.0-alpine
 ---> 7c9d8e1567b1
Step 2/7 : MAINTAINER Masayoshi Tohna
 ---> Running in a400a53db3ee
Removing intermediate container a400a53db3ee
 ---> 77145f10a4f0
Step 3/7 : RUN apk update && apk --no-cache add nginx
 ---> Running in d8caf9efce35
fetch http://dl-cdn.alpinelinux.org/alpine/v3.8/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.8/community/x86_64/APKINDEX.tar.gz
v3.8.5-65-g097f0358a9 [http://dl-cdn.alpinelinux.org/alpine/v3.8/main]
v3.8.5-37-gf06ffe835a [http://dl-cdn.alpinelinux.org/alpine/v3.8/community]
OK: 9565 distinct packages available
fetch http://dl-cdn.alpinelinux.org/alpine/v3.8/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.8/community/x86_64/APKINDEX.tar.gz
(1/2) Installing pcre (8.42-r0)
(2/2) Installing nginx (1.14.2-r2)
Executing nginx-1.14.2-r2.pre-install
Executing busybox-1.28.4-r3.trigger
OK: 7 MiB in 17 packages
Removing intermediate container d8caf9efce35
 ---> 71cfb2217c02
Step 4/7 : WORKDIR /usr/share/nginx/html
 ---> Running in 31de29b6bb59
Removing intermediate container 31de29b6bb59
 ---> 253552423e40
Step 5/7 : RUN rm -f *
 ---> Running in 1f610efda890
Removing intermediate container 1f610efda890
 ---> 71adc1a7fdf6
Step 6/7 : COPY ./drawthe.net/* /usr/share/nginx/html/
 ---> 2dade49c7640
Step 7/7 : CMD nginx -g "daemon off;"
 ---> Running in 47c324aff65c
Removing intermediate container 47c324aff65c
 ---> c143b7a0dd05
Successfully built c143b7a0dd05
Successfully tagged drawthenet-test:0.1.2

起動しません。

あ、nginxのconfいじる必要あり?今日はここまで。
※最終的にはアイコンをAWSの最新のものに入れ替えるところまでやる予定。

Masayoshi Tohna@おれさまラボMasayoshi Tohna@おれさまラボ

docker buildしてみる③

Ubuntuベースでdockerイメージを作りなおす。

試行錯誤の結果、drawthenetはnode v6.x以前でないと動かない。

参考:【2019年6月版】fs.writeFile したら ERR_INVALID_CALLBACK ですって - Qiita

古い node を動かすには nvm でインストールすると良いらしい。

参考:Ubuntu でapt を使用してNode.js をインストールする3 つの方法(Ubuntu 15.04, Ubuntu 14.04.2 LTS) - Qiita

Qiitaの手順でやると nvm を実行するときにターミナルの再読み込みを要求されて、乗り越え方がわからなかった。

ということで、以下のサイトを参考に行った。

参考:node.js — dockerにnvmをインストールします

Dockerfile
FROM ubuntu:latest
MAINTAINER Masayoshi Tohna

# Replace Shell with bash so we can source files
RUN rm /bin/sh && ln -s /bin/bash /bin/sh

# make sure apt is up to date
RUN apt update --fix-missing
RUN apt install -y curl
RUN apt install -y build-essential libssl-dev

ENV NVM_DIR /usr/local/nvm
ENV NODE_VERSION 6.17.1

# Install nvm with node and npm
RUN curl https://raw.githubusercontent.com/creationix/nvm/v0.30.1/install.sh | bash \
    && source $NVM_DIR/nvm.sh \
    && nvm install $NODE_VERSION \
    && nvm alias default $NODE_VERSION \
    && nvm use default

ENV NODE_PATH $NVM_DIR/v$NODE_VERSION/lib/node_modules
ENV PATH      $NVM_DIR/versions/node/v$NODE_VERSION/bin:$PATH

RUN node -v
Masayoshi Tohna@おれさまラボMasayoshi Tohna@おれさまラボ

最終的に nginx:latest をベースに作成。無事、ブラウザで呼び出すことができた。

Dockerfile
FROM nginx:latest
MAINTAINER Masayoshi Tohna

# Replace Shell with bash so we can source files
RUN rm /bin/sh && ln -s /bin/bash /bin/sh

# make sure apt is up to date
RUN apt update --fix-missing
RUN apt install -y curl
RUN apt install -y build-essential libssl-dev

ENV NVM_DIR /usr/local/nvm
ENV NODE_VERSION 6.17.1

# Install nvm with node and npm
RUN curl https://raw.githubusercontent.com/creationix/nvm/v0.30.1/install.sh | bash \
    && source $NVM_DIR/nvm.sh \
    && nvm install $NODE_VERSION \
    && nvm alias default $NODE_VERSION \
    && nvm use default

ENV NODE_PATH $NVM_DIR/v$NODE_VERSION/lib/node_modules
ENV PATH      $NVM_DIR/versions/node/v$NODE_VERSION/bin:$PATH

RUN node -v

WORKDIR /usr/share/nginx/html
COPY drawthe.net .

ビルドは以下の通り。

$ docker build --no-cache -t drawthenet:debian-nginx-node6.17.1 .

起動は以下の通り。

docker run --rm -d -p 4000:80 drawthenet:debian-nginx-node6.17.1
Masayoshi Tohna@おれさまラボMasayoshi Tohna@おれさまラボ

アイコンを変更する

drawthe.net のリポジトリに含まれているアイコンは古いので、最新のAWSアイコンに変更したい。

AWS公式が配布している アセットパッケージ をダウンロードして svg 画像を抜き出す。

参考:AWS シンプルアイコン - AWS アーキテクチャーセンター | AWS

drawthenet リポジトリの build/images/aws のアイコンを置き換える。
※古いアイコンも使いたければ置いといてもOK。

これで、docker コンテナを再ビルドしてあげれば新しいアイコンを使えるようになる。アイコンの指定は拡張子なしのファイル名でOK。

BEFORE AFTER
Masayoshi Tohna@おれさまラボMasayoshi Tohna@おれさまラボ

一応、テキストも残しておく。

BEFORE
icons:
  dns: {<<: *iconDefaults, icon: net_route53_hostedzone, x: 2, y: 3 }
  lb: {<<: *iconDefaults, icon: cmpt_elasticloadbalancing, y: "-1" }
  server1: {<<: *iconDefaults, icon: cmpt_ec2_instance, x: "-1", y: "-1" }
  server2: {<<: *iconDefaults, icon: cmpt_ec2_instance, x: "+1" }
  server3: {<<: *iconDefaults, icon: cmpt_ec2_instance, x: "+1" }
AFTER
icons:
  dns: {<<: *iconDefaults, icon: arch_route53_16, x: 2, y: 3 }
  lb: {<<: *iconDefaults, icon: arch_elasticloadbalancing_16, y: "-1" }
  server1: {<<: *iconDefaults, icon: arch_ec2_16, x: "-1", y: "-1" }
  server2: {<<: *iconDefaults, icon: arch_ec2_16, x: "+1" }
  server3: {<<: *iconDefaults, icon: arch_ec2m5n_32, x: "+1" }
Masayoshi Tohna@おれさまラボMasayoshi Tohna@おれさまラボ

もし、入れ替えた画像がうまく反映されない場合は drawthe.net/build/images/build_list.js をコンテナ内で実行してあげると反映される模様。この build_list.js が node v7.x 以降だと動かないので、今回作った Dockerfile では v6.17.1 で固定している。

Masayoshi Tohna@おれさまラボMasayoshi Tohna@おれさまラボ

使いたいアイコンを整理する

とりあえず、AWS公式アイコン集から svg を一つの階層にまとめる。

$ ~/project/drawthenet/aws-icons
$ find AWS-Architecture-Service-Icons_20200911/ -type f -name "*.svg" -exec cp {} ~/project/drawthenet/aws-icons \;

svg だけで全部で897ファイルあるようです。

Arch_AWS-Amplify-Console_16.svg みたいなファイル名なので、すべてのファイルに共通している先頭の Arch_ を rename コマンドで削除する。

$ rename s/Arch_// *.svg

複数サイズあると邪魔なので、デカサイズだけ残す。

$ for i in $(ls | grep -E "_16|_32|_48"); do rm -f $i; done

ここまでで228ファイルまで減少。

_64 という文言も不要なのでリネームしてしまいます。

$ rename s/_64// *.svg

ということで、クレンジング完了。※邪魔なので拡張子も除去した

AWS-Amplify-Console
AWS-Apache-MXNet
AWS-App-Config
AWS-App-Mesh
AWS-App-Wizard
AWS-AppSync
AWS-Application-Discovery-Service
AWS-Artifact
AWS-Auto-Scaling
AWS-Backint-Agent
AWS-Backup
AWS-Batch
AWS-Bottlerocket
AWS-Braket
AWS-Budgets
AWS-Certificate-Manager
AWS-Chatbot
AWS-Cloud-Development-Kit
AWS-Cloud-Trail
AWS-Cloud9
AWS-CloudEndure-Disaster-Recovery
AWS-CloudEndure-Migration
AWS-CloudFormation
AWS-CloudHSM
AWS-CloudMap
AWS-CodeArtifact
AWS-CodeCommit
AWS-CodeDeploy
AWS-CodePipeline
AWS-Command-Line-Interface
AWS-Compute-Optimizer
AWS-Config
AWS-Console-Mobile-Application _16
AWS-Console-Mobile-Application _32
AWS-Console-Mobile-Application _48
AWS-Console-Mobile-Application
AWS-Control-Tower
AWS-Cost-Explorer
AWS-Cost-and-Usage-Report
AWS-Data-Exchange
AWS-Data-Pipeline
AWS-Data-Sync
AWS-Database-Migration
AWS-Deep-Composer
AWS-Deep-Learning-AMIs
AWS-Deep-Learning-Containers
AWS-Deep-Lense
AWS-Deepracer
AWS-Device-Farm
AWS-Direct-Connect
AWS-Directory-Service
AWS-EC2-Image-Builder
AWS-Elastic-Beanstalk
AWS-Elemental-Appliances-&-Software
AWS-Elemental-Link
AWS-Elemental-MediaConnect
AWS-Elemental-MediaConvert
AWS-Elemental-MediaLive
AWS-Elemental-MediaPackage
AWS-Elemental-MediaStore
AWS-Elemental-MediaTailor
AWS-Express-Workflow
AWS-Fargate
AWS-Firewall-Manager
AWS-Global-Accelerator
AWS-Glue
AWS-Ground-Station
AWS-IQ
AWS-Identity-and-Access-Management
AWS-IoT-1-Click
AWS-IoT-Analytics
AWS-IoT-Button
AWS-IoT-Core
AWS-IoT-Device-Defender
AWS-IoT-Device-Management
AWS-IoT-Events
AWS-IoT-Greengrass-Core
AWS-IoT-SiteWise
AWS-IoT-Things-Graph
AWS-Key-Management-Services
AWS-Lake-Formation
AWS-Lambda
AWS-License-Manager
AWS-Local-Zones
AWS-Managed-Services
AWS-Management-Console
AWS-Migration-Hub
AWS-Nitro-Enclaves
AWS-OpsWorks
AWS-Organizations
AWS-Outposts
AWS-Parallel-Cluster
AWS-Personal-Health-Dashboard
AWS-PrivateLink
AWS-Professional-Services
AWS-Resources-Access-Manager
AWS-RoboMaker
AWS-SageMaker-Ground-Truth
AWS-SageMaker
AWS-Secrets-Manager
AWS-Security-Hub
AWS-Server-Migration-Service
AWS-Serverless-Application-Repository
AWS-Service-Catalog
AWS-Shield
AWS-Simple-Email-Service
AWS-Simple-Notification-Service
AWS-Simple-Queue-Service
AWS-Single-Sign-On
AWS-Site-to-Site-VPN
AWS-Snowball-Edge
AWS-Snowball
AWS-Snowcone
AWS-Snowmobile
AWS-Step-Functions
AWS-Storage-Gateway
AWS-Sumerian
AWS-Support
AWS-Systems-Manager
AWS-TensorFlow-on-AWS
AWS-Textract
AWS-ThinkBox-Deadline
AWS-ThinkBox-Frost
AWS-ThinkBox-Krakatoa
AWS-ThinkBox-Sequoia
AWS-ThinkBox-Stoke
AWS-ThinkBox-XMesh
AWS-Tools-and-SDK
AWS-Training-Certification
AWS-Transfer-Family
AWS-Transit-Gateway
AWS-Trusted-Advisor
AWS-VPN
AWS-WAF
AWS-Wavelength
AWS-Well-Architect-Tool
AWS-WorkLink
AWS-WorkSpaces
AWS-X-Ray
Alexa-For-Business
Amazon-API-Gateway
Amazon-App-Stream
Amazon-Application-Auto-Scaling
Amazon-Athena
Amazon-Augmented-AI-A2I
Amazon-Aurora
Amazon-Chime
Amazon-Cloud-Directory
Amazon-CloudFront
Amazon-CloudSearch
Amazon-CloudWatch
Amazon-CodeBuild
Amazon-CodeGuru
Amazon-Codestar
Amazon-Cognito
Amazon-Comprehend
Amazon-Connect
Amazon-Detective
Amazon-DocumentDB
Amazon-DynamoDB
Amazon-EC2-Auto-Scaling
Amazon-EC2-M5n
Amazon-EC2-R5n
Amazon-EC2
Amazon-EMR
Amazon-ElastiCache
Amazon-Elastic-Block-Store
Amazon-Elastic-Container-Kubernetes
Amazon-Elastic-Container-Registry
Amazon-Elastic-Container-Service
Amazon-Elastic-File-System
Amazon-Elastic-Inference
Amazon-Elastic-Transcoder
Amazon-Elasticsearch-Service
Amazon-EventBridge
Amazon-FSx-For-WFS
Amazon-FSx-for-Lustre
Amazon-FSx
Amazon-Forecast
Amazon-Fraud-Detector
Amazon-FreeRTOS
Amazon-GameLift
Amazon-Glacier
Amazon-GuardDuty
Amazon-Inspector
Amazon-Interactive-Video
Amazon-Kendra
Amazon-Keyspaces
Amazon-Kinesis-Data-Analytics
Amazon-Kinesis-Data-Streams
Amazon-Kinesis-Firehose
Amazon-Kinesis-Video-Streams
Amazon-Kinesis
Amazon-Lex
Amazon-Lightsail
Amazon-Lumberyard
Amazon-MQ
Amazon-Macie
Amazon-Managed-Blockchain
Amazon-Managed-Streaming-for-Kafka
Amazon-Neptune
Amazon-Neuron-ML-SDK
Amazon-Personalize
Amazon-Pinpoint-Journey
Amazon-Pinpoint
Amazon-Polly
Amazon-Quantum-Ledger-Database
Amazon-QuickSight
Amazon-RDS-for-VMware
Amazon-RDS
Amazon-Redshift
Amazon-Redshiftct
Amazon-Rekognition
Amazon-Route-53
Amazon-S3-Standard
Amazon-Timestream
Amazon-Transcribe
Amazon-Translate
Amazon-Virtual-Private-Cloud
Amazon-WorkDocs
Amazon-WorkMail
Elastic-Load-Balancing
Infrequent-Access-Storage-Class
Reserved-Instance-Reporting
Savings-Plans
Standard-Storage-Class
TorchServe
VMware-Cloud-on-AWS
Masayoshi Tohna@おれさまラボMasayoshi Tohna@おれさまラボ

アーキテクチャとリソースでフォルダが別れていることに気がついた。
アーキテクチャはさっきのでOKなので、リソースをなんとかする。

$ find AWS-Architecture-Resource-Icons_20200911/ -type f -name "*.svg" -exec cp {} ./res \;
$ for i in $(ls | grep _Dark); do rm -f $i; done
このスクラップは2020/11/28にクローズされました