2

I am writing a java program from terminal using printf and redirecting its output to a .java file but printf fails to interpret the horizontal backslash tab (\t), and when I have an exclamation mark (!) in the string, it doesn't even print and fails with this error:

bash: !": event not found

  • How do I force printf to include a horizontal tab?

  • And how do I include an exclamation without getting the above error?

commandline argument:

$ printf "%s\n" "public class {" "\tpublic static void main(String[] args) {" "dogBark()" "}" "public static void dogBark() {" "System.out.println("Woof")" "}" "}" > barkingDog.java

output from .java file

$ less barkingDog.java
public class {
\tpublic static void main(String[] args) {
dogBark()
}
public static void dogBark() {
System.out.println(Woof)
}

}
bit
  • 1,076
  • 2
  • 17
  • 36

3 Answers3

7

Using a here-document would probably be cleaner and easier to maintain than using printf. However, from the bash shell's built-in help printf

 %b   expand backslash escape sequences in the corresponding argument

and to prevent bash from treating ! as a history expansion, single-quote your strings:

$ printf '%b\n' 'public class {' '\tpublic static void main(String[] args) {' 'dogBark()' '}' 'public static void dogBark() {' 'System.out.println("Woof!")' '}' '}'
public class {
    public static void main(String[] args) {
dogBark()
}
public static void dogBark() {
System.out.println("Woof!")
}
}

See How to echo a bang!

steeldriver
  • 78,509
  • 12
  • 109
  • 152
  • @Jesse yes it does work. I didn't see that he replaced the s variable with a b. – bit Jul 07 '18 at 20:33
  • I'm using single quotes around `Woof!` and double for everything else but the error still persists. Does it have to be the other way around? – bit Jul 07 '18 at 20:39
  • 1
    @MyWrathAcademia: The string `'Woof!'` is wrapped within double quotes, which will escape the single quotes. So yes, try the other way around. – jesse_b Jul 07 '18 at 20:42
  • @steeldriver why is it that changing the `s` after `%` to a `b` caused `printf` to start interpreting the horizontal tab `\t` when previously it wasn't? – bit Jul 09 '18 at 20:33
  • @MyWrathAcademia because that's what it's designed to do - format the argument as a **b**ackslash escaped string instead of a regular **s**tring – steeldriver Jul 09 '18 at 20:44
3

printf can not interpret backslash characters (like \t) in the string printed. Only in the format string:

$ printf '\t%s' "test" "string"
    test    string

As a workaround you can insert a real tab in the string to print.

It is possible to use a format string of %b which could interpret backslash characters:

$ printf '%b' "\ttest" "\tstring"
    test    string

The character ! is used for history expansion. But that happens only when used unquoted. Just quoting it (with \ or ' not ") should prevent expansion. If that is still a problem, history expansion could be disabled:

$ set +H

This should work:

$ printf "%b\n" \
         "public class {" \
         "\tpublic static void main(String[] args) {" \
         "dogBark()" \
         "}" \
         "public static void dogBark() {" \
         "System.out.println(\"Woof\!\")" \
         "}" \
         "}" > barkingDog.java

$ cat barkingDog.java 
public class {
        public static void main(String[] args) {
dogBark()
}
public static void dogBark() {
System.out.println("Woof!")
}
}

But clearly, a here document is simpler:

$ cat <<\EOT >barkingDog.java
public class {
       public static void main(String[] args) {
dogBark()
}
public static void dogBark() {
System.out.println("Woof!")
}
}
EOT
  • `%b` is part of the POSIX sh specification; it is not a shell-specific extension. See the APPLICATION USAGE section of [the POSIX `echo` spec](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html) explicitly advising use of `printf '%b'` instead of using backslashes in `echo` arguments, and [the POSIX `printf` spec](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html); see number 7 in the list of exceptions, starting with the text *An additional conversion specifier character, `b`, shall be supported as follows*. – Charles Duffy Jul 08 '18 at 00:09
  • In bash: [Only ‘\’ and ‘'’ may be used to escape the history expansion character, but the history expansion character is also treated as quoted if it immediately precedes the closing double quote in a double-quoted string.](https://www.gnu.org/software/bash/manual/html_node/History-Interaction.html#History-Interaction) Also you don't have the doublequotes around Woof in the output. – dave_thompson_085 Jul 08 '18 at 00:11
  • @CharlesDuffy I stand corrected. Answer updated, thanks. –  Jul 08 '18 at 00:18
  • @dave_thompson_085 Yes, you are correct, however, in this case, the `!` will not be before a closing `"`. If using double quotes, a `\"` is required before the closing double quote, if using `'`, there is no closing `"`. Answer updated anyway. –  Jul 08 '18 at 00:26
0

I think you can accomplish what you want using ANSI-C quoting like so:

printf "%s\n" "public class {" \
$'\tpublic static void main(String[] args) {' \
"dogBark()" \
"}" \
"public static void dogBark() {" \
"System.out.println("Woof")" \
"}" \
"}" > barkingDog.java

Output:

public class {
        public static void main(String[] args) {
dogBark()
}
public static void dogBark() {
System.out.println(Woof)
}
}

As for the exclamation mark, when history expansion is enabled bash will attempt to expand ! when inside double quotes. So single quote it or escape it with \.

jesse_b
  • 35,934
  • 12
  • 91
  • 140