printf

printf [-v var] format [arguments]

printf 는 builtin 명령으로 C 언어의 printf 함수와 같은 기능을 제공합니다. echo 명령의 경우 shbash 가 escape 문자를 처리하는 방식이 다른데 printf 는 그런 차이가 없습니다. printf 에서는 기본적으로 " ", ' ' 모두에서 escape 문자가 처리됩니다. ( gawk 에서와 같이 escape 문자 테이블 에 따라서 처리가 됩니다. ) quotes 의 고유 기능은 그대로 유지 되므로 " " 에서는 변수확장, 명령치환 이 됩니다.

$ printf '%s\n%s\n' foo bar      # (single quotes)
foo                              # printf 에 \n 로 전달되고 escape 처리되어 newline 출력
bar

$ printf "%s\n%s\n" foo bar      # (double quotes)
foo                              # printf 에 \n 로 전달되고 escape 처리되어 newline 출력
bar

$ printf '%s\\n%s\\n' foo bar    # printf 에 \\n 로 전달되고 \\ 는 \ 로
foo\nbar\n                       # escape 처리되므로 \n 로 출력

$ printf "%s\\n%s\\n" foo bar    # double quotes 에서는 \\ 가 \ 로 escape 되므로
foo                              # printf 에 \n 로 전달되고 escape 처리되어 newline 출력
bar
------------------------------

$ printf %s 'foo\nbar\n'         # %s 는 인수값의 escape 문자가 처리되지 않고
foo\nbar\n

$ printf %b 'foo\nbar\n'         # %b 는 escape 문자가 처리된다.
foo
bar
------------------------------

$ AA=bar

$ printf 'foo $AA\n'             # single quotes 에서는 변수 확장이 안되고
foo $AA

$ printf "foo $AA\n"             # double quotes 에서는 된다.
foo bar

문자를 출력할 때는 printf '%c' ... 형식 외에 16 진수, 8 진수 escape sequence 를 사용할 수 있습니다.

$ printf "%c %c %c\n" ABC DEF GHI
A D G

$ printf '\x41 \x42 \x43\n'      # 16 진수
A B C

$ printf '\101 \102 \103\n'      # 8 진수
A B C

-v var 옵션은 출력값을 변수에 저장합니다. (bash only)

$ printf -v IFS " \t\n"          # escape sequence 가 처리되어 저장된다.

$ echo -n "$IFS" | od -tax1
0000000  sp  ht  nl
         20  09  0a

$ foo=bar                        # indirection 도 가능
$ printf -v "$foo" "%s: %d" "Content-Length" 123

$ echo "$bar"
Content-Length: 123

Arguments

printf 는 format tags 와 그에 상응하는 인수를 이용하여 여러가지 형태로 출력할 수 있습니다. 이때 인수에 사용되는 숫자는 다음과 같은 형식을 사용할 수 있습니다.

  • N : 10 진수 (decimal) 숫자
  • 0N : 8 진수 (octal) 숫자
  • 0xN : 16 진수 (hexadecimal) 소문자 숫자
  • 0XN : 16 진수 (hexadecimal) 대문자 숫자
  • 'X or "X : X 는 character
# %d 는 인수 값을 10 진수로 표시

$ printf "%d\n" 10        # decimal
10
$ printf "%d\n" 010       # octal
8
$ printf "%d\n" 0x10      # hexadecimal
16
$ printf "%d %d\n" "'A" '"A'   # character
65 65

format tags 개수보다 인수의 개수가 많을 경우는 명령이 반복됩니다.

$ printf "< %d >" 11
< 11 >

$ printf "< %d >" 11 22 33    # %d 는 하나인데 인수는 3개 이므로 3번 반복
< 11 >< 22 >< 33 >

$ printf "< %d >\n" 11
< 11 >

$ printf "< %d >\n" 11 22 33
< 11 >
< 22 >
< 33 >

$ printf '%s\n' /bin/*
/bin/bash
/bin/brltty
/bin/btrfs
. . .

$ arr=( /usr/bin/z* )
$ printf '%s\0' "${arr[@]}" | sort -z | tr '\0' '\n'
/usr/bin/zcat
/usr/bin/zcmp
/usr/bin/zdiff
. . .

Format Tags

format 스트링에서 다음과 같이 format tag 을 구성하여 인수 값의 출력 형태를 변경할 수 있습니다.

%[flags][width][.precision]specifier

Specifier

  • %d, %i : signed decimal number 로 표시합니다.

  • %u : unsigned decimal number 로 표시합니다.

  • %o : unsigned octal number 로 표시합니다.

  • %x : unsigned hexadecimal number (소문자) 로 표시합니다.

  • %X : %x 와 같으나 대문자로 표시합니다.

$ printf "%d, %d\n" 10 -10    # signed decimal
10, -10

$ printf "%u, %u\n" 10 -10    # unsigned decimal
10, 18446744073709551606

$ printf "%o, %o\n" 10 -10    # unsigned octal
12, 1777777777777777777766

$ printf "%x, %x\n" 10 -10    # unsigned hexadecimal
a, fffffffffffffff6
  • %f : floating point number 로 표시합니다.

  • %e : scientific notation 으로 표시합니다.

  • %E : %e 와 같으나 대문자 E 를 사용합니다.

  • %g : 값에 따라 floating point number 또는 scientific notation 을 사용합니다.

  • %G : %g 와 같으나 scientific notation 에서 대문자 E 를 사용합니다.

  • %a : C99 형식의 hexadecimal floating point number 로 표시합니다. ( bash only )

  • %A : %a 와 같으나 대문자로 표시합니다. ( bash only )

$ printf '%e\n' 123            # scientific notation
1.230000e+02

$ printf '%f\n' 123            # floating point number
123.000000

$ printf '%g\n' 123
123

$ printf '%f\n' 123.4567
123.456700

$ printf '%g\n' 123.4567       # floating point number 
123.457

$ printf '%f\n' 12345678.123
12345678.123000

$ printf '%g\n' 12345678.123   # scientific notation
1.23457e+07

$ printf '%f\n' 0.0000123
0.000012

$ printf '%g\n' 0.0000123      # scientific notation
1.23e-05

$ printf '%a\n' 123.4567       # hexadecimal floating point number
0xf.6e9d495182a9931p+3

  • %s : 인수 값을 escape 문자 처리 없이 그대로 출력합니다.
  • %b : 인수 값을 escape 문자 처리하여 출력합니다.
$ printf '%s\n' 'hello\tworld'     
hello\tworld                      # escape 문자 처리 없이 그대로 출력

$ printf '%b\n' 'hello\tworld'    
hello    world                     # escape 문자를 처리하여 출력
  • %q : shell 의 input 으로 사용할수 있게 escape 하여 출력합니다.
# 'bash -c CMD' 에서 실행하고자 하는 명령이 제대로 전달되지 않는다.
$ echo 'echo -e "first\nsecond"' | xargs -I CMD bash -c CMD
firstnsecond   # \n 처리가 되지 않는다.

$ printf '%q\n' 'echo -e "first\nsecond"' 
# 실행결과: echo\ -e\ \"first\\nsecond\"

$ printf '%q\n' 'echo -e "first\nsecond"' | xargs -I CMD bash -c CMD
first
second
-----------------------------------------------------------------

$ cat commands.txt
echo -e "first\nsecond"
echo -e "third\nfourth"
echo -e "fifth\nsixth"

$ cat commands.txt |
while read -r line; do printf "%q\n" "$line"; done |
xargs --max-procs=4 -I CMD bash -c CMD
----------------------------------------------------------------

# ssh 을 통해 공백이 있는 파일이름을 touch 명령의 인수로 올바르게 전달하기 위해 
$ printf '%q ' touch "a test file" "another file"
touch a\ test\ file another\ file

sshc() {
        remote=$1; shift
        ssh "$remote" "$(printf '%q ' "$@")"
}

$ sshc user@server touch "a test file" "another file"
  • %% : % 문자를 일반 문자로 프린트할 때 사용합니다.
  • %c : 인수의 첫번째 문자를 프린트합니다.
  • %(FORMAT)T : FORMAT 에따라 date-time 을 프린트합니다.
$ printf 'foo %%%s%%\n' bar
foo %bar%

$ printf '%c %c\n' abc def
a d

$ printf 'today is %(%Y-%m-%d)T\n'
today is 2015-08-31
  • %n : 앞에서 출력된 문자수를 인수로 주어진 변수에 대입합니다. ( bash only )
$ printf '12345%n6789%n\n' num1 num2
123456789

$ echo $num1 $num2
5 9

Width

  • N : field width 를 설정합니다.
  • * : field width 값을 인수로 받을 수 있습니다.
$ printf '%d %d %d\n' 100 200 300
100 200 300
$ printf '%10d %10d %10d\n' 100 200 300         # field width 를 10 으로 설정
       100        200        300
$ printf '%*d %*d %*d\n' 10 100 15 200 20 300   # 10, 15, 20 은 각각 * 에 대응하는 값
       100             200                  300

$ printf 'DE:AD:BE:EF:%02X:%02X\n' $((RANDOM % 256)) $((RANDOM % 256))
DE:AD:BE:EF:DA:FA

# 구분선 만들기
$ printf -v sep '%*s' 50 ; echo "${sep// /-}"
--------------------------------------------------

Flags

  • - : field width 내에서 값을 left 정렬합니다. ( default 는 right 정렬입니다. )
$ printf '%10d %10d %10d\n' 100 200 300
       100        200        300
$ printf '%-10d %-10d %-10d\n' 100 200 300  
100        200        300
  • 0 : field width 에 맞게 zero padding 합니다.
$ printf '%06d\n' 12 345 6789
000012
000345
006789
  • + : 숫자에 + , - sign 기호를 붙여서 표시합니다.
$ printf '%+10d %+10d %+10d\n' 100 -200 +300
      +100       -200       +300
  • space : + 를 사용하지 않을경우 sign 자리에 space 를 두어 정렬합니다.
$ printf '%d\n' 100 -200 +300
100
-200
300
$ printf '% d\n' 100 -200 +300  
 100
-200
 300
  • ' : 1000 의 자리마다 콤마를 넣어 표시합니다. ( bash only )
$ LC_ALL=en_US.UTF-8 printf "%'d\n"  123456789
123,456,789
............................

$ bc <<< 2^64
18446744073709551616

$ LC_ALL=en_US.UTF-8 printf "%'.d\n" $( bc <<< 2^64 )      # %'.d
bash: printf: warning: 18446744073709551616: Numerical result out of range
9,223,372,036,854,775,807

$ LC_ALL=ko_KR.UTF-8 printf "%'.f\n" $( bc <<< 2^64 )      # %'.f
18,446,744,073,709,551,616
  • # : alternative format 을 사용할 수 있습니다.

%#o 은 값을 octal number 로 표시할때 앞에 0 을 붙입니다.

$ printf '%#o\n' 10
012

%#x, %#X 은 값을 hexadecimal number 로 표시할때 앞에 0x , 0X 를 붙입니다.

$ printf '%#x %#X\n' 10 30
0xa 0X1E

%#g, %#G 은 precision 내에서 trailing zero 를 붙입니다.

$ printf '%g\n' 12.34
12.34
$ printf '%#g\n' 12.34
12.3400

Precision

. 을 이용하면 왼쪽에는 field width 를 오른쪽에는 precision 을 설정할 수 있습니다.
field width 크기는 precision 을 포함합니다.

  • .N : precision 값을 설정합니다.
  • .* : precision 값을 인수로 받을 수 있습니다.
$ printf '%f\n' 123.987654321
123.987654
$ printf '%.3f\n' 123.987654321    # precision 을 3 으로 설정
123.988                            # 소수 넷째 자리에서 반올림이 된다
$ printf '%.*f\n' 3 123.987654321  # '*' 을 이용하여 precision 값을 인수로 받음
123.988

%f%g 는 precision 값을 처리하는 방식이 다릅니다.
%f 는 소수점 이후의 개수를 나타내고 %g 는 전체 유효숫자 개수를 나타냅니다.

$ printf '%.5f\n' 123.12345678  # 소수점 이후 5개
123.12346

$ printf '%.5g\n' 123.12345678  # 전체 유효숫자 5개
123.12

$ printf '%.5f\n' 0.0012345678
0.00123

$ printf '%.5g\n' 0.0012345678
0.0012346

%s 에서의 사용은 출력 문자수를 제한합니다.

$ printf '%s\n' foobarzoo
foobarzoo

$ printf '%.5s\n' foobarzoo
fooba

$ printf '%.*s\n' 5 foobarzoo
fooba

# %.s 또는 %.0s 는 해당 인수를 출력에서 제외하는 효과가 있습니다.
$ printf '%d%d%.s%.0s%d\n' 11 22 33 44 55
112255

예제 )

0 ~ 127 까지 숫자를 decimal, octal, hexadecimal 로 출력하기

$ for ((x=0; x <= 127; x++)); do
>  printf '%3d | %04o | 0x%02x\n' $x $x $x
> done
  0 | 0000 | 0x00
  1 | 0001 | 0x01
  2 | 0002 | 0x02
...
...
125 | 0175 | 0x7d
126 | 0176 | 0x7e
127 | 0177 | 0x7f

1 ~ 2 자리로 되어있는 hexadecimal number 를 2 자리로 맞추기

$ mac_addr="0:13:ce:7:7a:ad"

$ printf '%02x:%02x:%02x:%02x:%02x:%02x\n' 0x${mac_addr//:/ 0x}
00:13:ce:07:7a:ad

현재 라인 우측 끝에 메시지 출력하기
tput cols 은 현재 터미널 columns 수를 출력. echo $COLUMNS 과 동일

bash$ printf '%*s\n' $COLUMNS "hello world"

sh$ printf '%*s\n' $(tput cols) "hello world"

Quiz

스트링 반복 하기

$ len=50
$ printf -v dash "%${len}s"
$ echo "${dash// /-}"
--------------------------------------------------

bash$ printf '%.s=' {1..50}
==================================================

sh$ printf '%.sAB' $(seq 25)
ABABABABABABABABABABABABABABABABABABABABABABABABAB

$ printf '%.sHello Printf\n' {1..5}
Hello Printf
Hello Printf
Hello Printf
Hello Printf
Hello Printf

2.

format 에서 사용되는 % 특수 문자를 일반 문자로 사용하려면 어떻게 할까요?

%% 를 사용하면 됩니다.

$ printf '%.s $((RANDOM % 10))' {1..10}
bash: printf: ')': invalid format character      # 오류
 $((RANDOM 

$ printf '%.s $((RANDOM %% 10))' {1..10}         # 또는 \045, \x25
 $((RANDOM % 10)) $((RANDOM % 10)) $((RANDOM % 10)) $((RANDOM % 10)) $((RANDOM % 10)) 
 $((RANDOM % 10)) $((RANDOM % 10)) $((RANDOM % 10)) $((RANDOM % 10)) $((RANDOM % 10))

$ eval echo $( printf '%.s $((RANDOM %% 10))' {1..30} )
3 7 7 0 1 2 0 4 0 1 1 2 8 3 2 4 3 3 6 1 5 6 6 1 1 2 4 0 1 7

3.

쉘에서는 echo 명령으로 -n 스트링을 프린트할 수가 없습니다. 왜냐하면 -n 을 옵션으로 인식하기 때문인데요. 어떻게 프린트할 수 있을까요?

$ echo "-n"
$                  # -n 가 표시되지 않는다.
$ echo -- "-n"
-- -n

# printf 명령을 이용하면 됩니다.
$ printf -- -n 
-n