- 投稿日:2020-09-21T23:34:03+09:00
Mac が勝手に複製した "hoge 2.txt" 的なファイルを一括削除する
"
hoge 2.txt
"、それは突然現れた僕、Github の private リポジトリに高専時代・大学時代の歴史をバックアップしてるんですけど、そのローカルファイルを iCloud Drive にも保存してたんですね。
今日ファイルの中身が見たくなって雲のマーク「ぽちー」って押して、ローカルに落としたんです。一通り閲覧して満足したので、一応変な diff が無いか確認するために
git status
を叩きました。すると「ズラズラズラーーーーーーー」っと無数にアウトプットが。。。。
しかも全部unstaged files
なんですよ。「怖いなー、怖いなー。何も追加してないのになー。」
って思ってよく見ると、全部「hogehoge 2.pdf
」みたいに、 "2
" っていうのが拡張子の直前に付いてたんです。。。それはなんと、全部 Mac が勝手に複製したファイルだったんです。。。。
対処法
はい。ということで、真面目に対処法をシェアします。
症状としては勝手にファイルが複製されて、全部拡張子の直前に2
が付いている状態です。
例:hoge.txt
の複製ファイルとしてhoge 2.txt
が作られている。今回はこの、 2 とついたファイルだけを削除するワンライナーを紹介します。
結論から言うと、$ find . -maxdepth 10 -type f | grep " 2\.*"| sed -e 's/ /\\ /g' | xargs rmというコマンドを叩けばいいです。
お疲れ様でした。。。。で終わるのもなんか味気ないので、軽く解説します。
解説
ワンライナーをもう一度。
$ find . -maxdepth 10 -type f | grep " 2\.*"| sed -e 's/ /\\ /g' | xargs rmこれをパイプ毎に分けると、
- 深さを指定して列挙:
find . -maxdepth 10 -type f
2.
と付くファイルのみに絞り込み:grep " 2\.*"
- スペースをエスケープ:
sed -e 's/ /\\ /g'
- 削除:
xargs rm
一つづつ解説します。
1. 深さを指定して列挙:
find . -maxdepth 10 -type f
これは特に問題ないかと思います。
今回は下階層全て検索したかったので、-maxdepth 10
を付けて 10 階層下まで見に行くようにしています。
find .
をfind ${path/to/directory}
とすれば好きなディレクトリについて検索できます。2.
2.
と付くファイルのみに絞り込み:grep " 2\.*"
ここも特筆することはないです。
.
はそのままだとエスケープが必要なので、バックスラッシュを付けて\.
としています。
これで${半角スペース}2.
をファイル名に含むものだけ抽出できます。3. スペースをエスケープ:
sed -e 's/ /\\ /g'
これがこのワンライナーの肝だと思います。
何をしているかと言うと、半角スペースにバックスラッシュを付けてエスケープさせています。
理由としては、このまま rm コマンドに渡してしまうと、半角スペースで区切られて別ファイルとして扱われてしまうためです。
例:
hoge 2.txt
というファイルを削除する場合
rm hoge 2.txt
を実行してみるrm
コマンドの気持ちとしてはhoge
と2.txt
というファイルが渡されたと勘違い- 「そんなファイルは無いよ」と言われるか運悪く
hoge
または2.txt
ファイルが意図せず削除されてしまう
なんかスラッシュばっかりで分かりにくいとは思いますが、置換コマンドは
$ sed -e 's/${置換前}/${置換後}/g'でその行に含まれた全ての対象を置換してくれます。
このコマンドとの diff を取れば、
- 置換前テキスト:
${半角スペース}
- 置換後テキスト:
\\${半角スペース}
というように指定していることが分かると思います。
さらに、バックスラッシュが連続しているのは、バックスラッシュそのものをエスケープするためで、実質 1 つのバックスラッシュを付けています。
つまり、半角スペースの前にバックスラッシュを付けているのです。これにより、
rm
コマンドが勘違いせずやってくれるはずです。4. 削除:
xargs rm
なんてことはないですが、最後に絞り込まれ、半角スペースをエスケープしたファイル名が指定され、削除される。
という全体的な流れです。
参考
- 投稿日:2020-09-21T17:29:41+09:00
VSCodeでswiftの環境設定をする方法
はじめに
VSCodeでSwiftを使うための環境設定の方法を丁寧に紹介します。
競技プログラミングでSwiftを使いたい、見慣れたVSCodeで使いたい、Xcodeで標準入力の仕方がよくわからないといった方におすすめです。開発環境
OS: macOS Catalina Version 10.15.6
Visual Studio Code (VSCode): Version 1.49.1手順
- Xcodeのインストール
- VSCodeのインストール
- NodeとNPMのインストール
- SourceKit-LSP Extension for Visual Studio Codeのインストールとビルド
- SourceKit-LSPの設定
1. Xcodeのインストール
Xcodeがすでにインストールされている場合は飛ばして構いません。
Xcodeのインストール方法: https://techacademy.jp/magazine/14092. VSCodeのインストール
VSCodeがすでにインストールされている場合は飛ばして構いません。
VSCodeのインストール方法: https://qiita.com/watamura/items/51c70fbb848e5f956fd63. NodeとNPMのインストール
Homebrewを使ってnodeをインストールします。同時にnpmもインストールされます。
$ brew install nodeインストールされたことを確認するために以下のコマンドを実行してみてください。
$ npm --version 6.14.84. SourceKit-LSP Extension for Visual Studio Codeのインストールとビルド
コマンドラインからSourceKit-LSPをクローンします。
$ git clone https://github.com/apple/sourcekit-lsp.git次にインストールされたフォルダへ移動します。
$ cd sourcekit-lsp/Editors/vscode/拡張パックをビルドします。
$ npm run createDevPackageビルドしたものをインストールします。
$ code --install-extension out/sourcekit-lsp-vscode-dev.vsix5. SourceKit-LSPの設定
VSCodeでSwiftのファイルを実行しようとした際に以下のエラーが出た場合には、VSCodeの設定を変更します。
Couldn't start client SourceKit Language ServerCmd+Shift+Pでコマンドパレットを開いたあとに
基本設定: 設定(JSON)を開く (Preferences: Open Settings (JSON)"
を選択します。
そしたら、すでにあるJSONに以下の文章を追加しましょう。(書き換えではなく追加です。)"sourcekit-lsp.serverPath": "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/sourcekit-lsp"6. 確認
hello_world.swiftというファイルを作成しましょう。
作成したファイルに以下のコードを記入してみます。hello_world.swiftprint("Hello, world!")ターミナルで以下のように書くと実行することができます。
swift hello_world.swiftまたは、Control+Option+nでも実行することができます。
ちなみに実行結果はHello, world!です。
7. 型推論
今のままでは型推論がされない(と思う)ので、以下のように実行してください。ファイルをまず開きます。左下に
(HDの名前) > ユーザ > (あなたのユーザ名) > ...と表示されていると思います。その中から、
(あなたのユーザ名)
をクリックします。以下順番にファイルを開いていきます。
sourcekit-lsp
を開きます。
Editors
を開きます。
vscode
を開きます。
out
を開きます。
sourcekit-lsp-vscode-dev.vsix
というファイルが有ることを確認してください。
確認できたらこのページを開きっぱなしにしておきます。
VSCodeの拡張機能タブから右上の・・・
を選択し、VSIXからのインストール
をクリックします。
すると、インストールするファイルを選ぶ画面が表示されるので先程開いたsourcekit-lsp-vscode-dev.vsix
をドラッグ&ドロップします。
最後に、開いたファイルをインストールしましょう。
これで型推論ができるようになりました!8.追記(C言語やC++のコンパイルにsourcekit-lspを用いたくない人へ)
lsp-sourcekitのページには以下のように、Swiftだけでなく、C、C++、Objective-Cにも対応していると書かれています。
lsp-sourcekit is a client for SourceKit-lsp, a Swift/C/C++/Objective-C language server created by Apple.
そのため、今までC言語やC++で使っていたコンパイラでは動くのにlsp-sourcekitでは動かないということが発生する可能性があります。そんなときは、Swiftを使うファイルをC言語、C++を使うファイルとは分けた上で、Swiftを使うワークスペースだけでlsp-sourcekitの拡張機能を有効にしましょう。
① SourceKit-LSPを無効にする② 再読み込みを行う
③ 拡張機能→SourceKit-LSP→有効にする→有効にする(ワークスペース)
9. 参考文献
https://nshipster.com/vscode/
https://medium.com/swlh/ios-development-on-vscode-27be37293fe1
https://scior.hatenablog.com/entry/2019/10/27/21582710. 終わりに
私は、まだまだ初心者なのですが参考文献に上げた3つのページを参考にVSCodeに導入してみたらうまく行ったので、共有させていただきました。
間違っているところがあったらご指摘ください。また、詳しい人がいたらぜひ記事を書いてください。私は、日本語でまとまっている記事を見つけ出すことができず、苦労しました...
- 投稿日:2020-09-21T17:09:26+09:00
Catalinaにアップデートしたらgitコマンドでエラー。xcrun: error: invalid active developer path…
エラー内容と解決策 その1
MacをCatalinaにアップデート後、gitを使おうとしたら下記の様なエラーが出ました。
xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools), missing xcrun at: /Library/Developer/CommandLineTools/usr/bin/xcrun
Command Line Toolsというものがなく怒られているらしいです。
調べたところ以下でインストール出来るとのことなので実行。$ xcode-select --installしかし……
またエラー
多くは前記の工程で解決するようですが、私の場合はポップアップに以下の様なエラーが出ました。
このソフトウェアは、現在ソフトウェア・アップデート・サーバから入手できないため、インストールできません。
解決策 その2
これに対する対策として、以下のページから手動でCommand Line Toolsをインストールすることで解決しました。
More Downloads for Apple Developers
appleIDでログイン後、
Command Line Tools
で検索をかけ、自分のXcodeのバージョンとあったものをダウンロードして、解凍後にインストールしてすればOKです。以上!!
- 投稿日:2020-09-21T13:10:52+09:00
【入門】iOS アプリ開発 #9【ゲームの状態遷移とシーケンス動作】
はじめに
今回はゲームとしてプレイできるように、ゲームの開始からゲームオーバーなどの状態遷移や各シーケンスの動作を作成する。以下が完成イメージ。ソースコードは GitHub に公開しているので参照してほしい。
状態遷移に関する仕様書
スタートモードとして、ゲームを開始する時のシーケンスが詳細に定義されている。
プレイモードはゲームプレイ中の状態で、プレイヤーがミスするとそのままのエサの状態からスタートし、残りのパックマンがいなくなるとゲーム・オーバーとなる。
またプレイフィールドのエサを全て食べるとラウンド・クリアとなる。
状態遷移とシーケンス動作の設計
ゲームプレイ中での必要なシーケンスを考慮して、下記の状態遷移図を作成した。
スタートモードは Start, Ready, Go の3つの状態に分けてシーケンスを作成する。
プレイモードは、主に Updating, ReturnToUpdating の2つの状態からなり、
プレイヤーがミスした場合は、PlayerMiss→PlayerDisappeared→PlayerRestartの状態/シーケンスを実行し、残りのパックマンがいれば Ready状態へ、なくなれば GameOver状態となる。またプレイフィールドのエサを全て食べると、RoundClear→PrepareFlashMaze→FlashMazeの状態/シーケンスを実行しラウンドクリアとなり、Ready状態へ移って次のラウンドが開始される。
状態遷移のソースコード
CgSceneMaze の handleSequenceメソッドに、状態遷移とシーケンス処理を実装していく。
/// Maze scene class for play mode /// This class has some methods to draw a maze and starting messages. class CgSceneMaze: CgSceneFrame, ActorDeligate { var player: CgPlayer! var blinky: CgGhostBlinky! var pinky: CgGhostPinky! var inky: CgGhostInky! var clyde: CgGhostClyde! var ptsManager: CgScorePtsManager! var specialTarget: CgSpecialTarget! var ghosts = CgGhostManager() var counter_judgeGhostsWavyChase: Int = 0 convenience init(object: CgSceneFrame) { self.init(binding: object, context: object.context, sprite: object.sprite, background: object.background, sound: object.sound) player = CgPlayer(binding: self, deligateActor: self) blinky = CgGhostBlinky(binding: self, deligateActor: self) pinky = CgGhostPinky(binding: self, deligateActor: self) inky = CgGhostInky(binding: self, deligateActor: self) clyde = CgGhostClyde(binding: self, deligateActor: self) ptsManager = CgScorePtsManager(binding: self, deligateActor: self) specialTarget = CgSpecialTarget(binding: self, deligateActor: self) ghosts.append(blinky) ghosts.append(pinky) ghosts.append(inky) ghosts.append(clyde) } /// States of game model enum EnGameModelState: Int { case Init = 0 case Start, Ready, Go, Updating, ReturnToUpdating, RoundClear, PrepareFlashMaze, FlashMaze, PlayerMiss, PlayerDisappeared, PlayerRestart, GameOver } /// Handle sequence /// To override in a derived class. /// - Parameter sequence: Sequence number /// - Returns: If true, continue the sequence, if not, end the sequence. override func handleSequence(sequence: Int) -> Bool { guard let state: EnGameModelState = EnGameModelState(rawValue: sequence) else { return false } switch state { case .Init: sequenceInit() case .Start: sequenceStart() case .Ready: sequenceReady() case .Go: sequenceGo() case .Updating: sequenceUpdating() case .ReturnToUpdating: sequenceReturnToUpdating() case .RoundClear: sequenceRoundClear() case .PrepareFlashMaze: sequencePrepareFlashMaze() case .FlashMaze: sequenceFlashMaze() case .PlayerMiss: sequencePlayerMiss() case .PlayerDisappeared: seauencePlayerDisappeared() case .PlayerRestart: sequencePlayerRestart() default: // Stop and exit running sequence. return false } // Continue running sequence. return true } // ============================================================ // Execute sequence in each state. // ============================================================ func sequenceInit() { drawBackground() goToNextSequence() } func sequenceStart() { context.resetGame() context.resetRound() context.numberOfFeeds = drawMazeWithSettingValuesAndAttributes() printBlinking1Up() printPlayers(appearance: false) printStateMessage(.PlayerOneReady) sound.enableOutput(true) sound.playSE(.Beginning) goToNextSequence(.Ready, after: 2240) } func sequenceReady() { printStateMessage(.ClearPlayerOne) printPlayers(appearance: true) player.reset() ghosts.reset() specialTarget.reset() ptsManager.reset() goToNextSequence(.Go, after: 1880) } func sequenceGo() { printStateMessage(.ClearReady) drawPowerFeed(state: .Blinking) player.start() ghosts.start() // Reset counter for wavy attack of ghosts counter_judgeGhostsWavyChase = 0 goToNextSequence() } // 〜 以下、省略 〜 }Start, Ready, Go それぞれの状態に対応するシーケンスを sequenceStart(), sequenceReady(), sequenceGo()メソッドで実装する。
仕様書とメソッドを対応させると以下の通り。
- ”1UP”表示が点滅 → printBlinking1Up()
- 巣の上に”PLAYER ONE”表示、巣の下に”READY”表示 → printStateMessage(.PlayerOneReady)
- 設定パックマンの数だけ”●”がプレイフィールド外の左下に表示される → printPlayers(appearance: false)
- スタートミュージック → sound.playSE(.Beginning)
- ”PLAYER ONE”表示が赤モンスターに代わり → printStateMessage(.ClearPlayerOne), ghosts.reset()
- 設定パックマン(プレイフィールド外の左下)が1つ減る → printPlayers(appearance: true)
- パックマンがスタート位置に表示される → player.reset()
- ”READY!”表示が消えて、プレイモードに移る → printStateMessage(.ClearReady)
プレイモードの仕様書
プレイモードにおいては、モンスターの出現タイミングや波状攻撃、アカモンスターのスパートといった細かい仕様が定義されている。これらによってモンスターは単調な追いかけ動作にならず、またプレイヤーのゲーム進行に合わせて難易度が調整される仕組みになっている。
ラウンドに対して更にモンスター出現タイミングのレベル、波状攻撃のスピードレベル、スパートする残りエサ数が定義されているが、今回は固定のレベル「A」、スパートは「①イ」のみ実装していく。
プレイモードのソースコード
プレイモードの仕様は sequenceUpdating() に動作を実装していく。
func sequenceUpdating() { // Player checks to collide ghost. let collisionResult = ghosts.detectCollision(playerPosition: player.position) switch collisionResult { case .None: // When it's no eat time, ghost goes out one by one. if player.timer_playerNotToEat.isEventFired() { player.timer_playerNotToEat.restart() ghosts.setStateToGoOut(numberOfGhosts: 4, forcedOneGhost: true) } // Appearance Timing of Ghosts ghosts.setStateToGoOut(numberOfGhosts: context.getNumberOfGhostsForAppearace(), forcedOneGhost: false) // Wavy Attack of ghosts // - Do not count timer when Pac-Man has power. if !player.timer_playerWithPower.isCounting() { counter_judgeGhostsWavyChase += SYSTEM_FRAME_TIME } // Select either Scatter or Chase mode. let chaseMode = context.judgeGhostsWavyChase(time: counter_judgeGhostsWavyChase) if chaseMode { pinky.chase(playerPosition: player.position, playerDirection: player.direction.get()) inky.chase(playerPosition: player.position, blinkyPosition: blinky.position) clyde.chase(playerPosition: player.position) } else { pinky.setStateToScatter() inky.setStateToScatter() clyde.setStateToScatter() } // If Blinky becomes spurt or not. let blinkySpurt: Bool = context.judgeBlinkySpurt() && !ghosts.isGhostInNest() blinky.state.setSpurt(blinkySpurt) // Blinky doesn't become scatter mode when he spurts. if blinkySpurt || chaseMode { blinky.chase(playerPosition: player.position) } else { blinky.setStateToScatter() } // For debug ghosts.drawTargetPosition(show: true) case .PlayerEatsGhost: let pts = context.ghostPts ptsManager.start(kind: pts, position: ghosts.collisionPosition, interval: 1000) //ms context.ghostPts = pts.get2times() addScore(pts: pts.getScore()) player.stop() player.clear() specialTarget.enabled = false ghosts.stopWithoutEscaping() sound.playSE(.EatGhost) sound.stopBGM() // REMARKS: To change playBGM(.BgmEscaping) immediately. goToNextSequence(.ReturnToUpdating, after: 1000) case .PlayerMiss: goToNextSequence(.PlayerMiss) } playBGM() }はじめにプレイヤーとモンスターの衝突判定を ghosts.detectCollision(playerPosition: player.position) で行う。
衝突がなければ(.None)、ノーイートタイムによって、ゴーストが巣から出ている処理を行う。4匹の中から必ず1匹は外に出して、ノーイートタイムをリスタートさせる。
次は、食べたエサの数でゴーストを巣から出していく。すでに指定の数が出ていたら何もしない。ミスして新たにスタートするときは、ミスバイパスシーケンスを通す。
CgContextクラスの getNumberOfGhostsForAppearace()メソッドは以下の通り。
func getNumberOfGhostsForAppearace() -> Int { let numberOfGhosts: Int // Miss Bypass Sequence if playerMiss { // Level A if numberOfFeedsEatedByMiss < 7 { numberOfGhosts = 1 } else if numberOfFeedsEatedByMiss < 17 { numberOfGhosts = 2 } else if numberOfFeedsEatedByMiss < 32 { numberOfGhosts = 3 } else { playerMiss = false numberOfGhosts = getNumberOfGhostsForAppearace() } } else { // Level A if numberOfFeedsEated < 30 { numberOfGhosts = 2 } else if numberOfFeedsEated < 90 { numberOfGhosts = 3 } else { numberOfGhosts = 4 } } return numberOfGhosts }sequenceUpdating() メソッドの処理フローに戻る。
次の波状攻撃は、パターンスタートからカウントしている時間 counter_judgeGhostsWavyChase によって Scatter と Chase を切り替える。
ただしプレイヤーが逆転している時は、カウントをストップする。CgContextクラスの judgeGhostsWavyChase()メソッドは以下の通り。
func judgeGhostsWavyChase(time: Int) -> Bool { let mode: Bool // Level A if time < 7000 || (time >= 27000 && time < 34000) || (time >= 54000 && time < 59000) || (time >= 79000 && time < 84000) { mode = false } else { mode = true } return mode }sequenceUpdating() メソッドの最後の処理フローでは、アカモンスター(Blinky)のスパート処理を行う。残りエサ数に達した時かつ全モンスターが巣から出ている時(!ghosts.isGhostInNest())にスパートする。
残りは衝突判定で、パックマンがモンスターを噛み付いた時(.PlayerEatsGhost)
、逆に捕まった時(.PlayerMiss)の処理をそれぞれ実装する。まとめ
ようやくゲームとしてプレイができるようになってきた。現在のソースコードは約5000行。
次はラウンドによって変化する難易度レベルやスピードレベルの詳細を作り込んでいく。
- 投稿日:2020-09-21T04:25:00+09:00
docker for macでThe data couldn’t be read because it is missingのエラーが出る場合
mac 10.15.6
、docker 2.3.0.5
以下のエラーが出ます。
Failed to analyse
The data couldn’t be read because it is missing
以下のページから
docker 2.3.0.4
にダウングレードすると起動できました。https://docs.docker.com/docker-for-mac/release-notes/#docker-desktop-community-2304
- 投稿日:2020-09-21T02:05:55+09:00
MacのTimeMachineのバックアップ先としてUbuntu 20.04のnetatalkを使う
背景
手元のMacを修理に出す前にバックアップを取る必要があった。
ディスクに余裕があるLinuxマシン(物理マシン)があったので、それを使うことにした。ググって出てくる情報が古くて今のバージョンに当てはまらないものだったので、情報を残しておく。
注意点
とりあえず動いたというレベルなので、設定が適当だったりします。
環境
- MacBookPro(macOS Catalina)
- Ubuntu 20.4
- netatalk 3.1.12
手順
Ubuntu側
netatalkインストール
$ sudo apt install netatalknetatalk設定
cd /etc/netatalk/ # 念のためバックアップを取る cp afp.conf afp.conf.bk vim afp.confafp.conf; ; Netatalk 3.x configuration file ; [Global] ; Global server settings ; [Homes] ; basedir regex = /xxxx ; [My AFP Volume] ; path = /path/to/volume [My Time Machine Volume] path = /var/timemachine_bk time machine = yes vol size limit= 256000pathはバックアップファイルが保存される場所を指定する。
vol size limitは、自分の環境に合わせて設定する。自分の場合は、ディスクが256GBなので、それっぽい値にした。
netatalkサービス再起動
必要かわからないけどやっておく。
sudo systemctl restart netatalk.service保存先ディレクトリの準備
自分の環境に合わせて設定する。
mkdir /var/timemachine_bk chown aki:aki /var/timemachine_bkMac側
設定で、TimeMachineを選択する。
自動的にUbuntuに作成したボリュームが認識されているはず。
あとはTimeMachieの設定なので割愛。