<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>シェルスクリプト | imakat.com</title>
	<atom:link href="https://imakat.com/tag/%E3%82%B7%E3%82%A7%E3%83%AB%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88/feed/" rel="self" type="application/rss+xml" />
	<link>https://imakat.com</link>
	<description>工夫と改善で人生をちょっと豊かに</description>
	<lastBuildDate>Thu, 26 Feb 2026 07:27:23 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://i0.wp.com/imakat.com/wp-content/uploads/2023/07/cropped-80d64ecd340db4e2ca3224859b04caed.png?fit=32%2C32&#038;ssl=1</url>
	<title>シェルスクリプト | imakat.com</title>
	<link>https://imakat.com</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">160909258</site>	<item>
		<title>【Mac】Dropboxリンクに一致するフルパスを見つける発見的方法</title>
		<link>https://imakat.com/2025/08/07/26878/</link>
		
		<dc:creator><![CDATA[imakat]]></dc:creator>
		<pubDate>Thu, 07 Aug 2025 13:03:57 +0000</pubDate>
				<category><![CDATA[デジタル]]></category>
		<category><![CDATA[ものづくり]]></category>
		<category><![CDATA[AppleScript]]></category>
		<category><![CDATA[シェルスクリプト]]></category>
		<guid isPermaLink="false">https://imakat.com/?p=26878</guid>

					<description><![CDATA[課題： Dropboxリンクを使って画像や動画をWordPressなどのブログに貼り付けることはできますが、意外とやっている人は少ないです。その理由はいくつかあると思いますが、一番大きな理由は、Dropboxリンクの文字 [&#8230;]]]></description>
										<content:encoded><![CDATA[<div class="sc-dynamic-embed">
  <style>
  /* リンクの見た目を整える */
  .sc-dynamic-embed .sc-link-container { 
      display: flex; 
      margin-bottom: 10px; 
      flex-wrap: wrap; 
      align-items: center; 
  }
  .sc-dynamic-embed .sc-link { margin-bottom: 0; }
  .sc-dynamic-embed .sc-link a {
    font-size: 15px; /* dynamicと合わせた基本サイズ */
    font-weight: normal;
    text-decoration: underline;
    color: #0073aa;
  }
  .sc-dynamic-embed .sc-link a:hover { text-decoration: none; color: #000; }
  
  /* ★スマホ画面（幅500px以下）の時は文字を縮小して統一感を出す */
  @media (max-width: 500px) {
    .sc-dynamic-embed .sc-link a { font-size: 13px; }
  }

  /* 行梱包時の基本スタイル */
  .sc-dynamic-embed .imk-line {
      display: inline-block;
      width: 100%;
      border-radius: 2px;
      transition: background-color 0.1s;
  }

  /* 古い枠内字幕ボックスを強制消去 */
  .sc-dynamic-embed #subtitleOverlay,
  .sc-dynamic-embed #scSubtitleOverlay,
  .sc-dynamic-embed .overlay-cue,
  .sc-dynamic-embed .band {
      display: none !important;
      opacity: 0 !important;
      visibility: hidden !important;
      pointer-events: none !important;
  }
  </style>

  <div class="sc-link-container">
    <p class="sc-link">
      <a href="https://imakat.com/ds62/?drid=55" target="_blank"
         onclick="return scStopAndGo(event, this);">
        👉低画質・枠外字幕はこちら
      </a>
    </p>
  </div>

  <style>
            :root{ --dr5emd-max: 1920px; }
            .video-wrap{position:relative;width:100%;margin:0 auto}
            figure.wp-block-video.aligncenter{
              width:100%;
              max-width:min(var(--dr5emd-max, 1920px), 98vw);
              margin:0 auto;
            }
            #subtitleOverlay{
              position:absolute; left:0; right:0; bottom:6%;
              padding:0 2%; text-align:center; pointer-events:none; z-index:2;
            }
            #subtitleOverlay .band{
              display:inline-block; background:rgba(0,0,0,0.35);
              padding:6px 10px; border-radius:8px; max-width:96%;
              margin:0 auto; box-shadow:0 1px 2px rgba(0,0,0,0.15);
            }
            #subtitleOverlay .overlay-cue{
              color:#fff; font-weight:600;
              font-size:clamp(16px, 3.6vw, 32px);
              line-height:1.32; white-space:pre-wrap; margin:2px 0;
              -webkit-text-stroke:.6px rgba(0,0,0,.7);
              text-shadow:-1px -1px 0 rgba(0,0,0,.6), 1px -1px 0 rgba(0,0,0,.6),
                          -1px  1px 0 rgba(0,0,0,.6), 1px  1px 0 rgba(0,0,0,.6);
            }
            @media (max-width:430px){
              #subtitleOverlay .overlay-cue{ font-size:clamp(16px, 4.2vw, 22px); }
            }
            .dr5emd-sublist details > p{
              height:200px; overflow:auto; background-color:#EDF7FF;
              padding:2px 6px; margin:0; box-shadow:3px 3px 4px black;
              position: relative;
            }
            .dr5emd-sublist details > summary{
              padding:2px 6px; width:100%;
              background-color:#ddd; border:none;
              box-shadow:3px 3px 4px black; cursor:pointer; list-style:none;
            }
            /* ▼ 自動スクロール時のハイライト（文字の太さを標準へ変更） */
            .active-hl {
                background-color: #ffff00 !important;
                color: #ff0000 !important;
                font-weight: normal; /* 標準の太さ */
                border-bottom: 2px solid red;
                display: inline-block;
                border-radius: 2px;
            }
            </style><div class="dr5emd-container"><figure class="wp-block-video aligncenter"><div class="video-wrap"><video id="myVideo" controls controlsList="nodownload" poster="https://imakat.com/rd.php?id=zPkM9gDH.png" playsinline preload="metadata" style="width:100%;height:auto;">  <source src="https://imakat.com/rd.php?id=6GP8yuht.mp4" type="video/mp4">  <track src="https://imakat.com/rd.php?id=FnZSmvnF.vtt" label="日本語" srclang="ja" kind="subtitles"></video><div id="subtitleOverlay" aria-hidden="true"></div></div><script>
document.addEventListener("DOMContentLoaded", function(){
  var video=document.getElementById("myVideo");
  var trackEl=video?video.querySelector("track[kind='subtitles'], track[kind='captions']"):null;
  var overlay=document.getElementById("subtitleOverlay"); if(!video||!overlay) return;
  video.addEventListener("contextmenu", function(e){ e.preventDefault(); return false; }, false);
  function setNative(mode){
    try{
      if(video.textTracks && video.textTracks.length){
        for(var i=0;i<video.textTracks.length;i++){ video.textTracks[i].mode = mode; }
      }
      if(trackEl && trackEl.track) trackEl.track.mode = mode;
    }catch(e){}
  }
  var isOverlay=true,lastSig="";
  function sig(active){if(!active||active.length===0)return"";var a=[];for(var i=0;i<active.length;i++){var c=active[i];a.push([c.startTime,c.endTime,c.text].join("|"));}return a.join("||");}
  function cueLine(c){var d=document.createElement("div");d.className="overlay-cue";d.setAttribute("translate","yes");if(typeof c.getCueAsHTML==="function")d.appendChild(c.getCueAsHTML());else d.textContent=c.text;return d;}
  function render(){
    if(!isOverlay || !trackEl || !trackEl.track) return;
    var ac=trackEl.track.activeCues,s=sig(ac); if(s===lastSig) return; lastSig=s;
    overlay.innerHTML=""; if(!ac || ac.length===0) return;
    var b=document.createElement("div"); b.className="band"; b.setAttribute("translate","yes");
    Array.from(ac).sort(function(a,b){return a.startTime-b.startTime;}).forEach(function(c){b.appendChild(cueLine(c));});
    overlay.appendChild(b);
  }
  function useOverlay(){isOverlay=true;overlay.style.display="";setNative("hidden");lastSig="";render();}
  function useNative(){isOverlay=false;overlay.style.display="none";setNative("showing");lastSig="";}
  useOverlay();
  if(trackEl){
    if(trackEl.track){ try{ trackEl.track.addEventListener("cuechange",render); }catch(e){} }
    trackEl.addEventListener("load", function(){ try{ if(trackEl.track) trackEl.track.addEventListener("cuechange",render); }catch(e){} render(); });
  }
  video.addEventListener("loadedmetadata",render);
  function handleWebkitMode(){ var m = video.webkitPresentationMode || "inline"; (m==="picture-in-picture"||m==="fullscreen") ? useNative() : useOverlay(); }
  if("webkitPresentationMode" in video){ video.addEventListener("webkitpresentationmodechanged",handleWebkitMode); handleWebkitMode(); }
  if("webkitCurrentPlaybackTargetIsWireless" in video){
    video.addEventListener("webkitcurrentplaybacktargetiswirelesschanged", function(){ video.webkitCurrentPlaybackTargetIsWireless ? useNative() : useOverlay(); });
  }
  if("pictureInPictureEnabled" in document){
    video.addEventListener("enterpictureinpicture",useNative);
    video.addEventListener("leavepictureinpicture",useOverlay);
  }
  document.addEventListener("fullscreenchange", function(){
    var fs=document.fullscreenElement;
    if(!fs) return useOverlay();
    (fs===video || (fs && fs.contains && fs.contains(video))) ? useNative() : useOverlay();
  });
});
</script>
                <figcaption></figcaption></figure><div class="dr5emd-sublist"><details><summary>字幕一覧(クリック)</summary> <p>
(<a href="#" class="imk-cue" data-seek="0:00">00:00:00</a>) (1)〜Dropboxリンクに一致するフルパスの検索〜<br>
(<a href="#" class="imk-cue" data-seek="0:11">00:00:11</a>) (2)アプリ「dropboxリンクに一致するフルパス検索.app」を起動します。<br>
(<a href="#" class="imk-cue" data-seek="0:17">00:00:17</a>) (3)Dropboxの共有リンクを入力します。<br>
(<a href="#" class="imk-cue" data-seek="0:27">00:00:27</a>) (4)前もって用意した、Dropboxリンクのサンプルを使います。<br>
(<a href="#" class="imk-cue" data-seek="0:38">00:00:38</a>) (5)ペーストします。 https://www.dropbox.com/scl/fi/7l9*************tqn50/130606_.jpg?rlkey=8i2wf85**************cxw&raw=1<br>
(<a href="#" class="imk-cue" data-seek="0:47">00:00:47</a>) (6)この例では、ファイル名から日本語を除いた部分が「130606_.jpg」となるフルパスを検索します。<br>
(<a href="#" class="imk-cue" data-seek="1:04">00:01:04</a>) (7)一致したファイルパス： /Volumes/NO3_SSD/Dropbox/dropbox_1/pmedia/130606_刺身は大丈夫か.jpg<br>
(<a href="#" class="imk-cue" data-seek="1:15">00:01:15</a>) (8)なぞって右クリック→サービス→Finderに表示<br>
(<a href="#" class="imk-cue" data-seek="1:24">00:01:24</a>) (9)目的のファイルがハイライトされています。<br>
(<a href="#" class="imk-cue" data-seek="1:28">00:01:28</a>) (10)画像は「130606_刺身は大丈夫か.jpg」<br>
(<a href="#" class="imk-cue" data-seek="1:32">00:01:32</a>) (11)ここは、ジャカルタの郊外、　<br>
(<a href="#" class="imk-cue" data-seek="1:35">00:01:35</a>) (12)韓国の焼肉屋の、<br>
(<a href="#" class="imk-cue" data-seek="1:38">00:01:38</a>) (13)刺身の盛り合わせ。<br>
(<a href="#" class="imk-cue" data-seek="1:41">00:01:41</a>) (14)常夏の国で、刺身なんか大丈夫か。<br>
(<a href="#" class="imk-cue" data-seek="1:43">00:01:43</a>) (15)と心配になりますが、腹痛になったことはなかったです。<br>
(<a href="#" class="imk-cue" data-seek="1:46">00:01:46</a>) (16)すみません。どうでもいい話。<br>
(<a href="#" class="imk-cue" data-seek="1:49">00:01:49</a>) (17)以上です。<br>
</p> </details>
<style>
details { font: 16px "Open Sans", Calibri, sans-serif; width: 100%; }
details > summary { padding: 2px 6px; width: 100%; background-color: #ddd; border: none; box-shadow: 3px 3px 4px black; cursor: pointer; list-style: none; }
details > p { font: 14px "Open Sans", Calibri, sans-serif; height:150px; overflow: scroll; background-color: #EDF7FF; padding: 2px 6px; margin: 0; box-shadow: 3px 3px 4px black; }
</style></div><script>
(function(){
  var root=document.querySelector(".dr5emd-sublist");
  var video=document.getElementById("myVideo");
  if(!root || !video) return;
  function parseTs(ts){
    if(!ts) return null;
    var p=ts.trim().split(":").map(function(x){return parseInt(x,10)||0;});
    if(p.length===2) return p[0]*60 + p[1];
    if(p.length===3) return p[0]*3600 + p[1]*60 + p[2];
    return null;
  }
  root.addEventListener("click", function(e){
    var a=e.target.closest && e.target.closest("a.imk-cue[data-seek]");
    if(!a || !root.contains(a)) return;
    e.preventDefault();
    var sec = parseTs(a.getAttribute("data-seek"));
    if(sec==null) return;
    try{ video.currentTime = sec; if(video.paused) video.play(); }catch(_){}
  });
  video.addEventListener("timeupdate", function(){
    var listContainer = root.querySelector("details > p");
    if(!listContainer) return;
    var cues = listContainer.querySelectorAll("a.imk-cue");
    if(cues.length === 0) return;
    var cur = video.currentTime;
    var active = null;
    for(var i=0; i<cues.length; i++){
        var t = parseTs(cues[i].getAttribute("data-seek"));
        if(t !== null && cur >= t - 0.5){
            active = cues[i];
        } else if(t > cur){
            break;
        }
    }
    if(active){
        if(active.classList.contains("active-hl")) return;
        var old = listContainer.querySelectorAll(".active-hl");
        for(var k=0; k<old.length; k++) old[k].classList.remove("active-hl");
        active.classList.add("active-hl");
        if(listContainer.offsetParent !== null){
            var containerRect = listContainer.getBoundingClientRect();
            var activeRect = active.getBoundingClientRect();
            var targetScroll = listContainer.scrollTop + (activeRect.top - containerRect.top) - (listContainer.clientHeight / 2) + (active.clientHeight / 2);
            listContainer.scrollTo({ top: targetScroll, behavior: "smooth" });
        }
    }
  });
})();
</script>
                </div>

  <script>
  (function(){
    var me = document.currentScript;
    var wrapper = me ? me.closest('.sc-dynamic-embed') : null;

    /* -----------------------------------------------
       1. 動画の保護機能（右クリック禁止・DL防止）
       ----------------------------------------------- */
    function protectVideo() {
      var target = wrapper ? wrapper : document;
      var mediaEls = target.querySelectorAll('video');
      mediaEls.forEach(function(v){
        if(v.dataset.protected === 'true') return;
        v.dataset.protected = 'true';
        v.setAttribute('controlsList', 'nodownload');
        v.oncontextmenu = function() { return false; };
        v.addEventListener('contextmenu', function(e){ e.preventDefault(); return false; }, false);
      });
    }

    /* -----------------------------------------------
       2. 字幕制御＆ハイライト機能（iPhone全画面 完全対応版）
       ----------------------------------------------- */
    function initSubtitles() {
      var target = wrapper ? wrapper : document;
      var video = target.querySelector('video');
      var listContainer = target.querySelector('details > p');
      
      if (!video || !listContainer) return false; 

      if (video.dataset.subInit === 'true') return true; 
      video.dataset.subInit = 'true';

      var oldOverlay = target.querySelector('#subtitleOverlay') || target.querySelector('#scSubtitleOverlay');
      if (oldOverlay) {
          oldOverlay.style.setProperty('display', 'none', 'important');
          oldOverlay.innerHTML = ''; 
      }

      function isSpecialMode() {
        var isFs = !!(document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement);
        var isPiP = !!(document.pictureInPictureElement && document.pictureInPictureElement === video) || (video.webkitPresentationMode === "picture-in-picture");
        var isIOSFs = !!video.webkitDisplayingFullscreen; 
        return isFs || isPiP || isIOSFs;
      }

      try {
        if(video.textTracks && video.textTracks.length > 0){
          for(var i=0; i<video.textTracks.length; i++){
             if(video.textTracks[i].kind === 'subtitles' || video.textTracks[i].kind === 'captions'){
                 video.textTracks[i].mode = "hidden";
             }
          }
        }
      } catch(e){}

      video.addEventListener("webkitbeginfullscreen", function() {
          try {
              if(video.textTracks && video.textTracks.length > 0) {
                  for(var i=0; i<video.textTracks.length; i++){
                      if(video.textTracks[i].kind === 'subtitles' || video.textTracks[i].kind === 'captions') video.textTracks[i].mode = "showing";
                  }
              }
          } catch(e){}
      });
      video.addEventListener("webkitendfullscreen", function() {
          try {
              if(video.textTracks && video.textTracks.length > 0) {
                  for(var i=0; i<video.textTracks.length; i++){
                      if(video.textTracks[i].kind === 'subtitles' || video.textTracks[i].kind === 'captions') video.textTracks[i].mode = "hidden";
                  }
              }
          } catch(e){}
      });

      var detailsEl = target.querySelector("details");
      if (detailsEl) {
          detailsEl.open = true; 
          var summaryEl = detailsEl.querySelector("summary");
          if (summaryEl) summaryEl.textContent = "字幕(シーン)はここをクリック";
      }

      if (!listContainer.dataset.formatted) {
          var html = listContainer.innerHTML;
          var lines = html.split(/<br\s*\/?>/i);
          var newHtml = "";
          for(var j=0; j<lines.length; j++) {
              if(lines[j].trim() === "") continue;
              newHtml += "<span class='imk-line'>" + lines[j] + "</span><br>";
          }
          listContainer.innerHTML = newHtml;
          listContainer.dataset.formatted = "true";
      }

      function parseTs(ts){
        if(!ts) return null;
        var p = ts.trim().split(":").map(function(x){return parseInt(x,10)||0;});
        if(p.length===2) return p[0]*60 + p[1];
        if(p.length===3) return p[0]*3600 + p[1]*60 + p[2];
        return null;
      }

      var rootSublist = target.querySelector(".dr5-sublist") || listContainer.parentElement;
      if (rootSublist) {
        rootSublist.addEventListener("click", function(e){
          var a = e.target.closest && e.target.closest("a.imk-cue[data-seek]");
          if(!a) return;
          e.preventDefault();
          var sec = parseTs(a.getAttribute("data-seek"));
          if(sec==null) return;
          try{ video.currentTime = sec; if(video.paused) video.play(); }catch(_){}
        });
      }

      video.addEventListener("timeupdate", function(){
        var desiredMode = isSpecialMode() ? "showing" : "hidden";
        try {
            if(video.textTracks && video.textTracks.length > 0){
                for(var i=0; i<video.textTracks.length; i++){
                    if((video.textTracks[i].kind === 'subtitles' || video.textTracks[i].kind === 'captions') && video.textTracks[i].mode !== desiredMode) {
                        video.textTracks[i].mode = desiredMode;
                    }
                }
            }
        } catch(e){}

        var cues = listContainer.querySelectorAll("a.imk-cue");
        if(cues.length === 0) return;
        var cur = video.currentTime;
        var activeA = null;

        for(var i=0; i<cues.length; i++){
            var t = parseTs(cues[i].getAttribute("data-seek"));
            if(t !== null && cur >= t - 0.5){ activeA = cues[i]; } 
            else if(t > cur){ break; }
        }

        if(activeA){
            var activeLine = activeA.closest(".imk-line");
            if(!activeLine) activeLine = activeA;

            if(activeLine.classList.contains("active-hl")) return;

            var allLines = listContainer.querySelectorAll(".imk-line");
            for(var k=0; k<allLines.length; k++) {
                allLines[k].classList.remove("active-hl");
                allLines[k].removeAttribute("style"); 
            }
            var allLinks = listContainer.querySelectorAll("a");
            for(var m=0; m<allLinks.length; m++) {
                allLinks[m].classList.remove("active-hl");
                allLinks[m].removeAttribute("style"); 
            }

            activeLine.classList.add("active-hl");
            activeLine.style.setProperty("background-color", "#ffff00", "important");
            activeLine.style.setProperty("color", "red", "important");
            activeLine.style.setProperty("font-weight", "normal", "important");
            
            var newLinks = activeLine.querySelectorAll("a");
            for(var n=0; n<newLinks.length; n++) {
                newLinks[n].style.setProperty("color", "red", "important");
                newLinks[n].style.setProperty("text-decoration", "none", "important");
            }

            if(listContainer.offsetParent !== null){
                var containerRect = listContainer.getBoundingClientRect();
                var activeRect = activeLine.getBoundingClientRect();
                var targetScroll = listContainer.scrollTop + (activeRect.top - containerRect.top) - (listContainer.clientHeight / 2) + (activeLine.clientHeight / 2);
                listContainer.scrollTo({ top: targetScroll, behavior: "smooth" });
            }
        }
      });

      return true;
    }

    /* -----------------------------------------------
       監視タイマー
       ----------------------------------------------- */
    var checks = 0;
    var checkTimer = setInterval(function(){
      protectVideo(); // ★dynamic2は保護を維持
      var success = initSubtitles();
      checks++;
      if (success || checks > 20) { 
        clearInterval(checkTimer);
      }
    }, 500); 

    /* -----------------------------------------------
       3. 画面遷移時の停止機能
       ----------------------------------------------- */
    if (!window.scStopAndGo) {
      window.scStopAndGo = function(event, link){
        try{
          var mediaEls = document.querySelectorAll('video, audio');
          mediaEls.forEach(function(m){
            try{
              if (!m.paused) m.pause();
              if (document.pictureInPictureElement === m && document.exitPictureInPicture) {
                document.exitPictureInPicture().catch(function(){});
              }
            }catch(e){}
          });
        }finally{
          event.preventDefault();
          setTimeout(function(){
            if (link.target === '_blank') {
              window.open(link.href, '_blank');
            } else {
              window.location.href = link.href;
            }
          }, 50);
        }
        return false;
      };
    }
  })();
  </script>
</div>



<h5 class="wp-block-heading">課題：</h5>



<p>Dropboxリンクを使って画像や動画をWordPressなどのブログに貼り付けることはできますが、意外とやっている人は少ないです。その理由はいくつかあると思いますが、一番大きな理由は、Dropboxリンクの文字列を見たときに、例えば以下のような画像ファイルの文字列を見たときに、</p>



<figure class="wp-block-flexible-table-block-table"><table class="has-fixed-layout"><tbody><tr><td><a rel="noopener" target="_blank" href="https://www.dropbox.com/scl/fi/55w8bp7q9758j9oktwy1q/2508083_-dropbox.png?rlkey=qp7y19s1brlh7s08hteiwhyj8&amp;raw=1">https://www.dropbox.com/scl/fi/55w8bp7q9758j9oktwy1q/2508083_-dropbox.png?rlkey=qp7y19s1brlh7s08hteiwhyj8&amp;raw=1<span class="fa fa-external-link external-icon anchor-icon"></span></a></td></tr></tbody></table></figure>



<p><br>これだけでは、その画像ファイルが自分のMac,PCのどこにあるのか全くわからない。さらにDropboxフォルダのどこにあるのかもわからない。迷子になるからです。</p>



<p>つまり、後になってその画像や動画を修正したい時、ローカルにあるファイルがすぐに見つからないという壁にぶつかってしまうのです。というか、安全対策上、ある場所をマル見せしない、それが基本ではありますが。</p>



<figure class="wp-block-image size-large is-resized"><a href="https://imakat.com/rd.php?id=g1deedLO.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=g1deedLO.png" alt="" style="width:199px;height:auto"/></a></figure>



<p>※正しくいうと、Dropboxの場合、おうちはDropboxのクラウドストレージ、出先がローカルフォルダ、という関係になります。ですから、エラーが続くからといって、Dropboxのクラウド側を空っぽにして、ローカルのファイルを送り込むようなことをするのは大間違いです。Dropboxリンクは全て新しいものに置き換わってしまいます。<strong>正しくは、いつも、ローカル側を空にして、クラウドストレージ側からダウンロードします</strong>。</p>



<h3 class="wp-block-heading">ポイント解説：</h3>



<p>その対処ですが、３つ考えられます。</p>



<p>１つ目は、ローカルで常に記録することです。</p>



<p>つまり、Dropboxリンクを取得するときに、そこに紐づくファイルパス（フルパスで）を記録しておくようにします。さらに、その後でファイルの名前を変えたり、フォルダを変更したりすることがあります。したがって、変更前のファイルパスと変更後のファイルパスをペアで記録しておくようにします。ローカルで確実に管理できますね。</p>



<p>２つ目は、Dropbox APIを利用する方法です。</p>



<p>Dropbox APIを使うと、Dropboxリンク及びローカルにあるDropboxフォルダ以下のパスがペアで取得できます。この情報を使って照合します。</p>



<p><strong>３つ目は、正確さは欠けますが、発見的方法（ヒューリスティック・アプローチ）です。</strong></p>



<p>そこまで丁寧な方法ではなくて、ざっくりと推測する、候補を複数ピックアップする、それだけでもかなり有用です。</p>



<p>APIを使わずに、</p>



<p>「すでにMac上にあるDropboxフォルダ内の実ファイルと照合する」だけでも、かなり役に立ちます。</p>



<p>今回は、一番シンプルな、この３つ目の方法を紹介します。</p>



<h5 class="wp-block-heading">手順：</h5>



<figure class="wp-block-flexible-table-block-table"><table class="has-fixed-layout"><tbody><tr><td>[考え方]<br>Dropbox APIは使わず、すでにMac上にあるDropboxフォルダ内の実ファイルと照合する、発見的方法です。<br>Dropboxリンクは、&#8221;***/<span class="bold-red">********</span>?rlkey=*******&amp;raw=1&#8243;における/と?に挟まれた<span class="bold-red">赤字</span>の部分に、ファイル名から日本語文字列を削除した部分が残されています。ローカルの対象フォルダの中のファイル名の日本語文字列を削除した部分と一致するものを抽出する方法です。<br><br><strong>AppleScriptのアプリで、以下のような手順を行うようにします。<br>（１）Dropbox共有リンクの入力を受け付ける<br>（２）そのURLから、共有リンクに埋め込まれたファイル名（dropboxStyleName）を抽出する<br>（３）Macのローカルディスク（Dropboxと同期されているフォルダ）内のファイルを find コマンドで検索し、<br>（４）ファイル名を変換（日本語文字を &#8211; に）して、dropboxStyleName と一致するかどうかを照合する<br>（５）一致したファイルの ローカルパス をリストアップして表示する<br>以上</strong></td></tr></tbody></table></figure>



<figure class="wp-block-image size-large is-resized"><a href="https://imakat.com/rd.php?id=tWkdgn3n.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=tWkdgn3n.png" alt="" style="width:103px;height:auto"/></a></figure>



<h5 class="wp-block-heading">スクリプト：</h5>



<figure class="wp-block-flexible-table-block-table"><table class="has-fixed-layout"><tbody><tr><td>AppleScript<br><strong>dropboxリンクに一致するフルパス検索.app</strong><br>下欄のスクリプトをコピペしてください。<br><span class="bold-blue">青字</span>の部分は、自分の条件に置き換えてください。<br></td></tr><tr><td>&#8212; ▼ 対象フォルダ、２つある場合です（サブフォルダも含めて検索）<br>set folder1 to &#8220;<span class="blue"><span class="bold-blue">/Volumes/NO3_SSD/Dropbox/********/********</span></span>&#8220;<br>set folder2 to &#8220;<span class="bold-blue">/Volumes/NO3_SSD/Dropbox/********/********</span>&#8220;</td></tr><tr><td><a href="https://imakat.com/?pubtxt=dropboxリンクに一致するフルパス検索app_pub.txt" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=zrU95MiY.png" alt="" style="width:120px; height:auto;"></a></td></tr></tbody></table></figure>



<h5 class="wp-block-heading">結果：</h5>



<p>ファイル名に、日本語を多用している場合は、複数の候補が表示されるようになっています。しかし、ファイル名には日付や連番、バージョンなどの英数字を含めるように気を配れば、複数の候補がある場合は、非常に少ないでしょう。したがって、今回の紹介の方法は、実用的であると思います。<br></p>



<p>以上</p>



<p></p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">26878</post-id>	</item>
		<item>
		<title>【Macクイックアクション】線画イラストを切り抜く方法　その３</title>
		<link>https://imakat.com/2025/05/29/26297/</link>
		
		<dc:creator><![CDATA[imakat]]></dc:creator>
		<pubDate>Thu, 29 May 2025 06:18:00 +0000</pubDate>
				<category><![CDATA[デジタル]]></category>
		<category><![CDATA[ものづくり]]></category>
		<category><![CDATA[ChatGPT]]></category>
		<category><![CDATA[クイックアクション]]></category>
		<category><![CDATA[画像加工]]></category>
		<category><![CDATA[Python3]]></category>
		<category><![CDATA[シェルスクリプト]]></category>
		<category><![CDATA[Automator]]></category>
		<guid isPermaLink="false">https://imakat.com/?p=26297</guid>

					<description><![CDATA[課題 Macのクイックアクションには標準で「背景を削除」があるのですが、どうも精度が今ひとつです。 画像ファイルの上で、右クリック→クイックアクション、「背景を削除」を選びます。非常に便利です。 普通の写真、動物や人物の [&#8230;]]]></description>
										<content:encoded><![CDATA[
<h5 class="wp-block-heading">課題</h5>



<p>Macのクイックアクションには標準で「背景を削除」があるのですが、どうも精度が今ひとつです。</p>



<p>画像ファイルの上で、右クリック→クイックアクション、「背景を削除」を選びます。非常に便利です。</p>



<figure class="wp-block-image size-large"><a href="https://imakat.com/rd.php?id=F5IeAUW3.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=F5IeAUW3.png" alt=""/></a></figure>



<p>普通の写真、動物や人物の映った普通の写真からの、背景削除は綺麗に出来ます。ここまでは問題ないです。</p>



<figure class="wp-block-image size-large"><a href="https://imakat.com/rd.php?id=TKt5rZl2.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=TKt5rZl2.png" alt=""/></a></figure>



<p>ところが、私は、線だけで描いたイラストをよく使うのですが、その背景削除は、なかなか上手にいきません。下のように、白塗りは不正確、線も不正確です。</p>



<figure class="wp-block-image size-large"><a href="https://imakat.com/rd.php?id=X2Gm36eF.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=X2Gm36eF.png" alt=""/></a></figure>



<h3 class="wp-block-heading">ポイント解説：</h3>



<p>ということで、これまで、線画イラストを切り抜く方法の、その１、その２と紹介してきましたが、今回は、その３です。Macを使う人にとっては、一番、使いやすいかもしれません。</p>



<p>まず最初に、ChatGPTにスクリプトの記述を依頼します。</p>



<p>以下が、依頼文(プロンプト）です。</p>



<h5 class="wp-block-heading">プロンプト</h5>



<figure class="wp-block-flexible-table-block-table"><table class="has-fixed-layout"><tbody><tr><td><strong>MacのAutomator,Python、シェルスクリプト、その他必要な言語を使って、下記フローの記述をお願いします。<br><br>１　MacのPDFまたは画像ファイル(pdf png jpg jpeg gif)の上で右クリックしてクイックアクションを起動する。クイックアクション名を&#8221;PDFまたは画像から切り抜き画像の生成&#8221;とする。<br>２　PDFファイルはJPG画像に変換して、他はそのままで、画像1とする。<br>３　画像1から、輪郭線と線だけを元の色(黒色)、不透明で抽出し、それ以外は、完全に透明にした画像(線と輪郭線だけの透過PNG画像)を作成する。注意点として、線の中は中空にせず線の色で塗り埋めること。これをPNG画像2とする。<br>４　PNG画像2から、線によって閉じた輪郭線（外周線)を検出して、その輪郭線の内側を白塗りにして、白塗り以外の部分を透明にして、PNG画像3を生成する。<br>５　PNG画像３を下にして、PNG画像2を上にして、重ねて、PNG画像４を生成する。PNG画像４が完成した画像となる。<br>６　PNG画像４、を読み込んだファイルと同じフォルダに書き込む。ファイル名は”読み込んだファイルのファイル名から拡張子を除いた部分&#8221;&amp;&#8221;_cropped.png&#8221;とする。<br>７　画像１、PNG画像２、PNG画像３、は削除する。<br>以上</strong></td></tr></tbody></table></figure>



<h5 class="wp-block-heading">スクリプト</h5>



<p>何回か修正、微調整を行なって、正しく処理されるようになったスクリプトの記述は以下です。そのままコピペで使えますが、<span class="blue">python3のパスは書き換えてください</span>。<strong>Automator、シェルスクリプト、Python3</strong>、を利用しています。</p>



<figure class="wp-block-flexible-table-block-table"><table class="has-fixed-layout"><tbody><tr><td>要約：PDFまたは画像から自動で切り抜き画像を作成する Automator ワークフローの内容である。入力ファイルの拡張子に応じて処理を分岐し、PDFは300dpiのJPEGに変換、GIFは最初のフレームをJPEG化、その他の画像はコピーして処理用JPEGとする。その後、Python スクリプトにファイル情報を引き渡し、OpenCV で画像を2倍拡大、濃い線部分を抽出して太らせ、線画のみの PNG（_lineonly）、外周塗りつぶし PNG（_filled）を生成。さらに両者を合成して最終的な切り抜き PNG（_cropped）を出力する。最後に中間ファイルを削除して処理を終了する。</td></tr><tr><td>クイックアクション<br>ワークフローが受け取る現在の項目：ファイルまたはフォルダ<br>検索対象：Finder.app<br>シェル：/bin/bash<br>入力の引き渡し方法：引数として<br>「シェルスクリプトを実行」を挿入</td></tr><tr><td><a href="https://imakat.com/?pubtxt=PDFまたは画像から切り抜き画像の生成workflow_pub.txt" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=zrU95MiY.png" alt="" style="width:120px; height:auto;"></a></td></tr></tbody></table></figure>



<h5 class="wp-block-heading">結果</h5>



<p>このスクリプトを実行します。<br>PDFまたは画像ファイルの上で右クリックして、クイックアクションから、選択します。</p>



<figure class="wp-block-image size-large"><a href="https://imakat.com/rd.php?id=5mJerXQl.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=5mJerXQl.png" alt=""/></a></figure>



<p>ファイル名の末尾に、_cropped.pngと入ったファイルが出来るので、これをクリックして確認します。</p>



<figure class="wp-block-image size-large"><a href="https://imakat.com/rd.php?id=4Da84efa.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=4Da84efa.png" alt=""/></a></figure>



<p><br>私の個人的な設定ですが、スクリーンショットはPDFで保存するようにしています。その理由は、PDFファイルですと図形や文字などを追記することができ、その追記も後から修正ができるからです。そのため、画像ファイルでもPDFでも、切り抜きができるようにしたかったのです。<br><br>以上です。<br></p>



<p></p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">26297</post-id>	</item>
	</channel>
</rss>
