FC2ブログ

本を読む

読書やコンピュータなどに関するメモ

UbuntuのconvertでPDFに変換するには設定変更が必要

 フリーランスで仕事をしていると「書類をプリントアウトして押印し、スキャンして提出」という作業が求められることがよくあります。

 JPEGなどの画像ファイルからPDFに変換するとき、ImageMagickのconvertコマンドを使うと、コマンドラインから一発で変換できます。JPEGからA4のPDFを作るには、こんな感じで。

$ convert -density 72 -page a4 shorui.jpg shorui.pdf

 ただし、最近のUbuntuのconvertでは、セキュリティ上の理由から、デフォルトではPDFの変換が許可されていません。そのため、そのまま実行するとエラーになります。

convert: not authorized `shorui.pdf' @ error/constitute.c/WriteImage/1028

 そこで、設定を変更します。/etc/ImageMagick-6/policy.xmlファイルを、管理者権限のテキストエディタで開きます。

$ sudo vi /etc/ImageMagick-6/policy.xml

 ここで、PDFの設定の箇所を見つけて、以下のように書き換えて保存します。

  <policy domain="coder" rights="none" pattern="PDF" />
            ↓
  <policy domain="coder" rights="read|write" pattern="PDF" />

 これで、変換が実行できるようになりました。

参考:convert - Error during converting jpg to pdf - Ask Ubuntu

lessで.odtや.docxを表示できるようにする

 TwitterでLibreOffice日本語チームが、ODFを「soffice --cat」でテキスト表示するというワザを紹介していました。

 --convert-toとかは知っていましたが、--catは知りませんでした。

 さっそくlessコマンドに仕込んでみます。

 多くのLinuxディストロではデフォルト設定で、lessにlesspipeを組み合わせるようになっています。たとえばlessに.pdfが与えられたらテキストにしてlessで表示、.zipや.tar.xzが与えられたらファイル一覧をlessで表示、という具合です。

 このlesspipeを個人的に拡張するには、~/.lessfilterというシェルスクリプトのファイルを作り、フィルター処理を書きます。

case `echo "$1" | tr '[:upper:]' '[:lower:]'` in
        *.odt|*.docx|*.doc)
                if [ -x "$(command -v soffice)" ]; then soffice --cat "$1";
                else echo "No LibreOffice or OpenOffice available"; fi ;;
        *)
                exit 1;;
esac

exit 0

 目的のファイル名だったら処理してexit 0することでlesspipe処理を終了、それ以外のファイル名だったらexit 1することで通常のlesspipe処理にフォールバック、という内容です。lesspipeの内容を参考にして(枠組をコピペして)書きました。

 ~/.lessfilterには実行権をつけておきます。

$ chmod +x ~/.lessfilter

 これで、メールで送られてきた.docxファイルも、さっとlessで確認できるようになりました。

$ less foo.docx

 めでたしめでたし。

ThinkPad+Ubuntuで、サスペンドから戻るとウィンドウがリサイズされる

 去年からThinkPad T470sを使い始め、最初からUbuntu 18.04を入れています。満足しているのですが、1つ問題があって、サスペンド状態からリジュームすると画面の各ウィンドウがリサイズされて画面左上に集まってしまうという症状をくらっていました。

 見た感じとしては、リジュームした直後に画面のHiDPIスケーリングが一度200%ぐらいになって、また元の設定になるような感じです。

 Ubuntuのlaunchpad(イシュートラッカー)にもBug #1726548 “Windows resized after resume”というスレッドが立っていて、同様の症状が次々寄せられているようでした。

 このスレッドで今月、Ask Ubuntuの18.04 - Preventing Window resizing after resume - Ask Ubuntuという質問と回答が紹介されていました。ここで書かれていたのは、GNOMEの自動スケーリング調整をオフにするという方法です。

 設定はgsettingsで確認できます。

$ gsettings get org.gnome.desktop.interface scaling-factor

 結果が「uint32 0」になっていると、GNOMEの自動スケーリングがオンになっています。

 オフにする場合もgsettingsから。

$ gsettings set org.gnome.desktop.interface scaling-factor 1

 私の場合は自動スケーリングを使っていないので、これで問題を回避できました。

Bash 5.0の新機能:namerefの挙動の変更

Qiitaと同時投稿です

Bash 5.0では、namerefの名前解決で非互換の挙動があると言われています。

リリースアナウンスでは次のように書かれています。

There are a few incompatible changes between bash-4.4 and bash-5.0. The changes to how nameref variables are resolved means that some uses of namerefs will behave differently, though I have tried to minimize the compatibility issues.

「bash 5.0のNEWSファイル私訳」にも次のようにあります。

  1. 関数内でnamerefの名前解決のループは、グローバルスコープでの名前の変数 に解決されるようになりました。

そもそもnamerefを知らないと変更点もわからないので、順に説明します。

おさらい①:namerefって?

namarefはBash 4.3から導入された機能です。ksh由来のようで、AIXのマニュアルでは「名前参照」と、Solarisのマニュアルでは「nameref」と表記されているようです。

namerefは、ほかの変数を参照する変数を作る機能です。変数のエイリアス(別名)を付けるときなどに使います。

namerefはdeclare -nlocal -nなど、変数宣言のときに-nオプションを指定して作ります。namerefな変数に、ほかの変数名を文字列として代入すると、エイリアスのように動きます。

$ aaa=3                 # 変数aaaに3を代入
$ declare -n bbb        # namerefな変数bbbを宣言
$ bbb=aaa               # bbbに'aaa'を代入($aaaではない)
$ echo $bbb             # bbbの内容は?
3

おさらい②:namerefの使い道

namerefの使い道として自分が考えたのが、関数から関数に値を渡すときに、変数名を渡す方法です。

たとえば、関数から関数に配列を渡すときには、引数や標準入力ではそのままでは渡せません。そこでダイナミックスコープを使って渡す方法があります。

foo() {
  local ary=('a b' c d)         # 配列変数aryを宣言
  bar
}

bar() {
  echo ${ary[0]}                # fooで宣言された配列変数aryを参照
}

foo                             # 「a b」と表示される

ただbarから暗黙的に変数aryを参照するのがいまいちです。そこで、namerefを使って変数名を渡すことで、barではfooで宣言された変数名の知識が不要になります。

foo() {
  local ary=('a b' c d)
  bar ary               # “ary”という変数名を文字列で引数に
}

bar() {
  local -n var=$1       # nameref変数varに1つめの引数の文字列(変数名)を代入
  echo ${var[0]}
}

foo                      # 「a b」と表示される

namerefの挙動の違い

さて、Bash 5.0で挙動が変わったのはどのあたりでしょうか。

「関数内でのnamerefの名前解決のループ(A nameref name resolution loop in a function)」ということなので、namerefで循環参照を作ってみます。

aaa=7                           # グローバル変数aaa

foo() {
  local -n aaa                  # ローカルなnameref変数aaa
  aaa=aaa                       # aaaからaaa自身を参照
  echo $aaa
}

foo

これをBash 4.4で実行すると、循環参照の警告が表示されたあと、echo $aaaでは何も表示されません。

$ bash ./n.sh
./n.sh: line 6: warning: aaa: circular name reference

Bash 5.0で実行すると、循環参照の警告が表示されたあと、echo $aaaでグローバル変数aaaの値である「7」が表示されます。

$ ./bash ./n.sh
./n.sh: line 6: warning: aaa: circular name reference
7

結論

そもそも、Bashでnamerefをそれほど使うとは思わないし、その中でも特殊なケースなので、非互換による影響はほとんどないのではないでしょうか。

Bash 5.0の新機能:カーソルの画面行移動

Qiitaと同時投稿です

Bash 5.0のNEWSファイルをさくっと翻訳したエントリー「bash 5.0のNEWSファイル私訳」を先日書きました。

この中のReadline 8.0の新機能として、カーソルの画面行移動機能がありました。

b. キーに割り当てられるコマンドに新しく`next-screen-line' and
   `previous-screen-line'があります。カーソルを画面行のそれぞれ次と前
   の行の同じカラムに移動します。

さっそく試してみましょう。

Bash 5.0のコマンドラインから、Ctrl-↓にnext-screen-lineを割り当てます。

$ bind '"\e[1;5B": next-screen-line'

同様にCtrl-↑にprevious-screen-lineを割り当てます。

$ bind '"\e[1;5A": previous-screen-line'

コマンドラインで、1行が1画面行を超えるように文字を入力します。

$ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

ここでCtrl-↓とCtrl-↑を押すとカーソルが画面行を移動することがわかります。

Bash 5.0の新機能:localvar_inherit

Qiitaと同時投稿です

Bash 5.0のNEWSファイルをさくっと翻訳したエントリー「bash 5.0のNEWSファイル私訳」を先日書きました。

この中に、localvar_inheritという新しいshoptオプションが追加されたことが書かれています。

o. 新しいshoptオプション:localvar_inherit。設定すると、ローカル変数
   は、直前のスコープにある同じ名前の変数の値を継承します。

自分で訳しておいてなんですが、わかりにくいですね。

Bash 5.0のmanpageでは、こう説明されています。

localvar_inherit
        If set, local variables inherit the value and attributes
        of a variable of the same name that exists at a previous
        scope before any new value  is  assigned.   The  nameref
        attribute is not inherited.

やはりわかりにくいですね。

そこで、ちょこっと調べて試してみた結果を、順を追って説明します。

おさらい①:関数

多くのプログラミング言語と同じく、Bashでも関数を定義できます。定義した関数はコマンドとして実行できます。

$ cat hello.sh
hello() {               # helloという関数を定義
    echo hello
}

hello                   # helloを実行
$ bash ./hello.sh
hello

おさらい②:ローカル変数

単純なシェルでは変数はグローバル変数しかありません。しかしBashでは、localコマンドによって関数内のローカル変数を定義できます。

$ cat localvar.sh
foo() {
  local aaa     # aaaというローカル変数を定義
  aaa=5         # aaaに5を代入
}

aaa=3           # aaaに3を代入
foo             # fooを実行
echo $aaa       # その後でaaaの値は?
$ bash ./localvar.sh
3

おさらい③:ローカル変数はダイナミックスコープ

Bashのローカル変数のスコープは、呼び出し先の関数でも有効です。Common Lispのスペシャル変数や、Emacs Lispのデフォルトのダイナミックスコープ変数、Perlのlocalなどに似ています。

次のように、関数fooと、fooから呼び出す関数barを定義します。すると、fooで定義したローカル変数aaaがbarからも参照できます。

$ cat scope.sh
foo() {
  local aaa=3   # aaaというローカル変数を定義して値を3に
  bar
}

bar() {
  echo $aaa     # barでのaaaの値は?
}

foo
$ bash ./scope.sh
3

同じ変数aaaを参照しているので、barで値を変更すると、fooでも変更されています。

$ cat scope2.sh
foo() {
  local aaa=3   # aaaというローカル変数を定義して値を3に
  bar
  echo $aaa     # barを呼び出した後でaaaの値は?
}

bar() {
  aaa=5         # barでaaaに5を代入
}

foo
$ bash ./scope2.sh
5

ようやく本題:localvar_inherit

では、Bash 5.0の新機能であるlocalvar_inheritを見てみましょう。

localvar_inheritをオンにすると、呼び出し元の変数と同じ名前の変数をlocalで定義したときに、呼び出し元の変数の値と属性が コピー されます。

次の例では、fooとbarで同じ名前の変数aaaを定義しています。barではaaaの値を代入していないのに、fooでaaaに設定した値が参照されます。

$ cat inherit.sh
shopt -s localvar_inherit       # localvar_inheritをオンに

foo() {
  local aaa=3   # fooでローカル変数aaaを定義
  bar
}

bar() {
  local aaa     # barでもaaaを定義。値は代入しない
  echo $aaa     # aaaの値は?
}

foo
$ bash ./inherit.sh
3

ダイナミックスコープと違い、同じ変数を参照するわけではありません。次のように、barでaaaの値を変更しても、fooのaaaの値は変わりません。

$ cat inherit.sh
shopt -s localvar_inherit

foo() {
  local aaa=3
  bar
  echo $aaa     # barを呼び出した後でaaaの値は?
}

bar() {
  local aaa
  aaa=5         # barでaaaに5を代入
}

foo
$ bash ./inherit.sh
3

用途

さて、これをどういうところで使うと便利でしょうか。

普通の変数の値であれば、引数などで渡せば十分です。ただし、配列は引数などでは渡しづらいので、localvar_inheritを使って呼び出し先にコピーするのが便利そうです。

bash 5.0のNEWSファイル私訳

Qiitaと同時投稿です

bash 5.0(とreadline 8.0)がリリースされたので、NEWSファイル(新機能リスト)からbash 5.0の新機能の部分を訳してみました。

 | HOME |  »

Categories

Recent Entries

Recent Comments

Recent Trackbacks

Appendix

emasaka

emasaka

フリーター。
連絡先はこのへん

Monthly