bash」タグアーカイブ

bash: ‘\n’ in read file (part2)

前回の抜き出しシェルスクリプトだけど。
echo -e で出力しようとしたのがいけなかったようだ。

while ループの中でガシガシとリダイレクトすればイケそう。
と思って Fedora で試すとバッチリ狙いどおりに出力された。

mac にソースを持って行くと、あれ?
なんかおかしいけど原因が解らない、仕事中も含め半日悩む。
下記の実験コードでやっと理解。

#!/bin/sh

while read -r line; do
    echo $line
    echo 0
done << __DOC__
aaa bbb
ccc\nddd
eee
__DOC__

Fedora

macOS

macOS の read は改行コードと文字列の \n を区別してくれない。
のではないな、echo で改行されてしまっているようだ。
echo “aa\nbb” だと普通に \n が出力されるのに、よくわからない。

# 追記
sh ではなく bash 指定なら Fedora 同様の出力になるようです。
zsh の場合 echo に -E オプションを付けないと同様な症状になる。
# 追記おしまい

後、前回は気が付かなかったけど macOS の echo には -e オプションが無い!
bash のバージョンが違うのは知っているけどまさかこんな所が違うとは。

同じ bash だから同じ挙動やオプションだと思ってはいけない教訓になった。
拡張の [[ とかは同様に使えるのにね。
てか、macOS って中身は本当に色々と古いんだよなぁ。

bash: ‘\n’ in read file

py2app で分割で作っている Python ファイルを扱う手段がわからない。
ならば合体スクリプトを作って単体にしてしまえばいいんでね?

目印に #begin, #end みたいなのを書いてその中間だけを取り出せば簡単そう。
ということで早速シェルスクリプトを書いてみた。

##!/bin/sh

ifs=$IFS
IFS=$'\n'

for filename in *.py; do
    while read -r line; do
        [[ $line =~ "#end" ]] && break
        [[ $begin = begin ]] && lines+="$line\n"
        [[ $line =~ "#begin" ]] && begin=begin
    done < $filename
    begin=""
done

echo -e $lines

IFS=$ifs

のですけど。。。

#!/usr/bin/env python3

#begin

class TestClass():
    def __init__(self):
        print('hello\nworld')

#end
TestClass()

こんなファイルで試すと。

class TestClass():
    def __init__(self):
        print('hello
world')

えぇ。。。

ソースコード中の \n まで改行とみなされてしまうのカヨ!
ちなみに -r 無しだと hellonworld になるのでもっと注意。
cat ならイケるかな。

##!/bin/sh

ifs=$IFS
IFS=$'\n'

for filename in *.py; do
    for line in `cat $filename`; do
        [[ $line =~ "#end" ]] && break
        [[ $begin = begin ]] && lines+="$line\n"
        [[ $line =~ "#begin" ]] && begin=begin
    done
    begin=""
done

echo -e $lines

IFS=$ifs

同じだ。。。

#!/usr/bin/env python3

import os

lines = []
begin = ''

for filename in [s for s in os.listdir() if s.endswith('.py')]:
    with open(filename) as f:
        for s in f.read().split('\n'):
            if '#end' in s: break;
            elif begin == 'begin': lines.append(s)
            elif '#begin' in s: begin = 'begin'
    begin = ''

print('\n'.join(lines))

python 等なら問題ない。

connect.py みたいなファイルを用意してシェルから呼ぶことにするか。
シェルってほんとに色々と困ったちゃん。

sips

Retina Display の mac でスクリーンショットする。
mac で見る分にはいいけど Blog にアップするとデカくてビビる。
前回のスクショの詳細を見ると 144dpi もある、通常の二倍だ。

全画面だと 2880x1800px になるからたしかに二倍だね。
色々検索するとみんな困っているようで、何故こうしたのやら。

macos – How can I stop my retina display from taking 2x sized screenshots? – Ask Different

sips という素敵なコマンドがあるようだ。
man を見ると BSD がとか出るけど最後に Darwin の文字が。
Fedora の dnf list にも出ないしやはり macOS 専用のようです。

早速クイックアクションに登録。
コピペだと上書きになるので別名保存処理を入れてみた。
コマンドではないからディレクトリ名を得る必要があるな。

for f in "$@"
do
    width=$((`sips -g pixelWidth "$f" | cut -s -d ':' -f 2 | cut -c 2-` / 2))
    dir=${f%/*}
    name=`date "+72dpi-%Y%m%d-%H:%M.%S.png"`
    sips -s dpiHeight 72.0 -s dpiWidth 72.0 -Z $width "$f" --out "$dir/$name"
done

name はお好みに。

値は正常になるけどなんかボケるなぁ。
でもこんなもんでいいだろう、今回よりコレ使います。

Automator JXA and shell

サービスがいいMacBookにしておく – ザリガニが見ていた…。

Automator ってコンテキストメニューが作れるんだ!
この方法なら筆者が書いた Finder Script より簡単にシェルが使える。
macOS アプリを JXA (AppleScript) で拡張 – L’Isola di Niente
いや Nautilus スクリプトと同じように使えるというべきか。

では早速。
Mojave ではサービスではなく「クイックアクション」になっています。

リンク先ではテキストの選択だけど Finder での選択もイケそう。
「Finder 項目を表示」を挟む必要があるみたい。
早速同じ AppleScript を試すと、、、、、

あぁ、やはりパス名は古い mac 形式、いつものことだ。
しかしこの input は CR 改行区切りの文字列なのか配列なのか?
JXA でないと筆者は解らないので「JavaScript を実行」に置き換えて。

function run(input, parameters) {
	
	// Your script goes here
	let app = Application.currentApplication();
	app.includeStandardAdditions = true;
	//let s = input.toString().split('\r').join('\n');
	let s = input.join('\n');
	app.displayAlert(s);

	//return input;
}

うん、JXA だと input は JavaScript の配列になるようだ。
こっちだと UNIX パスになるな、何故かはワカンネ。
最後の return は次に続くチェーンが無いなら不要みたい。

んで、名前を付けて保存すると。

~/Library/Services/

以下に保存される、*.workfrow はディレクトリのようだ。
中にある info.plist の NSMenuItem キーからメニューを変名できる。

まて、よく見ると「シェルスクリプトを実行」のアクションがある。
AppleScript, JXA だけでなく直接 bash も使えるようで。
入力を「引数として」の選択肢もある、これならどうなる?

ところでカレントディレクトリは又してもルートなんだろうか。
と思って調べたら $HOME だった、どっちにしろ移動しなきゃ。
ファイル実行ではないので $0 では調べられない、と最初思ったけど。
ファイルを選択してコンテキストメニューなんだから $1 でいいヤン。

cd `dirname $1`
echo "$@" > out.txt

で保存して Finder で何か選択して実行。
選択したファイルがある位置に UNIX 形式パスが IFS 区切りでリダイレクトされるのが確認できるはず。
これでシェルを簡単に使えるようになるのでお好きなコマンドを。
JXA とチェーンを繋げればかなり高度な自動化ができそう。

colon command

Bash, no-arguments warning, and case decisions – Stack Overflow

このページの

: ${1?"Usage: $0 ARGUMENT"}

がよく解らなかった。
先頭のコロンって何も起こらないコマンドだったはずだけど。
変数中の ? も今まで見たことがなかった。

何もしない組み込みコマンド ":" (コロン)の使い道 – Qiita

凄く詳しい解説をありがとう。

: echo 1
# 無視される

: | echo 1
# 1 が出力される

: touch one.txt > tow.txt
# 空の tow.txt だけが造られる

: ${var=3}
echo $var
# 3 が出力される

なるほど、先頭コロンが何もしないのは最初の引数だけなんだ。
でも :=, :? の場合は副作用が起こるということらしい

それと変数中の ? の正体は :? のようだ、イコールも同様。
:- と同様にコロンは実は無くてもいいということね。

つまり最初のは第一引数が無い場合は ? 以下をエラー出力して終了という意味。
こんな短いコードで実現、って Bash 解りづれぇYO!