printf

printf [-v var] format [arguments]

printf 에서는 기본적으로 " ", ' ' 모두에서 escape 문자가 처리됩니다. escape 문자가 처리되지 않게 하려면 따로 %s format 을 사용합니다. echo 명령의 경우 shbash 가 escape 문자를 처리하는 방식이 다른데 printf 는 그런 차이가 없습니다. quotes 의 고유 기능은 그대로 유지 되므로 " " 에서는 변수확장, 명령치환 이 됩니다.

$ AA=world

# double quotes
$ printf "hello\t$AA\n"    # \t \n escape 문자가 처리되고 변수확장이 된다.
hello    world

# single quotes
$ printf 'hello\t$AA\n'    # \t \n escape 문자만 처리된다.
hello    $AA

16 진수, 8 진수로 문자를 출력할때는 printf "%c" ... 형식으로 하지않습니다.

$ printf "hello \x41\x42\x43\n"     # 16 진수
hello ABC

$ printf "hello \101\102\103\n"     # 8 진수
hello ABC

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

$ printf -v IFS " \t\n"

Arguments

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

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

$ printf "%d\n" 10        # decimal
10
$ printf "%d\n" 010       # octal
8
$ printf "%d\n" 0x10      # hexadecimal
16
$ printf "%d\n" "'A"      # a character
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 >

$ args() { 
        echo COUNT: $#
        printf "ARG: %s\n" "$@"
}

$ args 11 22 33
COUNT: 3
ARG: 11
ARG: 22
ARG: 33

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 CMD; do printf "%q\n" "$CMD"; done |
xargs --max-procs=3 -I CMD bash -c CMD

----------------------------------------------------------------
# 공백이 있는 파일이름을 touch 명령의 인수로 올바르게 전달하기 위해 
sshc() {
        remote=$1; shift
        ssh "$remote" "$(printf "%q " "$@")"
}

$ printf "%q " touch "a test file" "another file"
touch a\ test\ file another\ file

$ 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 -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 )
$ printf "%'d\n"  123456789
123,456,789
  • # : 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 or %.0s 는 해당 인수를 출력에서 제외하는 효과를 갖습니다.

$ printf "%5d%5d%.s%.0s%5d\n" 11 22 33 44 55
   11   22   55

예제 )

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 과 동일

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

Quiz

---------- horizontal line 만들기

bash$ printf '%.s-' {1..50}; echo
--------------------------------------------------

sh$ printf '%.s-' $(seq 50 | xargs); echo
--------------------------------------------------

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

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

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