web-dev-qa-db-ja.com

printfまたはechoを使用して文字列の前に先行文字を追加する

先頭文字を追加して文字列を特定の長さにする方法を教えてください。

文字列が20文字より短い場合、文字列の前にゼロを追加したいとします。

printf '%020s\n' "$line"

ただし、これは文字列を先頭の空白で埋めますが、それ以外のものは埋めません。ゼロ。

文字列は、数字やその他の文字を含むランダムな文字列にすることができます。

文字列をprintf '%020i' $numberまたはprintf '%020d $number'の数値+休符に分割したくない。

私の現在の出力は:

         shortstring

私の望ましい出力は:

000000000shortstring
6
polym

Perlを使用できる場合:

$ Perl -e 'print sprintf "%020s\n","shortstring"'
000000000shortstring

より一般的な場合:

$ Perl -e 'print sprintf "%020s\n",shift' shortstring
000000000shortstring

$ Perl -e 'print sprintf "%020s\n", $_ for @ARGV' shortstring qwerty
000000000shortstring
00000000000000qwerty

一部のプラットフォームでは、少なくともAIXでは、先頭に0を付けて出力文字列をフォーマットできます。 GNU gcc、コンパイルすると警告が表示されます:

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

int main() {
    printf("%08s\n", "qwerty");
    return 0;
}

次に:

$ gcc -Wall test.c
test.c: In function ‘main’:
test.c:4:5: warning: '0' flag used with ‘%s’ gnu_printf format [-Wformat]

Perlを使用したくない場合は、次の方法を試してください。

$ line=shortstring
$ printf "%0$((20-${#line}))d%s"  0  $line
000000000shortstring

このアプローチでは、文字列長が20を超える場合は処理できません。@ mikeserv回答を使用して処理するか、printfの前にチェックを使用できます。

1
cuonglm

@Gnoucがすでに述べたように、あなたはすでに途中にいます:

_line='some chars'
printf '%0'$((20-${#line}))'d%s\n' 0 "$line"

###OUTPUT###
0000000000some chars
_

もちろん、行が20文字より長くても、0で終了します。それはifprintfが正しくないため、エラーで失敗しません。 _-_ dashをそのフォーマット文字列で処理します(これは明らかに最も可能性が高いケースです)。それでも、それは簡単に処理されます:

_printf "%.$((((l=20-${#line})>0)*l))d%s\n" 0 "$line"
_

そのフォーマット文字列は、printfの-​​_%._ precisionフラグを使用して、指定された長さまで10進引数にゼロを埋め込むか、_%.0d_ nullまたは_%.d_空の精度-先行する0を指定された数に切り捨てます。このような:

_printf '#%.0d%c%.5d' 0000000 ':separator' 4 
#:00004
_

(注:_%c_形式は、関連する引数の最初の文字を出力します)

_$((_ arithmetic expression _))_は最初に変数_$l_を20マイナス_${#line}_に設定し、次に_$l_がそれぞれ0未満または0より大きい場合、0または1に評価します。最後に_$l_を乗算します。したがって、20-_${#line}_がゼロより小さい場合は$((0*l))を実行しますが、それより大きい場合は$((1*l))を実行します。

このようにしてalwaysは、20と_${#line}_の差として数えられる数の先行ゼロのみを出力します。このような:

_line='some chars'
printf "#%.$((((l=20-${#line})>0)*l))d%s\n" 0 "$line"
#0000000000some chars
_

そして...

_line='some chars'
printf "#%.$((((l=4-${#line})>0)*l))d%s\n" 0 "$line"
#some chars
_

また、sタイプフィールドにも同じ_%._ precision形式を使用できます。

_line='some chars'
printf "#%.$((((l=4-${#line})>0)*l))d%.4s\n" 0 "$line"
#some
_

...必要に応じて切り捨てます。

ここではループになっています-以下は5回繰り返されます:

_line=
until line=fivec$line; [ $((l)) -lt 0 ] 
do  printf "#%.$((((l=20-${#line})>0)*l))d%s\n" 0 "$line"
done    
#000000000000000fivec
#0000000000fivecfivec
#00000fivecfivecfivec
#fivecfivecfivecfivec
#fivecfivecfivecfivecfivec
_

表示されているゼロがシェル変数の値に含まれることはなく、_$line_はもちろん、printfによってのみ自動的に生成されることに注意してください。

以下に、追加された精度_%.20s_を使用した同じループを示します。

_line=
until line=fivec$line; [ $((l)) -lt 0 ] 
do  printf "#%.$((((l=20-${#line})>0)*l))d%.20s\n" 0 "$line"
done    
#000000000000000fivec
#0000000000fivecfivec
#00000fivecfivecfivec
#fivecfivecfivecfivec
#fivecfivecfivecfivec
_

これでできるもう1つのことは、とても楽しいことです。動的に0を生成するprintfの機能と_$IFS_を組み合わせると、次のように必要なだけ文字列を何回でも使用できます。

_IFS=0 ; printf '10 lines\n%s' $(printf %010d) ; unset IFS 

###OUTPUT###
10 lines
10 lines
10 lines
10 lines
10 lines
10 lines
10 lines
10 lines
10 lines
10 lines
_
3
mikeserv

%sは文字列フォーマット仕様です。ゼロ埋め込みは 数値変換のために存在するように定義されています

d、i、o、u、x、X、a、A、e、E、f、F、g、およびG変換指定子の場合、先頭のゼロ(符号またはベースの指示に続く)を使用してフィールド幅にパディングします...その他の変換の場合、動作は未定義です

もしあなたの $lineは数値です。以下を使用します。

printf '%020i\n' $line

必要な結果(または他の数値フォーマット指定子の1つ)を取得します。値がより複雑であるが数値で始まる場合は、以下を使用できます。

printf '%020i %s\n' $num $restofline

数値と文字列を一度に組み合わせる。

3
Michael Homer

手動でパディングするだけです。 POSIXツールに固執するならば、もっと簡単な解決策はないと思います。

while [ ${#line} -lt 20 ]; do
  line=0$line
done

または

n=${#line}
while [ $n -lt 20 ]; do
  printf '0'
  n=$((n-1))
done
printf "$line\n"

もちろん、zshにはそのための構文があります。上記のスニペットとは異なり、これは20文字より長い場合、文字列を切り捨てます。切り捨てたくない場合は、文字列の末尾を追加できます。

print -r ${(l:20::0:)line}
print -r ${(l:20::0:)line}${line:20}

別のprintf0パディング%0Xd必要なときに。

zero_pad(){
  # zero_pad <string> <length>
  [ ${#1} -lt $2 ] && printf "%0$(($2-${#1}))d" ''
  printf "%s\n" "$1"
}

$ zero_pad "" 5
00000

$ zero_pad "over" 5
0over

$ zero_pad "under" 4
under

$ zero_pad "exact" 5
exact

$ zero_pad " space" 7
0 space
1
Matt