kiyo_hikoのブログ

メモ+日記?

ツイートに名前を付けて保存するために「ツイートID@ユーザーID」の文字列を一発で作る

標題の事がやりたくてPowerShellのミクロなアプリ作って実現したので以下にまとめます。
この記事の流れは次のようになります。(目次って貼れるのかな)

  • 背景・概要
  • 導入手順
  • 使い方
  • 注意点
  • 改良したいこと

背景・概要

ツイートをブラウザーから「HTML、完全」で保存しようとしたが、保存しようとすると以下のような超絶長いファイル名が表示されて凹んでます。

名無しさんはTwitterを使っています 「ころたんイェイ~ #咲-Saki- #天江衣 https___t.co_xxxx https___t.co_xxxx」 _ Twitter

画像とか単発のファイルなら後からリネームすればよいのだけども、HTML、完全はフォルダーまでくっついてくるので取り回しが悪すぎる。
ここでツイートのURLを見ると、以下のようになっています。

http://twitter.com/ユーザーID/status/ツイートID

ということは、記事タイトルのような ツイートID@ユーザーID の文字列を作って改めてWebページ、完全で保存すれば ファイル名すっきりだし、絶対に一意であるし、URLも復元できるしハッピーなんじゃなかろうかと思いました。

そのような文字列をクリップボードに用意する、小さなPowerShellアプリを作ります。
作成後は、以下の手順でクリップボードに文字列が貼り付けられます。

  • ブラウザーでツイートを開く → URLをコピーする → タスクバーのアプリをクリック

ブラウザー拡張機能で作った方がより楽できるかもですが、勉強めんどくさいので…。

導入手順

大雑把には、以下の3ステップで進めます。

  1. 文字列変換を担うPowerShellスクリプトを用意する。
  2. 実行するためのショートカットを作成する。
  3. タスクバーにピン留めする。

では以下、詳細。

PowerShellスクリプトを用意する

書いたものを置いときます。
コピペしてメモ帳なり、PowerShell ISEなり使って、とにかくテキストで拡張子を「.ps1」にして保存すればOKです。
ファイル名は、この記事の手順としては以下にしました。

twitter-make-id.ps1

間違って消さないように、デスクトップやドキュメントではなく、 MyPowerShell とか何でも、まあそれなりのフォルダーを切って保存した方が良いです。

# Twitter URLから、名前を付けて保存用に「ツイートID @ ユーザーID」形式の文字列を作る
# http://*.twitter.com/ユーザーID/status/ツイートID の形式に対応
# 使い方
# 1.保存したいTweetのURLをブラウザーからコピー
# 2.このスクリプトを実行
# 3.文字列はクリップボードに上書きされるので、好きな用途に使う
# 作成:2021. 5. 4

$orig = Get-Clipboard
$orig -replace "^`r?`n", ""
$orig -replace "`r?`n$", ""
$orig -match "/([^/]+)/status/([^/]+)"
$matches[2] + "@" + $matches[1] | Clip

このスクリプト内の個別のコマンドの操作については以下のサイトを参考にさせてもらいました。

実行するためのショートカットを作成する

前手順で作ったps1スクリプトと同じフォルダーにショートカットを作ります。
(やり方:ps1スクリプトを右クリック→ ショートカットの作成(S)

作ったら今度はショートカットの方を右クリックして プロパティ(R) を表示し、 リンク先(T)実行時の大きさ(R) を編集します。
それ以外はデフォルトでOKです。

  • リンク先:ps1のフルパスが表示されていると思いますが、その文字列の前に、次の文字列を貼り付ける
    • powershell -ExecutionPolicy RemoteSigned -File
    • ※最後の -File の後とps1フルパスの間に半角スペースを置いてください。
  • 実行時の大きさ:必須ではないが、そのままだと実行するたびに一瞬コンソールウィンドウが表示されてウザいので、最小化状態で実行するようにします。

画像の2要素。リンク先、ps1が見えてて貼り付ける文字列が見えてないですが…。

f:id:kiyo_hiko:20210504070803j:plain
自作twitter-make-idスクリプトのショートカット

以下のサイトを参考にさせてもらいました。

qiita.com

タスクバーにピン留めする

前手順で作ったショートカットを右クリックし、 タスクバーにピン留めする(K) を選ぶと、タスクバーにアイコンが貼られます。

f:id:kiyo_hiko:20210504071134j:plain
タスクバーにPowerShellショートカットをピン留め

実行時はこれをクリックします。
導入手順は以上です。

使い方

  1. ブラウザーで表示しているツイートのURLをクリップボードにコピーします。
    • Alt-DCtrl-C とかが早いかな?
  2. タスクバーのtwitter-make-idのアイコンをクリックします。
  3. クリップボードツイートID@ユーザーID 文字列がコピーされるので、保存ファイル名にペーストするなど、自由に使います。

注意点

クリップボードを操作するので、ぶっ壊れては困るデータを保持しているときにクリックしないよう気を付けてください。
想定外でクリックしても大した害はないと思いますが、利用は自己責任で。

改良したいこと

アイコンがPowerShellのデフォルトになってしまうので、わかりやすいアイコンがあった方がいいかも知れないです。
その場合、タスクバーからいったん外してから、ショートカットのプロパティでアイコンを変更し、改めてタスクバーにピン留めしてください。

Perl: Net::FTP使ったコードをデバッグしたくて引数全部printするラッパ書いた

PerlのNet::FTPモジュールをラップして、メソッドを呼ぶごとに引数をプリントアウトするクラス書きました。

  • デバッグによいかも。
  • 別にFTPに限る必要はなくて、少し直せば他でもバンバン使えるはず。

あとAUTOLOADを少し勉強しました。

コード

{ package FtpDebug;
    use Net::FTP qw//;
    sub new {
        my $this = shift;
        bless { me => Net::FTP->new(@_) }, $this;
    }
    sub AUTOLOAD {
        my $this = shift;
        our $AUTOLOAD;
        my $method = (split /::/, $AUTOLOAD)[-1];
        # 次の行はお好みでファイルに出すとかカスタマイズ
        print "Calling method $method (", join(', ', @_) . ")\n";
        $this->{me}->$method(@_);
    }
    sub DESTROY {}
}

使い方

  1. クラスを読める位置にコピペするか、useできる場所にpm切ります。
  2. Net::FTPを使ったコードでnew Net::FTPまたはNet::FTP->newしているところをFtpDebug->newで置き換えます。
  3. 置き換えたコードでFTPのメソッドを呼ぶたびに引数がプリントアウトされます。

AUTOLOAD要点

  • Perlで未定義のメソッド呼ぶとこれが呼ばれるらしい?
  • sub AUTOLOADで書いて中にグローバル変数our $AUTOLOAD書くとFQNでメソッド名が得られる。
    • 上記コードでは、FQNそのまま呼ぶとDeep Recursionになってよくないのでsplitして最終要素取っている。
  • そのままではDESTROYというメソッドもこれに捕まってしまうので、空メソッドとして定義する必要がある。
    • なおDESTROYはPerlシステムの処理なので通常深入りしない
      • (参照カウンタが0になってオブジェクトが消滅するときに自動で呼ばれる処理)

なぜ作ったか、あるいはタダの愚痴

クソ長いメソッド書いたPerlで、$workFileだのftpDataDirだの妙に抽象的な変数名使ってるわ、 逆に処理手順はファイルをカウントアップとかやたら具体的で全体で何をするかという抽象が書かれてない。 そんなクソコードとの出会いがあり、怒りに震えてました。

  • 変数名は具体的に、処理は抽象的なのが俺が思うよいコードです。これでは俺の理想と真逆です。

で突然「あれ?コードがわかんなきゃとにかく全部プリントアウトすりゃいいんじゃね?」という発想が浮かんで作った。
クソコードとの出会いは案外多いから会っても泣かないために、これからもクソコードを力で殴るコード書きたいです。

複雑なフォルダー階層から異なるファイルだけを見つける

WinMergeとかでやるのもしんどくなるぐらい大量のフォルダー階層AとBを比較したい。
かつ、AとB全体で異なるファイルが数個とか数十個とか、大した数無い。
…といった場合に比較が楽になりそうな方法を考えた。

次の方針でAとBからテキストを吐き出して、テキスト比較すればできるはず。

  • ファイルの一覧をすべて引いてくる(これはgci Recurseでよさげ)
  • 取得したファイル名の横にハッシュ値を表示する(Get-FileHashくっつける)
$result = "C:/AforDiff.txt"; # これは例。BならBforDiff.txtなどやっぱりそれっぽい名前に
NullFile $result; # 前のブログエントリー参照

cd A # B分吐くならcd B
foreach ($f in gci . -File -Recurse) {
  $relPath = rvpa -Relative $f.FullName;
  $relPath + "`t" + (Get-FileHash $f.FullName).Hash >> $result;
}

これをAとBでやり、吐かれたテキストを比較します。
変更や追加/削除があればハッシュ部が違ったり、行の増分/減分が発生し、わかる。

PowerShellで空のファイルを作る(bashの":>filename"相当)

作業中のフォルダーに、指定したファイル名でとにかく空のファイルがほしい場合。

  • ファイルが無いときはNew-Item (エイリアス:ni)で空のファイル作ればOKなはず。
  • ファイルが有るときはClear-Contents(エイリアス:clc)でファイルの中身を空にすればOKなはず。

くっつけて関数にしておく。

function NullFile ($f) {
  if (Test-Path $f) {
    clc $f;
  } else {
    ni -ItemType file $f;
}

こんな感じでいけます。

> NullFile C:/test.txt

Perl:配列を奇数番目要素と偶数番目要素に分ける

あと一歩シンプルな書き方が思いつかなくて、とりあえずメモっときます。
割とどうでもいい話です。

やりたいこと

@data = qw/able baker charlie dog easy fox/;
# これを次の形にする
@even_index_data = qw/able charlie easy/;
@odd__index_data = qw/baker dog fox/;

逆zip的な事がやりたくて。

答え

@even_index_data = @data[grep { $_ % 2 == 0 } 0..$#data];
@odd__index_data = @data[grep { $_ % 2 == 1 } 0..$#data];

Perlではこれが一番シンプルかな。パフォーマンスは考えてません。

世間では

stackoverflow.com - 本質的には多分上と同じ。

www.perlmonks.org - 配列にpushする人とか。

タッチパネルWindows PCで誤ってファイルコピーしたときに取り消す方法

なにを言ってるのかイミフなタイトルなので補足

状況が伝わりにくいかもしれません。こういうことがありました。
そのとき対処した内容のメモです。

  1. タッチパネルPCで外付けドライブに一部のファイルを退避しようと思い立った
  2. ファイルが飛び飛びなのでCtrlを押しながらタップして選択まくった
  3. 100個ぐらい選んだところで手が滑ってスワイプ(ドラッグ)した

→ Ctrlマウスドラッグでファイルコピーしたときと同じ操作になったぞい。

  • 選択の労力はパーになるわ、「なになに - コピー.*」なファイルが大量に作られるわで怒髪天

コピーを無理くり取り消す

  • なんか右クリックでundoできませんでした。何故。

仕方ないので「コピー」の名を持ってるファイルを検索してあぶり出し、全部けします。
しかしWindowsの検索窓は気まぐれなので、よくわからん検索結果を提示してきてあてになりません。

  • Windows2000ぐらいまでの厳密文字列の比較とか正直選ばせてほしい

そこでPowerShellです。「コピー」の名を持つファイルをリストアップして消します。

  • どうせPowerShell使うならと、コピー元のハッシュと照合して安全に消します。

組んだPowerShellスクリプトは下段にあります。

一応注意喚起(免責)

ファイルコピーの取り消し(=ファイル削除)を伴うPC操作の話なので取り扱いは事前テストしたうえで慎重に。
不注意で事故っても私は何の責任も持ちません。

作ったもの(説明はあと)

これを走らせます。
自分はPowerShell ISEで即席で書いたのでそのままISEで走らせてps1に保存してました。

# 対象フォルダーは逐次指定するなりカレントにするなりお好きに
cd C:\Users\kyhk\Downloads

# 重複したやつをリストアップ
$dups = gci -Filter "* - コピー*"

foreach ($dup in $dups) {
    # 元ファイル名はたぶんこんなだろう
    $orig = $dup -replace " - コピー", ""

    # 元ファイル名でファイル見つけた
    if (Test-Path $orig) {
        $hash_orig = Get-FileHash $orig
        $hash_dup = Get-FileHash $dup

        # ハッシュも一致した
        if ($hash_orig.Hash -eq $hash_dup.Hash) {
            $dup
            Write-Host "ハッシュ値が同じ元ファイルを検出しました。コピー側を自動で削除します。"
            del $dup # 消す
        }
    }
}

説明

極めて大雑把に

  • gci:dirコマンドみたいなものです。Filterでワイルドカード付けられます
  • Test-Path:ファイルの存在確認できます
  • Get-FileHash:ファイルからハッシュオブジェクトを作ります。別のハッシュ値と比べるときはHashプロパティ経由で値を取りませう

これで解決しました

超めんどくさかった

プロジェクトのステップ数をカウントする(Windows)

もらったプロジェクトの大きさを大雑把に把握したいときとかに即席で。
コードに出てくるLengthを知ってれば何のことはないですが、備忘録的にメモ。
PowerShellでファイル名と個別のステップ数を表示しつつ全ステップ数出します。

2手順かかります

  1. プロジェクトフォルダーに移動してアドレスバーで「powershell」と打ってEnter

  2. 起動したPowerShellで以下のような感じでgciからgc lengthをループするコマンドを打つ。

PS> $acc = 0; foreach ($f in gci . -Filter "ワイルドカードでファイル名") { Write-Host $f.FullName; $len = (gc $f.FullName).Length; $acc = $acc + $len; $len }; $acc

出力は大体こんな感じになります。

C:/foo/bar/baz/Able.cpp
111
C:/foo/bar/baz/Baker.cpp
222
C:/foo/bar/baz/Charlie.cpp
333
666