Detect CRLF (^M) line terminators in shell script
Shell will complain 'bad interpreter' error for script which contains CRLF (^M) line terminators.
The error message likes 'bash: ./test.sh: /bin/bash^M: bad interpreter: No such file or directory'.
This generally caused by the script file was edited by a windows ( dos ) editor and saved with dos format.
How to check whether a shell script file contains CRLF? Either the below tricks can help you out.
1) Grep the hex code of CR.
The new line terminator for a text on Unix system is only LF. So, the once the file contains CR, it's not a compatible unix shell script.
The hex code of CR is 0x0D. The grep command is :
grep -c $'\x0D' test.sh
-c means print the total number of match character.
To be honestly, I do not understand the $ in this command either. :P I just found this black magic somewhere by Google.
--- update---
Quote from bash manual page:
Words of the form $'string' are treated specially. The word expands to string, with backslash-escaped characters replaced as specified by the ANSI C standard. |
2) Find the hex code of CR by Perl Regular Expression.
The theory is the same to the above one. Just replace with Regular Expression. To be more precisely, Regular Expression match the whole CRLF in the end of each line.
perl -n -e 'print if /\x0d\x0a$/;' test.sh | wc -l
The hex code of LF is 0x0A. wc is to count all the matched lines.
3) Determin file's line teminator by file. This is my favorite one. :D
Command line tool file is used to check the file type of files. It also report the character set and line terminates of regular text file.
file -m /dev/null test.sh
-m /dev/null is a trick to force the file to treat shell script as a regular text file.
Convert file format from dos to unix
Convert file format by sed
$cp you-file-name your-file-name.bak $sed 's/^M//g' your-file-name.bak > your-file-name
Covert file format by Perl
$perl -i.bak -pe 's/^M//g;' your-file-name
Covert file format by vim
:setlocal ff=unix :wq
character cast by Perl -- II
uppercase cast:
perl -p -e '$_=uc'
lowercase cast:
perl -p -e '$_=lc'
character cast by Perl
uppercase cast:
perl -p -e 's/(lowercase pattern)/\u$1/g'
perl -n -e 'print "\U$_"'
perl -p -e '$_="\U$_"'
lowercase cast:
perl -p -e 's/(uppercase pattern)/\l$1/g'
perl -n -e 'print "\L$_"'
perl -p -e '$_="\L$_"'
how to use the non-buffered stdout of Perl
At first, it's really a interesting bug. I can't help writing it down.
I write a perl script which should be running quietly and infinitely on the background. I don't like to do writing the run-time log into a file directly in the script. So, I decide to just redirect the stdout to a file when I execute the script.
Now, here comes the werid bug. I start up the script. It works well. Everything is well except there is no log in the redirected stdout file. But after I disable the infinite loop, let it just run one time and exit. Suprisedly, the run-time logs appear in the redirected stdout file.
Fortunately, I realize that the problem of perl's output functionality at last. With default behavior, Perl saves the output strings in a buffer instead of do phsical write to screen everytime. If I kill the script when it's running, then Perl doesn't have the opportunity to write the buffered strings to the screen. The solution is simply adding the code '$| = 1;' to the begining of the script . It tells Perl using non-buffered I/O to the screen.
Furthermore, in the case of using the non-buffered I/O to other device. We can refer to the example below.
$oldh= select(DEV);
$| =1;
select($oldh);
Nice weekend.