お知らせ

  • 利用規約を守って投稿してください。また、よくある質問および投稿の手引きも参照してください。
  • メッセージの投稿にはアカウントが必要です。未登録の方は、ユーザ登録ページからアカウントを作成することができます。

#1 2011-10-08 12:48:14

katsu07
ゲスト

bashスクリプトでのprintfの挙動

初歩的な感があるのでこちらで良いか解らないのですが、質問させてください。
使用しているubuntuは 10.10 desktop remixです。
以下のサンプルスクリプトをターミナルで実行した場合、ひと手間しないと桁揃えがうまくいきません。

サンプルスクリプト
#!/bin/bash

nihongo=(日 日本 日本語 日本語で 日本語です)
hankaku1=(aaaaa bbbbb ccccc ddddd eeeee)
hanzen=(fあfff gいggg hうhhh iえiii jおjjj)
hankaku2=(kkkkk lllll mmmmm nnnnn ooooo)

# 全角スペース("4個" "3個" "2個" "1個")
_space_=("    " "   " "  " " ")

# 全角を2byteとして桁揃え(Shift_JIS)
for i in `seq 0 5`
    do
    printf "%-10s %5s %6s %5s\n" ${nihongo[$i]} ${hankaku1[$i]} ${hanzen[$i]} \
                                        ${hankaku2[$i]}
    done

# 全角を3byteとして桁揃え(UTF-8)
for i in `seq 0 5`
    do
    printf "%-15s %5s %7s %5s\n" ${nihongo[$i]} ${hankaku1[$i]} ${hanzen[$i]} \
                                        ${hankaku2[$i]}
    done

# 桁揃えするには、全角を3byteとして文字の最大数まで全角スペースを入れる。
for i in `seq 0 5`
    do
    printf "%-15s %5s %7s %5s\n" ${nihongo[$i]}${_space_[$i]} ${hankaku1[$i]} \
                                ${hanzen[$i]} ${hankaku2[$i]}
    done


結果は以下の様になります。
日        aaaaa fあfff kkkkk
日本     bbbbb gいggg lllll
日本語  ccccc hうhhh mmmmm
日本語で ddddd iえiii nnnnn
日本語です eeeee jおjjj ooooo
                             
日             aaaaa fあfff kkkkk
日本          bbbbb gいggg lllll
日本語       ccccc hうhhh mmmmm
日本語で    ddddd iえiii nnnnn
日本語です eeeee jおjjj ooooo
                                   
日     aaaaa fあfff kkkkk
日本    bbbbb gいggg lllll
日本語   ccccc hうhhh mmmmm
日本語で  ddddd iえiii nnnnn
日本語です eeeee jおjjj ooooo

表示文字幅の関係で乱れてるかもしれませんが、3つ目の出力の様に、全角スペースを入れる等しないと日本語の文字数が変動する
値を、正常に桁揃えする事ができません。

これはprintfの正常な挙動なのでしょうか?

 

#2 2011-10-08 21:39:14

emasaka
メンバ
登録日: 2008-02-14

Re: bashスクリプトでのprintfの挙動

man printfを見ると「解釈のしかたは C の printf(3) 関数と同じ」と書かれており、Cのprintf()関数でも同様の結果となります。

$ cat foo.c
#include <stdio.h>

int main()
{
    char *strs[] = {"あ", "ああ", "あああ"};
    int i;
    for (i = 0; i < 3; i++) {
        printf("%10s#\n", strs[i]);
    }
}
$ cc foo.c
$ ./a.out
       あ#
    ああ#
あああ#

そのほかのプログラミング言語でも、だいたい同じですね。
簡単にいうと、3バイトの文字を2桁幅で表示しているためのずれです。

マルチバイト文字を2桁幅として(本当はそうとは限りませんが)桁を揃えるには、以下のようなロジックになるかと思います。

$ cat hoge.sh
#!/bin/bash

strpad() {
    local clen=${#3}
    local LANG=C
    local blen=${#3}
    local -i pad="$2 - (clen + (blen - clen) / 2)"
    if ((pad > 0)); then
        printf -v $1 "%s%*s" $3 $pad ' '
    else
        printf -v $1 "%s" $3
    fi
}

for s in ああ あああああ あa; do
    strpad s1 10 $s
    echo "$s1#"
done

$ bash ./hoge.sh
ああ      #
あああああ#
あa       #

オフライン

 

#3 2011-10-08 23:17:24

katsu07
ゲスト

Re: bashスクリプトでのprintfの挙動

返信ありがとうございます。
やはり3byteの文字を2桁表示で扱っているのが原因だったんですね。
疑問が解決してスッキリしました。
又、サンプルスクリプトまで記述して頂き、こうやるのかと大変参考になりました。
ありがとうございます。

 

Board footer

Powered by FluxBB