0

I have a names.txt file with with the top line of text being

51 Pipe-line\Closed3\00001011_-_Portfolio\UW\Old\NID50_Future_022814.xlsx

I would like to write new filenames replacing \ with /

I wrote a script.sh and invoked it using sh script.sh. My first attempt...


while read one two three; do

  new=$(echo $two|tr '\\' '/')
  echo $one
  echo $two
  echo $three
  echo $new

done < ./names.txt

51
Pipe-lineClosed300001011_-_PortfolioUWOldNID50_Future_022814.xlsx

Pipe-lineClosed300001011_-_PortfolioUWOldNID50_Future_022814.xlsx

This ate the / char. I found out passing a -r will show the / so my next attempt was

 while read -r one two three; do

  new=$(echo $two|tr '\\' '/')
  echo $one
  echo $two
  echo $three
  echo $new

done < ./names.txt

51
Pipe-line\Closed3

Pipe-line/Closed3

This eats half the filename. What is happening? How do I get this to work?

3 Answers3

4

It's not the read, but the echo.

I wrote a script.sh and invoked it using sh script.sh.

In Debian and Ubuntu, sh is dash, in which echo evaluates C-style backslash-escapes in its arguments:

$ dash -c 'foo="foo\000bar"; echo "$foo"; printf "%s\n" "$foo"' 
foo
foo\000bar

The \000 gets turned to the NUL byte, which apparently ends the string that echo outputs. Quoting the variable doesn't help here, since it only changes the processing of the shell command line before the command itself runs, and here it's the echo itself that handles the backslashes.

This is a known portability issue, described in more detail here: Why is printf better than echo?

For example, Bash's echo does the backslash-processing if given the -e argument.

The question was originally tagged with , so if you want to run the script using Bash, run it with bash script.sh, not sh script.sh.

ilkkachu
  • 138,973
  • Ah.. I updated the tags. Works now, appreciate the explanation too. – lonewarrior556 Mar 29 '17 at 15:26
  • 1
    @lonewarrior556, when you do that new=$(echo $two | tr), the string gets cut on the \000, and $new never contains the rest. The other backslashes are followed by capital letters, and aren't valid escapes, so the echo just leaves them alone. – ilkkachu Mar 29 '17 at 15:46
1

This can be accomplished in a much simpler and robust way with sed:

sed -i 's|\\|/|g' myfile

Gives:

cat myfile 51 Pipe-line/Closed3/00001011_-_Portfolio/UW/Old/NID50_Future_022814.xls

EDIT:

Following your comment, this is the way I would achieve this:

for i in $(cat test | awk '{print $2}')
do
  mv $i `sed 's|\\|/|g' <<< $i`
done
Bruno9779
  • 1,384
  • I will need to run a mv command at some point using the old filename and new file name. I still need to read the old file names from file – lonewarrior556 Mar 29 '17 at 14:54
  • -i is the "in place" flag, meaning it edits the source file. If you remove it the changed string is printed to STDOUT. Then you can just redirect it to a file. I will edit the answer. – Bruno9779 Mar 29 '17 at 14:57
  • In my example I only copied one line in a file, since I don't know what the file really contains – Bruno9779 Mar 29 '17 at 15:07
  • script.sh: 1: renametest.sh: Syntax error: redirection unexpected – lonewarrior556 Mar 29 '17 at 15:23
  • @Fox That's right, it is a typo (I copied the ooutput of sed -i and made a mistake) – Bruno9779 Mar 29 '17 at 16:54
0

Just this will suffice:

tr '\\' / < names.txt