Are shell scripts sensitive to encoding and line endings?

I am making a NW.js app on Mac, and want to run the app in dev mode by double-clicking on an icon. First step, I'm trying to make my shell script work.

Using VSCode on Windows (I wanted to gain time), I have created a run-nw file at the root of my project, containing this:

#!/bin/bash

cd "src"
npm install

cd ..
./tools/nwjs-sdk-v0.17.3-osx-x64/nwjs.app/Contents/MacOS/nwjs "src" &

but I get this output:

$ sh ./run-nw

: command not found  
: No such file or directory  
: command not found  
: No such file or directory  

Usage: npm <command>

where <command> is one of:  (snip commands list)

(snip npm help)

npm@3.10.3 /usr/local/lib/node_modules/npm  
: command not found  
: No such file or directory  
: command not found

I really don't understand:

  • it seems that it takes empty lines as commands. In my editor (VSCode) I have tried to replace rn with n (in case the r creates problems) but it changes nothing.
  • it seems that it doesn't find the folders (with or without the dirname instruction), or maybe it doesn't know about the cd command ?
  • it seems that it doesn't understand the install argument to npm
  • the part that really weirds me out, is that it still runs the app (if I did a npm install manually)...
  • Not able to make it work properly, and suspecting something weird with the file itself, I created a new one directly on the Mac, using vim this time. I entered the exact same instructions, and... now it works without any issue.
    A diff on the two files reveals exactly zero difference.

    What can be the difference? What can make the first script not work? How can I find out?

    Update

    Following the accepted answer's recommandations, after the wrong line endings came back, I checked multiple things. It turns out that since I copied my ~/.gitconfig from my Windows machine, I had autocrlf=true , so every time I modified the bash file under Windows, it re-set the line endings to rn .
    So, in addition to running dos2unix (which you will have to install using Homebrew on mac), if you're using Git, check your config.


    Yes. Bash scripts are sensitive to line-endings. They should have Unix-style line-endings, ie, each line is terminated with a Line Feed character (decimal 10, hex 0A in ASCII).

    DOS/Windows line endings

    With Windows or DOS-style line endings , each line is terminated with a Carriage Return followed by a Line Feed character. If a script file was saved with Windows line endings, Bash sees the file as

    #!/bin/bash^M
    ^M
    cd "src"^M
    npm install^M
    ^M
    cd ..^M
    ./tools/nwjs-sdk-v0.17.3-osx-x64/nwjs.app/Contents/MacOS/nwjs "src" &^M
    

    In this case, the carriage return ( ^M or r ) is not treated as whitespace. Bash interprets the first line after the shebang (consisting of a single carriage return character) as the name of a command/program to run.

  • Since there is no command named ^M , it prints : command not found
  • Since there is no directory named "src"^M (or src^M ), it prints : No such file or directory
  • It passes install^M instead of install as an argument to npm which causes npm to complain.
  • Note: I've used caret notation to represent non-printing characters, ie, ^M is used to to represent the Carriage Return characters (represented as r in other contexts); this is the same technique used by cat -v and Vim.

    Solution

    The solution is to convert the file to Unix line endings.

  • This can be done using the dos2unix program:

    dos2unix run-nw
    
  • Or open the file in Vim and run the following command before saving:

    :set fileformat=unix
    
  • Note: with the Bash port for Cygwin, there's a custom igncr option that can be set to ignore the Carriage Return in line endings (presumably because some users use native Windows programs to edit their text files).

    Useful utilities

    The file utility is useful for quickly seeing which line endings are used in a text file. Here's what it prints for for each file type:

  • Unix line endings: Bourne-Again shell script, ASCII text executable
  • Mac line endings: Bourne-Again shell script, ASCII text executable, with CR line terminators
  • DOS line endings: Bourne-Again shell script, ASCII text executable, with CRLF line terminators
  • The dos2unix utility is very useful for converting text files between Unix, Mac and DOS line endings.

    Useful links

    Wikipedia has an excellent article covering the many different ways of marking the end of a line of text, the history of such encodings and how newlines are treated in different operating systems, programming languages and Internet protocols (eg, FTP).

    Classic Mac OS line endings

    With Classic Mac OS (pre-OS X), each line was terminated with a Carriage Return (decimal 13, hex 0D in ASCII). If a script file was saved with such line endings, Bash would only see one long line like so:

    #!/bin/bash^M^Mcd "src"^Mnpm install^M^Mcd ..^M./tools/nwjs-sdk-v0.17.3-osx-x64/nwjs.app/Contents/MacOS/nwjs "src" &^M
    

    Since this single long line begins with an octothorpe ( # ), Bash treats the line (and the whole file) as a single comment.

    Note: In 2001, Apple launched Mac OS X which was based on the BSD-derived NeXTSTEP operating system. As a result, OS X also uses Unix-style LF-only line endings and since then, text files terminated with a CR have become extremely rare. Nevertheless, I think it's worthwhile to show how Bash would attempt to interpret such files.


    另一种摆脱不需要的CR(' r')字符的方法是运行tr命令,例如:

    $ tr -d 'r' < dosScript.py > nixScript.py
    
    链接地址: http://www.djcxy.com/p/57192.html

    上一篇: Cron不运行bash脚本

    下一篇: shell脚本对编码和行尾是否敏感?