qmastery
qmastery

Reputation: 87

printf is "eating" the backslashes. How to preserve them?

I'm trying to make a tiny POSIX-compatible sed-like function. However I noticed that the code below is "eating" the backslashes.

input.txt for mysed ./input.txt "TEST2" "TEST2=\"two\"" call :

TEST1="\\_PR.CP%02d"
TEST2="one"
TEST3="\\_PR.P%03d"

Expected:

TEST1="\\_PR.CP%02d"
TEST2="two"
TEST3="\\_PR.P%03d"

Received:

TEST1="\_PR.CP%02d"
TEST2="two"
TEST3="\_PR.P%03d"

How to modify the code below to preserve all the backslashes? Maybe it's possible to replace a custom printf '%b\n' format by something better?

#!/bin/sh
# Replaces the lines containing the text '$2' of a file '$1' with a line '$3'.
mysed () {
    if [ -f "$1" ] ; then
        mysed_tmp="./.mysed"
        rm -f $mysed_tmp
        while IFS= read -r line
        do
            case $line in
                *"$2"*)
                    if [ ! -z "$3" ] ; then
                        printf '%b\n' "$3" >> $mysed_tmp
                    fi
                    ;;
                *)
                    printf '%b\n' "$line" >> $mysed_tmp
                    ;;
            esac
        done < "$1"
        mv $mysed_tmp $1
        return 0
    else
        return 1
    fi
}

echo "TEST1=\"\\\\_PR.CP%02d\"" > input.txt
echo "TEST2=\"one\"" >> input.txt
echo "TEST3=\"\\\\_PR.P%03d\"" >> input.txt

mysed ./input.txt "TEST2" "TEST2=\"two\""
cat ./input.txt

EDIT: replaced '%b\n' with '%s\n', everything is working now as intended.

Upvotes: 1

Views: 1045

Answers (1)

Ed Morton
Ed Morton

Reputation: 203359

Look:

$ x="TEST2=\"two\""; printf '%s\n' "$x"
TEST2="two"

$ x='TEST2=\"two\"'; printf '%s\n' "$x"
TEST2=\"two\"

$ TEST1="\\_PR.CP%02d"; printf '%s\n' "$TEST1"
\_PR.CP%02d

$ TEST1='\\_PR.CP%02d'; printf '%s\n' "$TEST1"
\\_PR.CP%02d

Hopefully this will make things clearer:

$ foo() { printf '%%b:<%b>\n%%s:<%s>\n---\n' "$1" "$1"; }

$ foo "TEST2=\\"two\\""
%b:<TEST2=      wo\>
%s:<TEST2=\two\>
---

$ foo 'TEST2=\\"two\\"'
%b:<TEST2=\"two\">
%s:<TEST2=\\"two\\">
---

So:

  1. If you don't want backslashes stripped by the shell when the string is defined then don't tell the shell to interpret them - use ' not ".
  2. If you don't want backslashes stripped by printf when the string is printed then don't tell printf to interpret them - use %s not %b.

Upvotes: 2

Related Questions