Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I need to periodically run a command, that ensures that some text files are kept in linux mode. Unfortunately dos2unix always modifies file, which would mess file's and folder's timestamp and cause unnecessary writes.

The script I write is in Bash, so I'd prefer answers based on Bash.

share|improve this question
    
I think you mean dos2unix. Have you tried the -k switch? –  depquid Jun 17 '13 at 16:47
    
A number of the answers below assume you have a TEXT file. Although this is implied in your question, you have not made this specific claim. –  mdpc Jun 17 '13 at 17:25
    
@mdpc, what would the question mean with regards to a non-"TEXT" file? –  Samuel Edwin Ward Jun 17 '13 at 20:37
add comment

7 Answers 7

If the goal is just to avoid affecting the timestamp, dos2unix has a -k or --keepdate option which will keep the timestamp the same. It will still have to do a write to make the temporary file and rename it, but your timestamps will not be affected.

If any modification of the file is unacceptable, you can use the following solution from this answer.

find . -not -type d -exec file "{}" ";" | grep CRLF
share|improve this answer
add comment

You can use dos2unix as a filter and compare its output to the original file:

dos2unix < test | cmp -s - test
share|improve this answer
add comment

a bash function for you:

# return 0 (true) if first line ends in CR
isDosFile() {
    [[ $(head -1 "$1") == *$'\r' ]]  
}

Then you can do stuff like

streamFile () {
    if isDosFile /tmp/foo.txt; then
        sed 's/\r$//' "$1"
    else
        cat "$1"
    fi
}

streamFile /tmp/foo.txt | process_lines_without_CR
share|improve this answer
3  
You don't have to use isDosFile() in your example: streamFile() { sed 's/\r$//' "$1" ; }. –  paraxor Jun 17 '13 at 18:59
    
I think this is the most elegant solution; it doesn't read whole file, just the first line. –  Adam Ryczkowski Jun 22 '13 at 20:50
add comment

First method:

[[ $(grep -c $'\r\n' myfile.txt) -gt 0 ]] && echo dos

(We test if there is a carriage return in the file.)

Second method:

[[ $(file myfile.txt) =~ CRLF ]] && echo dos

because file reports something like:

myfile.txt: UTF-8 Unicode text, with CRLF line terminators
share|improve this answer
    
You can replace "$(echo -e '\r')" with the much simpler $'\r', although personally I'd use $'\r\n' to reduce the number of false positives. –  rici Jun 17 '13 at 17:03
    
@rici grep $'\r\n' seems to match all files on my system... –  depquid Jun 17 '13 at 17:09
    
@rici: good catch. I edited my answer according to your suggestion. — depquid: Maybe you are on Windows? :-) rici's tip works here. –  BertS Jun 17 '13 at 17:11
    
@depquid (and BertS): Actually, I think the correct invocation is grep -U $'\r$', to prevent grep trying to second-guess line-endings. –  rici Jun 17 '13 at 17:18
    
Also, you can use -q to just set the return code if a match is found, instead of -c which requires an additional check. Personally I like your second solution, although it's highly dependent on the whims of file and might not work in a non-English locale. –  rici Jun 17 '13 at 17:22
show 2 more comments

You could try to grep for CRLF code, octal:

grep -U $'\015' myfile.txt

or hex:

grep -U $'\x0D' myfile.txt
share|improve this answer
    
Of course, the assumption is that this is a text file. –  mdpc Jun 17 '13 at 17:24
add comment

If a file has DOS/Windows-style CR-LF line endings, then if you look at it using a Unix-based tool you'll see CR (`'\r') characters at the end of each line.

This command:

grep -l '^M$' filename

will print filename if the file contains one or more lines with Windows-style line endings, and will print nothing if it doesn't. Except that the "^M" has to be a literal carriage return character, typically entered in the shell by typing Ctl-V Enter. Unfortunately, I can't find a way to encode a literal carriage return (or other control character) in a grep regular expression other than entering it directly.

You can you another tool instead:

awk '/\r$/ { exit(1) }' filename

This will exit with a status of 1 (setting $? to 1) if the file contains any Windows-style line endings, and with a status of 0 if it doesn't, making it useful in a shell if statement (note the lack of [ brackets ]):

if awk '/\r$/ { exit(1) }' filename ; then
    echo filename has Unix-style line endings
else
    echo filename has at least one Windows-style line ending
fi

A file can contain a mixture of Unix-style and Windows-style line endings. I'm assuming here that you want to detect files that have any Windows-style line endings.

share|improve this answer
add comment

If you only want to test whether the file is crlf or lf, just try:

sed -n l <scriptname>

Edited

How does it work? From sed manpage, you can see:

-n : Suppresses default output
 l : list nonprinting characters

So using l will print nonprinting characters like crlf, lf,... and -n will remove line that is regular output of sed, which doesn't contain the nonprinting characters.

share|improve this answer
    
How does this work? –  lgeorget Jun 17 '13 at 17:36
3  
That will find any nonprinting characters, not just CR characters at the end of a line. And it doesn't tell you whether such characters exist or not, it just displays them as \r. –  Keith Thompson Jun 17 '13 at 18:53
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.