1
1

More than 5 years have passed since last update.

Perlで「 `brew install coreutils` しろ」に抗う

Last updated at Posted at 2019-05-07

Q: これMacで動かないんだけど?
A: brew install coreutilsしろ

みたいな身も蓋もないissueをよく見かける。

PerlならMacにもLinuxにも普段入っているので、これをPerlでなんとかしてみたい。

base64

base64 の-wオプションで末尾の改行を無くしたい。

#!/bin/bash

if [ $(uname) == "Darwin" ]; then
    datecmd=gdate
    base64cmd=gbase64
else
    datecmd=date
    base64cmd=base64
fi
ts=$(($(${datecmd} +%s%N)/100000))

# [...]

tmp_CWLogB64=$(echo ${tmp_CWLogRaw/__timestamp__/$ts} | gzip | ${base64cmd} -w0)

Time::HiResで1秒未満の現在時刻を、base64にはMIME::Base64を使う。

ts=$(perl -MTime::HiRes=time -e 'printf "%.4f\n", time' | tr -d '.')
tmp_CWLogB64=$(echo ${tmp_CWLogRaw/__timestamp__/$ts} | gzip \
    | perl -MMIME::Base64 -0777 -ne 'print encode_base64($_, "");')

cut

BSDの cut に-zオプションが無い。

p () {
    local open
    open=open   # on OSX, "open" opens a pdf in preview
    ag -U -g ".pdf$" \
    | fast-p \
    | fzf --read0 --reverse -e -d $'\t'  \
        --preview-window down:80% --preview '
            v=$(echo {q} | gtr " " "|"); 
            echo -e {1}"\n"{2} | ggrep -E "^|$v" -i --color=always;
        ' \
    | gcut -z -f 1 -d $'\t' | gtr -d '\n' | gxargs -r --null $open > /dev/null 2> /dev/null
}

gプレフィックスのついているものの置き換えをそれぞれ検討する:

p () {
    local open
    open=open   # on OSX, "open" opens a pdf in preview
    ag -U -g ".pdf$" \
    | fast-p \
    | fzf --read0 --reverse -e -d $'\t'  \
        --preview-window down:80% --preview '
            v=$(echo {q} | tr " " "|"); 
            printf "%s\n%s\n" "{1}" "{2}" | egrep "^|$v" -i --color=always;
        ' \
    | perl -0 -l0 -F'\t' -nae 'print $F[0]' | tr -d '\n' | xargs -0 $open > /dev/null 2> /dev/null
}

うーんこれはいまいち分かりづらいかな。

head

head -n -1ができない。

 command -v ghead > /dev/null && HEAD="ghead" || HEAD="head"
...
        SNAPSTOREMOVE="`listsnaps \"${TEMPLATE}\" \"${DATASET}\" | $HEAD -n -1`"

最後の行だけスキップ:

        SNAPSTOREMOVE="`listsnaps \"${TEMPLATE}\" \"${DATASET}\" | perl -nle 'print if !eof'`"

hostname

hostnameの-iオプション。これはもとのコードにも問題ありそう。

export S3_ENDPOINT_URL=http://$(hostname -i):9000
hostname_i=$(perl -MSys::Hostname -MSocket -le '
    $ip = gethostbyname(hostname); print inet_ntoa($ip) if $ip;')
export S3_ENDPOINT_URL="http://${hostname_i}:9000"

readlink

readlinkの-f/-eオプション

if [ `uname` == Darwin ]; then
    READLINK=greadlink
else
    READLINK=readlink
fi

SCRIPT_FN=`$READLINK -f "$0"`

Cwd::realpathを使ってcoreutilsに依存しない可搬性のあるコードにする:

SCRIPT_FN=$(perl -MCwd=realpath -le 'print realpath shift' "$0")

これはすっきり。

realpath

realpathがPOSIXに無い。上と違って厄介なのは-m/--canonicalize-missingオプション。

node_path=$(realpath --canonicalize-missing "${2-}")

realpathが出てくるところまでトラバースして残りをディレクトリ扱いに:

node_path=$(perl -MFile::Spec::Functions=catdir,splitdir -MCwd=realpath -le '
    @p = splitdir shift;
    push @s, pop @p while !realpath catdir @p;
    print catdir realpath(catdir @p), @s;
' "${2-}")

sed

  gsed "1iID\t${gene}" ${file}ID_CpG > ${file}ID_CpG_labelled

ファイルにヘッダをつけようとしたもの。BSDのsediコマンドがないので失敗してるパターン。
エスケープが甘いのでそれも考慮する。

  perl -plse 'print "ID\t$gene" if $. == 1;' -- -gene="$gene" "${file}ID_CpG" > "${file}ID_CpG_labelled"

でもこんなことをするぐらいなら普通にcatしたほうがいいと思うけど。

( printf "ID\t%s\n" "$gene"; cat "${file}ID_CpG"; ) > "${file}ID_CpG_labelled"

sha512sum

@check("SHA512 checksum is correct")
    def check_sha512(state: State) -> R:
    return _check_sh(f"sha512sum -c {state.sha512_path}", workdir=state.release_dir)

COREモジュールの Digest::SHAshasum コマンドを同梱しているので単純に置き換える:

@check("SHA512 checksum is correct")
    def check_sha512(state: State) -> R:
    return _check_sh(f"shasum -c {state.sha512_path}", workdir=state.release_dir)

shuf

ファイルからランダムに1行だけピックしたい。

shuf -n 1 commit_messages.txt

これはperlfaq5にある通りですね。

perl -e 'srand; rand($.) < 1 && ($line = $_) while <>; print $line;' commit_messages.txt

1行だけじゃなくて複数行(N=5)とかになると若干面倒:

perl -e '
    $N = 5;
    srand;
    while (<>) {
        if ($. <= $N) { push @l, $_; next; }
        $r = rand $.; if ($r < $N) { $l[$r] = $_; }
    }
    print @l;
' commit_messages.txt

stat

stat の-cオプションが無いとのこと。lsで代替するパッチが入ってるけど、lsも環境異存だし。

docker run -it -v $(pwd):/src:z --user="$(stat -c "%u:%g" .)" "$IMAGE" \
  /src/script/fullbuild "$BOOTLOADER_COMMIT" "$FIRMWARE_COMMIT"

これを直接perlでやるとこんな感じ

docker run -it -v "$(pwd):/src:z" \
  --user="$(perl -le 'print join ":", (stat(shift))[4..5]' .)" "$IMAGE" \
  /src/script/fullbuild "$BOOTLOADER_COMMIT" "$FIRMWARE_COMMIT"

もしくは File::stat:

UID_GID=$(perl -MFile::stat=:FIELDS -le \
  'stat shift; print join "$st_uid:$st_gid";' .)

docker run -it -v "$(pwd):/src:z" --user="$UID_GID" "$IMAGE" \
  /src/script/fullbuild "$BOOTLOADER_COMMIT" "$FIRMWARE_COMMIT"

timeout

timeout 2 $fmt_running_cmd &> /dev/null

ちょうどいいのがSOにあった

perl -e 'alarm shift; exec @ARGV' 2 $fmt_running_cmd &> /dev/null

他にもありそう。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1