for loop / unix2dos to clean a group of files with specific extension

592 views Asked by At

I am trying to use unix2dos on a group of C++ source code files. Basically, unix2dos converts LF to CRLF.

I could simply do the following, and it does what I want :

#!/bin/sh
find . -type f \( -name "*.h" -o -name "*.cpp" \) -exec unix2dos {}\;

but I don't want the file to be modified if it has CRLF end of lines already. That's why I have to modify the script.

#!/bin/sh
for i in `find . -type f \( -name "*.h" -o -name "*.cpp" \)` 
do
  LINE=`file $i | grep CRLF`
  if [ $? -eq 1 ]
  then
    unix2dos $i
  fi
done

The for loop seems a bit tricky to use since spaces are not being handled correctly. When the filename contains space, the shell is trying to apply unix2dos incorrectly on a splited string.

How do I solve the problem ?

5

There are 5 answers

1
Thibault On BEST ANSWER

Simply change your unix2dos command with the following (provided by putnamhill upper) :

`perl -wpi -e 's/([^\r])\n/$1\r\n/g' $1`;

Then do your previous find command :

#!/bin/sh
find . -type f \( -name "*.h" -o -name "*.cpp" \) -exec unix2dos {}\;

And you are all set.

7
anubhava On
  • You shouldn't process find command's output in a for loop.

  • You need to quote your variables properly in shell.

Try this code instead:

#!/bin/sh
find . -type f \( -name "*.h" -o -name "*.cpp" \) | while read i
do
  LINE=`file "$i" | grep -c CRLF`
  if [ $LINE -eq 0 ]
  then
    unix2dos "$i"
  fi
done

UPDATE: If you decide to use BASH then you can do this looping more efficiently. Consider following code:

#!/bin/bash
while read file
do
  grep -q $'\r'"$" "$file" && unix2dos "$file"
done < <(find . -type f \( -name "*.h" -o -name "*.cpp" \))

< <(...) syntax is called process substitution that makes above while loop in the current shell itself thus allowing you to set shel variables in current shell process and saving a forking of sub-shell creation.

0
Cole Tierney On

You could use the following perl, which should leave CRLF files unchanged:

#!/bin/sh
find . -type f \( -name "*.h" -o -name "*.cpp" \) -exec perl -pi -e 's/([^\r])\n/$1\r\n/' "{}"\;

It will insert a CR before any LF that isn't preceded by a CR.

2
janos On

You could check with a grep if a file contains a \r and run unix2dos conditionally, like this:

find . -type f \( -name "*.h" -o -name "*.cpp" \) -exec sh -c 'grep -q ^M "{}" && dos2unix "{}"' \;

... where you enter ^M by pressing Control-V and Enter. (^M is the \r character)

1
Erwin Waterlander On

Unix2dos will change LF to CRLF, but it will not change CRLF to CRCRLF. Any existing DOS line break will stay unchanged. So the simplest way to do what you want is:

unix2dos *.h *.cpp

best regards, Erwin