AppleサポートAppおよびYouTubeチャンネルのお知らせ

* iOS向けAppleサポートAppのバージョン5.6.1が公開されました。

YouTube - Apple Japanチャンネルで有用なヒントや使い方を配信中です。

コミュニティでの投票方法と通知設定

コミュニティでの投票方法や通知の設定方法に関する記事を公開しました。

Apple Account(Apple ID)の不正利用を確認する方法

Apple Accountに関するよくある質問についてはこちらのページを、また不正利用を確認する方法についてはこちらのドキュメントをご参照ください。

しばらく返答が寄せられていないようです。 再度ディスカッションを開始するには、新たに質問してください。

".com"で終わる文字列をテキストクリッピングファイルとして保存する方法が知りたい.

【OS】macOS Mojave

テキストエディタで選択中の文字列を Finder へドラッグしてテキストクリッピングファイルの生成を試みます.

  • textSanpure.com
  • textSanpure.info

前者はテキストクリッピングファイルでなくウェブロケーションファイル(*.webloc)が生成されます.【添付画像2】

後者は正しくテキストクリッピングファイル(*.textClipping)が生成されます.【添付画像3】


前者の場合、システムが「この文字列はURLを指すものだ」と誤認してweblocファイルを生成したと考えます.テストはしていませんが .eu や .jp などでもそうなるのかもしれません.しかしながら www.test.info などというサイトもあるようなので一概にドメイン名だけで判断しているわけではなさそうですが.


はじめは拡張子名を変更すれば問題は解決するのでは? と思いそうしたのですが、内容自体も改変されているようで、上記の例で言えば テキスト textSanpure.com の前に http:// という文字列が差し込まれ、完全にURL文字列となっているようです.生成されたクリッピングファイルの内容はもはや変更できないので(再作成するしかない、はず)、拡張子名を変えれば済むという問題ではないようです.


仮に".com"で終わる文字列をテキストクリッピングファイルとして保存する方法がないのであれば、しかたなくテキストファイルに文字を置き、それを毎度コピーしてどこかへ貼り付けることで対処はできますが、正直面倒です.今回の挙動が単なるバグであれば古いOSを使っているので諦めてその代替案で運用するしかないですが、対処法があれば知りたいです.よろしくお願いします.



【添付画像】生成されたファイル(カラム表示状態をキャプチャした)とテキストエディタの内容


【添付画像2】textSanpure.com - QuickLook


【添付画像3】textSanpure.info - QuickLook

Mac mini, macOS 10.14

投稿日 2024/07/23 19:01

返信
返信: 20

2024/07/24 06:33 light289 への返信

High Sierra と Mojave 及び Parallels Desktop 18 上の Lion と Ventura で試したところ、light289 さんと同じ結果となりました。


末尾が ".com" のテキストをドラッグアンドドロップしてテキストクリッピングファイルを作成することはおそらくできないと思います。ちなみに「@」が含まれるテキストは webloc ではなく、mailloc となるようです。


で、テキストクリッピングファイルを調査したところ下図のようになっていて、中身は bplist データ、拡張属性 com.apple.FinderInfo にはタイプとクリエータがセットされていて、さらにリソースフォークにもデータが格納されているようです。



これなら編集できそうです。仮のテキストクリッピングファイルを用意して、plutil コマンドで xml に変換し、vim で開くとこんな感じ。



次のように編集してファイルを保存した後に plutil で元に戻す。



次にリソースフォーク。今時そんなもの不要だろうと思って削除したところクイックルックでテキストが表示されなくなりました...。どうやらデータをリソースフォークから引っ張ってきてるようです。ということでリソースフォークも編集して保存して完成。




これらをスクリプト化すれば多少は楽になるかもしれませんが、それでもかなり面倒なような...。クリッピングファイルは便利だと思いますが、UTF-8、LF な標準テキストで運用してはどうでしょうかね?


2024/07/24 18:07 Hiro__S への返信

選択したテキストからテキストクリッピングファイルを作成する Automator クイックアクションを作ってみました。テキストを選択してワークフローを実行するとデスクトップに「text.textClipping」が生成されます。


クリッピングファイルは UTF-8 の情報さえあれば機能するようなので、UTF-16、レガシーエンコーディング等は「なし」にしました。ファイルの形式は XML。ファイル名は文字数制限とか面倒なので「text.textClipping」固定。要するに超手抜きです。


なお、Rez コマンドを使うので Command Line Tools が必要となります。それと、このワークフローで生成されるファイルは上記のとおり若干データを間引いてあるので何らかの問題が生じる可能性はあります。



JavaScriptを実行

'use strict';

function run(argv) {
    const query = argv[0].toString();

    const fpath = `~/Desktop/text.textClipping`;
    const furl = $.NSURL.fileURLWithPath($(fpath).stringByExpandingTildeInPath);

    const dct = {'UTI-Data':{'public.utf8-plain-text':query}};
    const err = $();

    if (! $(dct).writeToURLError(furl, err)) {
        throw new Error(err.localizedDescription.js);
    }

    return [furl.path.js, query];
}


シェルスクリプトを実行

fpath="${1}"
query="${2}"
rsrc="${fpath}.r"

bstr=$(printf '%s' "${query}" | xxd -p | awk '{ printf $0; }')
data=$(printf "data 'utf8' (256) { $\"%s\" };" "${bstr}")

echo "${data}" > "${rsrc}"
Rez "${rsrc}" -o "${fpath}"
rm "${rsrc}"


動作確認

・Paralles Desktop 18 / Mojave 10.14.6 [要 Command Line Tools]

・Paralles Desktop 18 / Ventura 13.6.7 [要 Command Line Tools]


追記: 選択したテキストを基にファイル名を作るなら JXA より AppleScript の方がやりやすいかもしれません。


2024/08/02 13:50 Hiro__S への返信

シェルスクリプトを実行アクションにおける Unicode 正規化問題と、データサイズが大きい場合にリソースフォークのデータが壊れる問題に対処しました。


それと、ファイルをバイナリ形式で書き出すように変更。内容は Catalina 以降と同じになるようにしました。Mojave でも大丈夫だと思います。


さらにリソースフォークに、utxt、TEXT、drag を追加しました。TEXT リソースについては Catalina 以降とも異なりますがおそらく問題ないと思います。


これで最終版とするつもです。



# 投稿ボタンを誤って押したので一旦取り消した上で再投稿しました。

2024/07/27 16:20 Hiro__S への返信

選択したテキストベースのファイル名になるようにしてみました。なお、ファイルの中身は前のスクリプトと同じです。


JavaScript のコード

'use strict';

function grapheme_clusters(str) {
    const nsstr = $(str);
    const len = nsstr.length;
    let a = [];
    let i = 0;
    while (i < len) {
        const range = nsstr.rangeOfComposedCharacterSequenceAtIndex(i);
        a.push(nsstr.substringWithRange(range).js);
        i += Number(range.length);
    }
    return a;
}

function set_fname(fsrep, ext) {
    const margin = 5;
    let fname = '';
    if ((fsrep + ext).length < 256 - margin) {
        fname = fsrep;
    } else {
        const chrs = grapheme_clusters(fsrep);
        const ellipsis = '…';
        for (let i = 0; i < chrs.length; i++) {
            if ((fname + chrs[i] + ellipsis + ext).length > 255 - margin) {
                break;
            } else {
                fname += chrs[i];
            }
        }
        fname = fname + ellipsis;
    }
    return fname.replace(/[/:]/g, '_');
}

function resolve_conflicts(fpath, dname, fname, ext) {
    const filemanager = $.NSFileManager.defaultManager;
    let n = 2;
    while (filemanager.fileExistsAtPath($(fpath))) {
        fpath = `${dname}/${fname} ${n}${ext}`;
        if (n > 1000) {
            throw new Error(`${fpath}: filename: overflow`);
        }
        n++;
    }
    return fpath;
}

function run(argv) {
    const query = argv[0].toString();
    const fsrep = $(query).fileSystemRepresentation;

    const ext = '.textClipping';
    const fname = set_fname(fsrep, ext);
    const dname = $('~/Desktop').stringByExpandingTildeInPath.js;

    let fpath = $(dname).stringByAppendingPathComponent(fname + ext).js;
    fpath = resolve_conflicts(fpath, dname, fname, ext);

    const furl = $.NSURL.fileURLWithPath($(fpath).stringByExpandingTildeInPath);
    const dct = {'UTI-Data':{'public.utf8-plain-text':query}};
    const err = $();

    if (! $(dct).writeToURLError(furl, err)) {
        throw new Error(err.localizedDescription.js);
    }

    return [furl.path.js, query];
}


2024/07/27 19:25 Hiro__S への返信

連投すみません。ファイル名にタブ文字などが含まれないようにしました。


JavaScript


'use strict';

function grapheme_clusters(str) {
    const nsstr = $(str);
    const len = nsstr.length;
    let a = [];
    let i = 0;
    while (i < len) {
        const range = nsstr.rangeOfComposedCharacterSequenceAtIndex(i);
        a.push(nsstr.substringWithRange(range).js);
        i += Number(range.length);
    }
    return a;
}

function set_fname(str, ext) {
    const fsrep = $(str).fileSystemRepresentation.replace(/[/:]/g, '_').replace(/\s+/g, ' ').trim();
    const margin = 5;
    let fname = '';
    if ((fsrep + ext).length < 256 - margin) {
        fname = fsrep;
    } else {
        const chrs = grapheme_clusters(fsrep);
        const ellipsis = '…';
        for (let i = 0; i < chrs.length; i++) {
            if ((fname + chrs[i] + ellipsis + ext).length > 255 - margin) {
                break;
            } else {
                fname += chrs[i];
            }
        }
        fname = fname + ellipsis;
    }
    return fname;
}

function resolve_conflicts(fpath, dname, fname, ext) {
    const filemanager = $.NSFileManager.defaultManager;
    let n = 2;
    while (filemanager.fileExistsAtPath($(fpath))) {
        fpath = `${dname}/${fname} ${n}${ext}`;
        if (n > 1000) {
            throw new Error(`${fpath}: filename: overflow`);
        }
        n++;
    }
    return fpath;
}

function run(argv) {
    if (argv.length < 1) {
        return 'usage: osascript -l JavaScript me.applescript string';
    }

    const query = argv[0].toString();

    const ext = '.textClipping';
    const fname = set_fname(query, ext);
    const dname = $('~/Desktop').stringByExpandingTildeInPath.js;

    let fpath = $(dname).stringByAppendingPathComponent(fname + ext).js;
    fpath = resolve_conflicts(fpath, dname, fname, ext);

    const furl = $.NSURL.fileURLWithPath($(fpath).stringByExpandingTildeInPath);
    const dct = {'UTI-Data':{'public.utf8-plain-text':query}};
    const err = $();

    if (! $(dct).writeToURLError(furl, err)) {
        throw new Error(err.localizedDescription.js);
    }

    return [furl.path.js, query];
}


2024/07/31 15:39 Hiro__S への返信

何度も済みません。ファイル名の先頭が「.」にならないための処理などいくつかの修正を加えました。


JavaScript


'use strict';

function grapheme_clusters(str) {
    const nsstr = $(str);
    const len = nsstr.length;
    let a = [];
    let i = 0;
    while (i < len) {
        const range = nsstr.rangeOfComposedCharacterSequenceAtIndex(i);
        a.push(nsstr.substringWithRange(range).js);
        i += Number(range.length);
    }
    return a;
}

function set_fname(str, ext) {
    const fsrep = $(str).fileSystemRepresentation.replace(/^\./, '_').replace(/[/:]/g, '_').replace(/\s+/g, ' ').trim();
    const margin = 5;
    let fname = '';
    if ((fsrep + ext).length < 256 - margin) {
        fname = fsrep;
    } else {
        const chrs = grapheme_clusters(fsrep);
        const ellipsis = '…';
        for (let i = 0; i < chrs.length; i++) {
            if ((fname + chrs[i] + ellipsis + ext).length > 255 - margin) {
                break;
            } else {
                fname += chrs[i];
            }
        }
        fname = fname + ellipsis;
    }
    return fname;
}

function resolve_conflicts(fpath, dname, fname, ext) {
    const filemanager = $.NSFileManager.defaultManager;
    let n = 2;
    while (filemanager.fileExistsAtPath($(fpath))) {
        fpath = `${dname}/${fname} ${n}${ext}`;
        if (n > 1000) {
            throw new Error(`${fpath}: filename: overflow`);
        }
        n++;
    }
    return fpath;
}

function run(argv) {
    if (argv.length < 1) {
        return 'usage: osascript -l JavaScript me.applescript string';
    }

    const query = argv[0].toString();

    const ext = '.textClipping';
    const fname = set_fname(query, ext);
    const dname = $('~/Desktop').stringByExpandingTildeInPath.js;

    let fpath = $(dname).stringByAppendingPathComponent(fname + ext).js;
    fpath = resolve_conflicts(fpath, dname, fname, ext);

    const furl = $.NSURL.fileURLWithPath(fpath);
    const dct = {'UTI-Data': {'public.utf8-plain-text': query}};
    const err = $();

    if (! $(dct).writeToURLError(furl, err)) {
        throw new Error(err.localizedDescription.js);
    }

    return [furl.path.js, query];
}


2024/07/31 15:49 Hiro__S への返信

連投かつ長文ですみませんが、検証して分かったことを書いておきます。


Lion から Ventura までのテキストクリッピングファイルを調査したところ、データフォークに情報が保存されるようになったのは Sierra 以降ということが分かりました。それより前はリソースフォークのみでデータフォークは空。


また、bplist データ内の "com.apple.traditional-mac-plain-text" キーの値は Sierra から Mojave までと、Catalina 以降で異なることも判明。この違いによりリソースフォークに保存される情報も Catalina 以降若干変わってます。


あと、これらの検証の過程で Automator の「シェルスクリプトを実行」アクションの問題を発見。同アクションではテキストが勝手に Unicode 正規化されるようで、なんと Lion から Ventura すべての環境で同様です。(Apple にはフィードバックしました)


Automator.app

printf '%s' 'プ' | xxd -p #=> e38395e3829a


Terminal.app

printf '%s' 'プ' | xxd -p #=> e38397


今回のワークフローで言うと「アップル」が「アッフ半濁点ル」となります。ただ、影響を受けるのはクイックルックのみなのでギリギリ許容範囲かなと。ただ、ほかのスクリプトでは致命傷となる危険性があるので要注意ですね。


2024/08/02 13:48 Hiro__S への返信

JavaScriptを実行

'use strict';

function grapheme_clusters(str) {
    const nsstr = $(str);
    const len = nsstr.length;
    let a = [];
    let i = 0;
    while (i < len) {
        const range = nsstr.rangeOfComposedCharacterSequenceAtIndex(i);
        a.push(nsstr.substringWithRange(range).js);
        i += Number(range.length);
    }
    return a;
}

function set_fname(str, ext) {
    const fsrep = $(str).fileSystemRepresentation.replace(/^\./, '_').replace(/[/:]/g, '_').replace(/\s+/g, ' ').trim();
    const margin = 5;
    let fname = '';
    if ((fsrep + ext).length < 256 - margin) {
        fname = fsrep;
    } else {
        const chrs = grapheme_clusters(fsrep);
        const ellipsis = '…';
        for (let i = 0; i < chrs.length; i++) {
            if ((fname + chrs[i] + ellipsis + ext).length > 255 - margin) {
                break;
            } else {
                fname += chrs[i];
            }
        }
        fname = fname + ellipsis;
    }
    return fname;
}

function resolve_conflicts(fpath, dname, fname, ext) {
    const filemanager = $.NSFileManager.defaultManager;
    let n = 2;
    while (filemanager.fileExistsAtPath($(fpath))) {
        fpath = `${dname}/${fname} ${n}${ext}`;
        if (n > 1000) {
            throw new Error(`${fpath}: filename: overflow`);
        }
        n++;
    }
    return fpath;
}

function base64(string) {
    const data = $(string).dataUsingEncoding($.NSUTF8StringEncoding);
    return data.base64EncodedStringWithOptions(0).js;
}

function run(argv) {
    const query = argv[0].toString();

    const ext = '.textClipping';
    const fname = set_fname(query, ext);
    const dname = $('~/Desktop').stringByExpandingTildeInPath.js;

    let fpath = $(dname).stringByAppendingPathComponent(fname + ext).js;
    fpath = resolve_conflicts(fpath, dname, fname, ext);

    const furl = $.NSURL.fileURLWithPath(fpath);

    const isascii = $(query).canBeConvertedToEncoding($.NSASCIIStringEncoding);
    const enc = isascii ? $.NSASCIIStringEncoding : $.NSData.data;

    const dct = {
        'UTI-Data': {
            'public.utf8-plain-text': query,
            'public.utf16-plain-text': $(query).dataUsingEncoding($.NSUTF16LittleEndianStringEncoding),
            'com.apple.traditional-mac-plain-text': enc
        }
    };

    const err = $();
    const bplist = $.NSPropertyListSerialization.dataWithPropertyListFormatOptionsError(
        $(dct),
        $.NSPropertyListBinaryFormat_v1_0,
        0,
        err
    );

    if (! err.isNil()) {
        throw new Error(`error: ${f}: ${err.localizedDescription.js}`);
    } else {
        const err = $();
        if (! bplist.writeToURLOptionsError(furl, $.NSDataWritingAtomic, err)) {
            throw new Error(`error: ${f}: ${err.localizedDescription.js}`);
        }
    }

    return [furl.path.js, base64(query), isascii.toString()];
}


2024/08/02 13:51 Hiro__S への返信

シェルスクリプトを実行

fpath="${1}"
query=$(printf '%s' "${2}" | base64 -D)
isascii="${3}"

rsrc="${fpath}.r"

utxt_value=$(printf '%s' "${query}" | piconv -f UTF-8 -t UTF-16BE | xxd -c16 -p | awk '{ printf "$\"%s\"\n", $0 }')
utf8_value=$(printf '%s' "${query}" | xxd -c16 -p | awk '{ printf "$\"%s\"\n", $0 }')

utxt_data=$(printf "data 'utxt' (256) {\n%s\n};" "${utxt_value}")
utf8_data=$(printf "data 'utf8' (256) {\n%s\n};" "${utf8_value}")

if [[ "${isascii}" == "true" ]]; then
    head="00000001000000000000000000000003"
    utxt="75747874000001000000000000000000"
    utf8="75746638000001000000000000000000"
    text="54455854000001000000000000000000"

    text_value="${utf8_value}"
    drag_value=$(printf '%s\n%s\n%s\n%s' "${head}" "${utxt}" "${utf8}" "${text}" | awk '{ printf "$\"%s\"\n", $0 }')

    text_data=$(printf "data 'TEXT' (256) {\n%s\n};" "${text_value}")
    drag_data=$(printf "data 'drag' (128) {\n%s\n};" "${drag_value}")

    rsrc_data=$(printf '%s\n%s\n%s\n%s' "${utxt_data}" "${utf8_data}" "${text_data}" "${drag_data}")
else
    head="00000001000000000000000000000002"
    utxt="75747874000001000000000000000000"
    utf8="75746638000001000000000000000000"

    text_value=""
    drag_value=$(printf '%s\n%s\n%s' "${head}" "${utxt}" "${utf8}" | awk '{ printf "$\"%s\"\n", $0 }')

    text_data=""
    drag_data=$(printf "data 'drag' (128) {\n%s\n};" "${drag_value}")

    rsrc_data=$(printf '%s\n%s\n%s' "${utxt_data}" "${utf8_data}" "${drag_data}")
fi

echo "${rsrc_data}" > "${rsrc}"
Rez "${rsrc}" -o "${fpath}"
rm "${rsrc}"

echo "${fpath}"


2024/08/03 01:01 Hiro__S への返信

JavaScript

'use strict';

function grapheme_clusters(str) {
    const nsstr = $(str);
    const len = nsstr.length;
    let a = [];
    let i = 0;
    while (i < len) {
        const range = nsstr.rangeOfComposedCharacterSequenceAtIndex(i);
        a.push(nsstr.substringWithRange(range).js);
        i += Number(range.length);
    }
    return a;
}

function set_fname(str, ext) {
    const fsrep = $(str).fileSystemRepresentation.replace(/^\./, '_').replace(/[/:]/g, '_').replace(/\s+/g, ' ').trim();
    const margin = 5;
    let fname = '';
    if ((fsrep + ext).length < 256 - margin) {
        fname = fsrep;
    } else {
        const chrs = grapheme_clusters(fsrep);
        const ellipsis = '…';
        for (let i = 0; i < chrs.length; i++) {
            if ((fname + chrs[i] + ellipsis + ext).length > 255 - margin) {
                break;
            } else {
                fname += chrs[i];
            }
        }
        fname = fname + ellipsis;
    }
    return fname;
}

function resolve_conflicts(fpath, dname, fname, ext) {
    const filemanager = $.NSFileManager.defaultManager;
    let n = 2;
    while (filemanager.fileExistsAtPath($(fpath))) {
        fpath = `${dname}/${fname} ${n}${ext}`;
        if (n > 1000) {
            throw new Error(`${fpath}: filename: overflow`);
        }
        n++;
    }
    return fpath;
}

function base64(string) {
    const data = $(string).dataUsingEncoding($.NSUTF8StringEncoding);
    return data.base64EncodedStringWithOptions(0).js;
}

function run(argv) {
    const query = argv[0].toString();

    const ext = '.textClipping';
    const fname = set_fname(query, ext);
    const dname = $('~/Desktop').stringByExpandingTildeInPath.js;

    let fpath = $(dname).stringByAppendingPathComponent(fname + ext).js;
    fpath = resolve_conflicts(fpath, dname, fname, ext);

    const furl = $.NSURL.fileURLWithPath(fpath);

    const m = query.match(/^\p{ASCII}+/u, '$0');
    const trad_query = m ? m[0] : '';

    const dct = {
        'UTI-Data': {
            'public.utf8-plain-text': query,
            'public.utf16-plain-text': $(query).dataUsingEncoding($.NSUTF16LittleEndianStringEncoding),
            'com.apple.traditional-mac-plain-text': $(trad_query).dataUsingEncoding($.NSASCIIStringEncoding)
        }
    };

    const err = $();
    const bplist = $.NSPropertyListSerialization.dataWithPropertyListFormatOptionsError(
        $(dct),
        $.NSPropertyListBinaryFormat_v1_0,
        0,
        err
    );

    if (! err.isNil()) {
        throw new Error(`error: ${f}: ${err.localizedDescription.js}`);
    } else {
        const err = $();
        if (! bplist.writeToURLOptionsError(furl, $.NSDataWritingAtomic, err)) {
            throw new Error(`error: ${f}: ${err.localizedDescription.js}`);
        }
    }

    return [furl.path.js, query, trad_query].map(x => base64(x));
}


2024/08/03 01:02 Hiro__S への返信

シェルスクリプト

fpath=$(base64 -D <<< "${1}")
query=$(base64 -D <<< "${2}")
trad_query=$(base64 -D <<< "${3}")

rsrc="${fpath}.r"

utxt_value=$(printf '%s' "${query}" | iconv -f UTF-8 -t UTF-16BE | xxd -c16 -p | awk '{ printf "$\"%s\"\n", $0 }')
utf8_value=$(printf '%s' "${query}" | xxd -c16 -p | awk '{ printf "$\"%s\"\n", $0 }')

utxt_data=$(printf "data 'utxt' (256) {\n%s\n};" "${utxt_value}")
utf8_data=$(printf "data 'utf8' (256) {\n%s\n};" "${utf8_value}")

if [[ -n "${trad_query}" ]]; then
    head="00000001000000000000000000000003"
    utxt="75747874000001000000000000000000"
    utf8="75746638000001000000000000000000"
    text="54455854000001000000000000000000"

    text_value=$(printf '%s' "${trad_query}" | xxd -c16 -p | awk '{ printf "$\"%s\"\n", $0 }')
    drag_value=$(printf '%s\n%s\n%s\n%s' "${head}" "${utxt}" "${utf8}" "${text}" | awk '{ printf "$\"%s\"\n", $0 }')

    text_data=$(printf "data 'TEXT' (256) {\n%s\n};" "${text_value}")
    drag_data=$(printf "data 'drag' (128) {\n%s\n};" "${drag_value}")

    rsrc_data=$(printf '%s\n%s\n%s\n%s' "${utxt_data}" "${utf8_data}" "${text_data}" "${drag_data}")
else
    head="00000001000000000000000000000002"
    utxt="75747874000001000000000000000000"
    utf8="75746638000001000000000000000000"

    text_value=""
    drag_value=$(printf '%s\n%s\n%s' "${head}" "${utxt}" "${utf8}" | awk '{ printf "$\"%s\"\n", $0 }')

    text_data=""
    drag_data=$(printf "data 'drag' (128) {\n%s\n};" "${drag_value}")

    rsrc_data=$(printf '%s\n%s\n%s' "${utxt_data}" "${utf8_data}" "${drag_data}")
fi

echo "${rsrc_data}" > "${rsrc}"
Rez "${rsrc}" -o "${fpath}"
rm "${rsrc}"

echo "${fpath}"


2024/08/19 13:46 Hiro__S への返信

クリッピングテキストとして使用する文字列を入力し、それからクリッピングファイルを生成・保存するAppleScript.かなり簡便なものですが.この方法ではリソースフォークが用意されないため QuickLook で内容の確認はできません.ですが、ファイルを実際にテキスト領域にドラッグ&ドロップさせるとクリッピングテキストが指定された位置に反映されます.macOS Mojave 環境で試したので、それよりバージョンの大きいOSで動くかはわかりません(ただしmacOS 10.13以降で用意されたメソッドを使用しているため、おそらくSierraとかでは動きません).


use AppleScript version "2.4"
use framework "Cocoa"
use scripting additions

-- 空文字列が入力されていた場合は何もせず終了する.	
-- ピリオドで始まる文字列が入力されていてもとりあえず進行する.
-- 入力された文字列はテキストクリッピングの内容として、加えて、主ファイル名としても使用される.

on run
	me's main()
end run

on main()
	
	-- テキストクリッピングとして使用する文字列を入力させる.
	set returnValue to display dialog "テキストクリッピングとして使用する文字列を入力してください.それはそのまま、主ファイル名としても使用されます." default answer ""
	set clippingText to returnValue's text returned as string
	
	# いくつかの例外処理(簡易)
	
	-- 空文字列が入力されていた場合は何もせず終了する.	
	if clippingText is "" then return
	
	-- ピリオドで始まる場合の処理.および付帯文字列の用意.
	if clippingText starts with "." then
		display dialog "ピリオドで始まる文字列が入力されたため隠しファイルとして生成します.Finderで隠しファイルを表示しない設定にしている場合、表面上は作成されていないように見えるため注意してください."
		set additionalText to "(隠しファイル)"
	else
		set additionalText to ""
	end if
	
	-- 保存する「ファイル名」を組み立てる.	
	-- 入力した文字列(=同上)をそのまま主ファイル名として扱う.拡張子名は textClipping で固定.
	set filename to clippingText & ".textClipping"
	
	-- 保存場所を指定させる.
	set promptString to "以下の場所に " & (filename as string) & " として保存します" & additionalText & "."
	set baseLocationPath to choose folder with prompt promptString without multiple selections allowed
	set baseLocationPath to (POSIX path of baseLocationPath)
	
	-- 最終的な保存場所(パス+ファイル名)を決定する.
	set savePath to current application's NSString's stringWithString:baseLocationPath
	set savePath to savePath's stringByAppendingPathComponent:filename
	set saveURL to current application's NSURL's fileURLWithPath:savePath
	
	-- XMLファイルとして保存する.
	me's writeToURLAsXML(saveURL, clippingText)
	
end main


#	指定された文字列(クリッピングテキスト)を連想配列へ成形した後、指定された場所(URL)へXMLファイルとして書き出す.
on writeToURLAsXML(anURL, clippingText)
	
	-- 内包する連想配列の実データを作成する.
	-- 以下のXMLファイルから各項目を引用し、作成している.
	(*
		<?xml version="1.0" encoding="UTF-8"?>
		<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
		<plist version="1.0">
		<dict>
			<key>UTI-Data</key>
			<dict>
				<key>com.apple.traditional-mac-plain-text</key>
				<data>
				a2ltb2NoaS5pbmZv
				</data>
				<key>public.utf16-plain-text</key>
				<data>
				awBpAG0AbwBjAGgAaQAuAGkAbgBmAG8A
				</data>
				<key>public.utf8-plain-text</key>
				<string>テキストクリッピングの内容</string>
			</dict>
		</dict>
		</plist>	
	*)
	
	#	<key>com.apple.traditional-mac-plain-text</key>
	#	<data>
	#	a2ltb2NoaS5pbmZv
	#	</data>
	set nString to current application's NSString's stringWithString:"a2ltb2NoaS5pbmZv"
	set appleTraditionalData to nString's dataUsingEncoding:(current application's NSUTF8StringEncoding)
	set appleTraditionalKey to "com.apple.traditional-mac-plain-text"
	
	#	<key>public.utf16-plain-text</key>
	#	<data>
	#	awBpAG0AbwBjAGgAaQAuAGkAbgBmAG8A
	#	</data>
	set nString to current application's NSString's stringWithString:"awBpAG0AbwBjAGgAaQAuAGkAbgBmAG8A"
	set publicutf16Data to nString's dataUsingEncoding:(current application's NSUTF8StringEncoding)
	set publicutf16Key to "public.utf16-plain-text"
	
	#	<key>public.utf8-plain-text</key>
	#	<string>指定された文字列</string>
	set publicutf8String to clippingText
	set publicutf8Key to "public.utf8-plain-text"
	
	-- 内包する連想配列を作成.
	set subDict to current application's NSDictionary's dictionaryWithObjects:{appleTraditionalData, publicutf16Data, publicutf8String} forKeys:{appleTraditionalKey, publicutf16Key, publicutf8Key}
	
	-- 最終的な連想配列を生成.
	
	#	<key>UTI-Data</key>
	#	<dict>内包する連想配列</dict>
	set mainDict to current application's NSDictionary's dictionaryWithObject:subDict forKey:"UTI-Data"
	
	-- XMLファイルとして書き出す.macOS 10.13以降 でのみ使用可.
	mainDict's writeToURL:anURL |error|:(missing value)
	
end writeToURLAsXML


正直、これで何故動くのかというのはよくわかりません.特にNSDataで用意されているキー"com.apple.traditional-mac-plain-text"の内容は「a2ltb2NoaS5pbmZv」となっていますが、この値が何を元に生成されたものなのかわかりません.ただし結果として「ちゃんと動いているように見える」ので、自身が利用するという前提であれば、問題ないかなぁと.


今回はテキスト形式のXMLを無理矢理成形して新しいXMLファイルを作成する手順を踏みませんでしたが(<string>テキストクリッピングの内容</string>の前後で文字列を分割するとかいろいろ)、macOS 10.13以降で用意されたメソッドの代わりにシェルスクリプトの plutil を使用するなど、稼働するOSに依存しないコードは書けそうですが..


しばらく自分で使ってみて、何か問題があったらまた書きます.なければ、ひとまずは解決、ということになります.

2024/08/19 22:32 light289 への返信

特にNSDataで用意されているキー"com.apple.traditional-mac-plain-text"の内容は「a2ltb2NoaS5pbmZv」となっていますが、この値が何を元に生成されたものなのかわかりません.


すみません。この辺りをもうちょっと丁寧に説明すべきでした。


Sierra から Mojave まではちょっと複雑で、対象のテキストが ASCII の場合は ASCII、そうでない場合は OS の言語設定に応じた文字コード (MacRoman や MacJapanese など)、それもダメな場合は UTF-16LE でエンコード、、、というルールになっているものと思われます。多分。


Catalina 以降このルールは変更されて、先頭からの連続した ASCII 文字列となったようです。(例: españa => espa、アップル => 空データ)


何にしてもこのデータは XML ファイルでは base64 でエンコードされた文字列となります。Terminal からこんな感じで確認できます。


例1: テキストが「kimochi.info」の場合


コマンド

printf '%s' 'kimochi.info' | base64


結果

a2ltb2NoaS5pbmZv


コマンド

printf '%s' 'a2ltb2NoaS5pbmZv' | base64 -D


結果

kimochi.info


例2: OS の言語設定が日本語で、テキストが「アップル」の場合


コマンド

printf '%s' 'アップル' | piconv -f utf8 -t MacJapanese | base64


結果

g0GDYoN2g4s=


コマンド

printf '%s' 'g0GDYoN2g4s=' | base64 -D | piconv -f MacJapanese -t utf8


結果

アップル


public.utf16-plain-text についてはもうちょっと簡単で、テキストを UTF-16LE でエンコードしたデータとなるようです。


コマンド

printf '%s' 'kimochi.info' | piconv -f utf8 -t utf-16le | base64


結果

awBpAG0AbwBjAGgAaQAuAGkAbgBmAG8A


コマンド

printf '%s' 'awBpAG0AbwBjAGgAaQAuAGkAbgBmAG8A' | base64 -D | piconv -f utf-16le -t utf8


結果

kimochi.info


ただし、ASOC で処理する際は base64 でエンコードする必要はなく、NSData のままで構いません。


2024/08/19 22:33 light289 への返信

これらを踏まえ、お示しのコードは次のように変更すると良いと思います。(変数名とかは好きなように変えてください)


#   <key>com.apple.traditional-mac-plain-text</key>
set nString to current application's NSString's stringWithString:clippingText
set e1 to current application's NSASCIIStringEncoding
set e2 to current application's NSString's defaultCStringEncoding()
set e3 to current application's NSUTF16LittleEndianStringEncoding
repeat with e in {e1, e2, e3}
    if (nString's canBeConvertedToEncoding:e) then
        set encoding to contents of e
        exit repeat
    end if
end repeat
set appleTraditionalData to nString's dataUsingEncoding:(encoding)
set appleTraditionalKey to "com.apple.traditional-mac-plain-text"


#   <key>public.utf16-plain-text</key>
set nString to current application's NSString's stringWithString:clippingText
set publicutf16Data to nString's dataUsingEncoding:(current application's NSUTF16LittleEndianStringEncoding)
set publicutf16Key to "public.utf16-plain-text"


2024/08/21 16:53 light289 への返信

デコード、エンコードの順で書いた方が良かったですね。失礼しました。


ところで、com.apple.traditional-mac-plain-text キーについてちょっとだけ補足。Catalina 以降のテキストクリッピングファイルに合わせるならこんな感じで良いと思います。このデータは参照される可能性が低い (UTF-8 の情報も UTF-16 の情報もない場合に参照される) ので丁寧に作る必要はないのでしょうが、一応サンプルとしてポストしておきます。


#   <key>com.apple.traditional-mac-plain-text</key>
set nString to current application's NSString's stringWithString:clippingText
set regex to current application's NSRegularExpression's regularExpressionWithPattern:"\\A\\p{ASCII}+" options:0 |error|:(missing value)
set match to regex's firstMatchInString:nString options:0 range:(current application's NSMakeRange(0, nString's |length|()))
if match is not missing value then
    set substring to nString's substringWithRange:(match's range())
    set appleTraditionalData to substring's dataUsingEncoding:(current application's NSASCIIStringEncoding)
else
    set appleTraditionalData to current application's NSData's |data|()
end if
set appleTraditionalKey to "com.apple.traditional-mac-plain-text"


テキストクリッピングファイルの仕様が Sierra と Catalina で段階的に変更されたことを今回初めて知りました。また、(話が逸れるので詳細は書きませんが) MacJapanese 絡みの厄介な問題を発見することもできましたし、Automator のアクションのバグ (というか間違った仕様?) を見つけることもできて得した気分になりました。ありがとうございます。


".com"で終わる文字列をテキストクリッピングファイルとして保存する方法が知りたい.

Apple サポートコミュニティへようこそ
Apple ユーザ同士でお使いの製品について助け合うフォーラムです。Apple Account を使ってご参加ください。